line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package ClearCase::SyncTree; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
$VERSION = '0.60'; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
require 5.004; |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
918
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
8
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
5
|
use Cwd; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
77
|
|
10
|
1
|
|
|
1
|
|
6
|
use File::Basename; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
136
|
|
11
|
1
|
|
|
1
|
|
1081
|
use File::Compare; |
|
1
|
|
|
|
|
1324
|
|
|
1
|
|
|
|
|
60
|
|
12
|
1
|
|
|
1
|
|
1016
|
use File::Copy; |
|
1
|
|
|
|
|
6080
|
|
|
1
|
|
|
|
|
83
|
|
13
|
1
|
|
|
1
|
|
8
|
use File::Find; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
60
|
|
14
|
1
|
|
|
1
|
|
5
|
use File::Path; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
68
|
|
15
|
1
|
|
|
1
|
|
5
|
use File::Spec 0.82; |
|
1
|
|
|
|
|
30
|
|
|
1
|
|
|
|
|
29
|
|
16
|
1
|
|
|
1
|
|
1055
|
use ClearCase::Argv 1.34 qw(chdir); |
|
1
|
|
|
|
|
51628
|
|
|
1
|
|
|
|
|
120
|
|
17
|
|
|
|
|
|
|
|
18
|
1
|
50
|
|
1
|
|
11
|
use constant MSWIN => $^O =~ /MSWin|Windows_NT/i ? 1 : 0; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
110
|
|
19
|
1
|
50
|
|
1
|
|
5
|
use constant CYGWIN => $^O =~ /cygwin/i ? 1 : 0; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
559
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my $lext = '.=lnk='; # special extension for pseudo-symlinks |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub new { |
24
|
0
|
|
|
0
|
0
|
0
|
my $proto = shift; |
25
|
0
|
|
|
|
|
0
|
my $class; |
26
|
0
|
0
|
|
|
|
0
|
if ($class = ref($proto)) { |
27
|
|
|
|
|
|
|
# Make a (deep) clone of the invoking instance |
28
|
0
|
|
|
|
|
0
|
require Clone; |
29
|
0
|
|
|
|
|
0
|
Clone->VERSION(0.12); # 0.10 has a known bug |
30
|
0
|
|
|
|
|
0
|
return Clone::clone($proto); |
31
|
|
|
|
|
|
|
} |
32
|
0
|
|
|
|
|
0
|
$class = $proto; |
33
|
0
|
|
|
|
|
0
|
my $self = {@_}; |
34
|
0
|
|
|
|
|
0
|
bless $self, $class; |
35
|
0
|
|
|
|
|
0
|
$self->comment('By:' . __PACKAGE__); |
36
|
|
|
|
|
|
|
# Default is to sync file modes unless on ^$%#* Windows. |
37
|
0
|
|
|
|
|
0
|
$self->protect(1); |
38
|
|
|
|
|
|
|
# Set up a ClearCase::Argv instance with the appropriate attrs. |
39
|
0
|
|
|
|
|
0
|
$self->ct; |
40
|
|
|
|
|
|
|
# By default we'll call SyncTree->fail on any cleartool error. |
41
|
0
|
|
|
|
|
0
|
$self->err_handler($self, 'fail'); |
42
|
|
|
|
|
|
|
# Set default file comparator. |
43
|
0
|
|
|
|
|
0
|
$self->cmp_func(\&File::Compare::compare); |
44
|
0
|
|
|
|
|
0
|
return $self; |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
sub err_handler { |
48
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
49
|
0
|
|
|
|
|
0
|
my $ct = $self->ct; |
50
|
0
|
0
|
|
|
|
0
|
if (@_ >= 2) { |
51
|
0
|
|
|
|
|
0
|
my($obj, $method) = @_; |
52
|
0
|
0
|
|
|
|
0
|
$method = join('::', ref($obj), $method) unless $method =~ /::/; |
53
|
0
|
|
|
|
|
0
|
$ct->autofail([\&$method, $obj]); |
54
|
|
|
|
|
|
|
} else { |
55
|
0
|
|
|
|
|
0
|
$ct->autofail(@_); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# For internal use only. Provides a std msg format. |
60
|
|
|
|
|
|
|
sub _msg { |
61
|
0
|
|
|
0
|
|
0
|
my $prog = basename($0); |
62
|
0
|
|
|
|
|
0
|
my $type = shift; |
63
|
0
|
|
|
|
|
0
|
my $msg = "@_"; |
64
|
0
|
|
|
|
|
0
|
chomp $msg; |
65
|
0
|
|
|
|
|
0
|
return "$prog: $type: $msg\n"; |
66
|
|
|
|
|
|
|
} |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# For internal use only. A synonym for die() with a std error msg format. |
69
|
|
|
|
|
|
|
sub fatal { |
70
|
0
|
|
|
0
|
0
|
0
|
die _msg('Error', @_); |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# For internal use only. A synonym for warn() with a std error msg format. |
74
|
|
|
|
|
|
|
sub warning { |
75
|
0
|
|
|
0
|
0
|
0
|
warn _msg('Warning', @_); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# For internal use only. Returns the ClearCase::Argv object. |
79
|
|
|
|
|
|
|
sub ct { |
80
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
81
|
0
|
0
|
|
|
|
0
|
return $self->{ST_CT} if $self->{ST_CT}; |
82
|
0
|
0
|
|
|
|
0
|
if (!defined(wantarray)) { |
83
|
0
|
|
|
|
|
0
|
my $ct = ClearCase::Argv->new({autochomp=>1, outpathnorm=>1}); |
84
|
0
|
|
|
|
|
0
|
$ct->syxargs($ct->qxargs); |
85
|
0
|
|
|
|
|
0
|
$self->{ST_CT} = $ct; |
86
|
|
|
|
|
|
|
} |
87
|
0
|
|
|
|
|
0
|
return $self->{ST_CT}; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# For internal use only. Returns a clone of the ClearCase::Argv object. |
91
|
|
|
|
|
|
|
sub clone_ct { |
92
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
93
|
0
|
|
|
|
|
0
|
my $ct = $self->ct->clone(@_); |
94
|
0
|
0
|
0
|
|
|
0
|
my $af = $self->ct->autofail |
|
|
|
0
|
|
|
|
|
95
|
|
|
|
|
|
|
unless $_[0] and (ref($_[0]) eq 'HASH') and exists $_[0]->{autofail}; |
96
|
0
|
0
|
0
|
|
|
0
|
$ct->autofail($af) if $af && ref($af); #Cloning doesn't share the value |
97
|
0
|
|
|
|
|
0
|
return $ct; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub gen_accessors { |
101
|
1
|
|
|
1
|
0
|
5
|
my @key = map {uc} @_; |
|
14
|
|
|
|
|
27
|
|
102
|
1
|
|
|
1
|
|
6
|
no strict 'refs'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
151
|
|
103
|
1
|
|
|
|
|
5
|
for (@key) { |
104
|
14
|
|
|
|
|
26
|
my $var = "ST_$_"; |
105
|
14
|
|
|
|
|
27
|
my $meth = lc; |
106
|
|
|
|
|
|
|
*$meth = sub { |
107
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
108
|
0
|
0
|
|
|
|
0
|
$self->{$var} = shift if @_; |
109
|
0
|
|
|
|
|
0
|
return $self->{$var}; |
110
|
|
|
|
|
|
|
} |
111
|
14
|
|
|
|
|
93
|
} |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
gen_accessors(qw(protect remove reuse vreuse lblver ignore_co overwrite_co |
114
|
|
|
|
|
|
|
snapdest ctime lbtype inclb cmp_func rellinks dstview)); |
115
|
|
|
|
|
|
|
sub gen_flags { |
116
|
1
|
|
|
1
|
0
|
3
|
my @key = map {uc} @_; |
|
3
|
|
|
|
|
10
|
|
117
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
978
|
|
118
|
1
|
|
|
|
|
4
|
for (@key) { |
119
|
3
|
|
|
|
|
10
|
my $var = "ST_$_"; |
120
|
3
|
|
|
|
|
6
|
my $meth = lc; |
121
|
|
|
|
|
|
|
*$meth = sub { |
122
|
0
|
|
|
0
|
|
|
my $self = shift; |
123
|
0
|
0
|
0
|
|
|
|
$self->{$var} = 1 if $_[0] || !defined(wantarray); |
124
|
0
|
|
|
|
|
|
return $self->{$var}; |
125
|
|
|
|
|
|
|
} |
126
|
3
|
|
|
|
|
24
|
} |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
gen_flags(qw(label_mods no_cr no_cmp)); |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
sub comment { |
131
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
132
|
0
|
|
|
|
|
|
my $cmnt = shift; |
133
|
0
|
0
|
|
|
|
|
if (ref $cmnt) { |
|
|
0
|
|
|
|
|
|
134
|
0
|
|
|
|
|
|
$self->{ST_COMMENT} = $cmnt; |
135
|
|
|
|
|
|
|
} elsif ($cmnt) { |
136
|
0
|
|
|
|
|
|
$self->{ST_COMMENT} = ['-c', $cmnt]; |
137
|
|
|
|
|
|
|
} |
138
|
0
|
|
|
|
|
|
return $self->{ST_COMMENT}; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub normalize { |
142
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
143
|
0
|
|
|
|
|
|
chomp(my $path = shift); |
144
|
0
|
|
|
|
|
|
my $dv = $self->dstview; |
145
|
0
|
|
|
|
|
|
my $md = $self->mvfsdrive if MSWIN; |
146
|
0
|
|
|
|
|
|
for ($path) { |
147
|
0
|
|
|
|
|
|
if (MSWIN) { |
148
|
|
|
|
|
|
|
s%^$md:%%; |
149
|
|
|
|
|
|
|
s%^[\\/]\Q$dv%%; |
150
|
|
|
|
|
|
|
s%\\%/%g; |
151
|
|
|
|
|
|
|
$_ = "$md:/$dv$_"; |
152
|
|
|
|
|
|
|
} elsif (CYGWIN) { |
153
|
|
|
|
|
|
|
# 4 cases: unc; /view/x user mount; view drive; mvfs drive/tag |
154
|
|
|
|
|
|
|
s%^/(/?view/$dv|cygdrive/\w(/$dv)?)%%; |
155
|
|
|
|
|
|
|
$_ = "//view/$dv$_"; |
156
|
|
|
|
|
|
|
} else { |
157
|
0
|
|
|
|
|
|
s%^/view/$dv%%; |
158
|
0
|
|
|
|
|
|
$_ = "/view/$dv$_"; |
159
|
|
|
|
|
|
|
} |
160
|
0
|
|
|
|
|
|
s%/\.?$%%; |
161
|
|
|
|
|
|
|
} |
162
|
0
|
|
|
|
|
|
return $path; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
sub canonicalize { |
166
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
167
|
0
|
|
|
|
|
|
my $base = shift; |
168
|
0
|
|
|
|
|
|
for (@_) { |
169
|
0
|
0
|
0
|
|
|
|
$_ = File::Spec->canonpath(join('/', $base, $_)) |
170
|
|
|
|
|
|
|
if $_ && ! File::Spec->file_name_is_absolute($_); |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Returns -other and -do private files. Checkouts are handled separately. |
175
|
|
|
|
|
|
|
sub _lsprivate { |
176
|
0
|
|
|
0
|
|
|
my $self = shift; |
177
|
0
|
|
|
|
|
|
my $implicit_dirs = shift; |
178
|
0
|
|
|
|
|
|
my $base = $self->dstbase; |
179
|
0
|
|
|
|
|
|
my $dv = $self->dstview; |
180
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autofail=>0, stderr=>0}); |
181
|
0
|
|
|
|
|
|
my @vp; |
182
|
0
|
|
|
|
|
|
for ($ct->argv('lsp', [qw(-oth -do -s -inv), "$base/.", '-tag', $dv])->qx) { |
183
|
0
|
|
|
|
|
|
$_ = $self->normalize($_); |
184
|
0
|
0
|
|
|
|
|
push(@vp, $_) if m%^\Q$base/%; |
185
|
|
|
|
|
|
|
} |
186
|
0
|
|
|
|
|
|
push(@vp, grep {$ct->des([qw(-s)], "$_/.\@\@")->stdout(0)->system} |
|
0
|
|
|
|
|
|
|
187
|
0
|
0
|
0
|
|
|
|
@{$self->{ST_IMPLICIT_DIRS}}) |
188
|
|
|
|
|
|
|
if $self->{ST_IMPLICIT_DIRS} && $implicit_dirs; |
189
|
0
|
|
|
|
|
|
return @vp; |
190
|
|
|
|
|
|
|
} |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
sub _lsco { |
193
|
0
|
|
|
0
|
|
|
my $self = shift; |
194
|
0
|
|
|
|
|
|
my $base = $self->_mkbase; |
195
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct; |
196
|
0
|
|
|
|
|
|
my $sil = $self->clone_ct(stderr=>0, autofail=>0); |
197
|
0
|
|
|
|
|
|
my %co; |
198
|
0
|
|
|
|
|
|
for ($ct->lsco([qw(-s -cvi -a)], $base)->qx) { |
199
|
0
|
|
|
|
|
|
$_ = $self->normalize($_); |
200
|
0
|
0
|
0
|
|
|
|
$co{$_}++ if m%^\Q$base/% || $_ eq $base; |
201
|
|
|
|
|
|
|
} |
202
|
0
|
|
|
|
|
|
for my $dir (@{$self->{ST_IMPLICIT_DIRS}}) { |
|
0
|
|
|
|
|
|
|
203
|
0
|
|
|
|
|
|
my $dad = dirname($dir); |
204
|
0
|
0
|
|
|
|
|
$co{$dad}++ if $sil->lsco([qw(-s -cvi -d)], $dad)->qx; |
205
|
|
|
|
|
|
|
} |
206
|
0
|
0
|
|
|
|
|
return wantarray? sort keys %co : scalar keys %co; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
sub mvfsdrive { |
210
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
211
|
0
|
|
|
|
|
|
if (MSWIN && ! $self->{ST_MVFSDRIVE}) { |
212
|
1
|
|
|
1
|
|
17
|
no strict 'subs'; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
35
|
|
213
|
1
|
|
|
1
|
|
6
|
use vars '$Registry'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1151
|
|
214
|
|
|
|
|
|
|
require Win32::TieRegistry; |
215
|
|
|
|
|
|
|
# HKLM is read-only for non-admins so open read-only |
216
|
|
|
|
|
|
|
Win32::TieRegistry->import('TiedRef', '$Registry', qw(KEY_READ)); |
217
|
|
|
|
|
|
|
my $LMachine = $Registry->Open('LMachine', {Access => KEY_READ}); |
218
|
|
|
|
|
|
|
$self->{ST_MVFSDRIVE} = $LMachine->{SYSTEM}-> |
219
|
|
|
|
|
|
|
{CurrentControlSet}->{Services}->{Mvfs}->{Parameters}->{drive}; |
220
|
|
|
|
|
|
|
die "$0: Error: unable to find MVFS drive" unless $self->{ST_MVFSDRIVE}; |
221
|
|
|
|
|
|
|
} |
222
|
0
|
|
|
|
|
|
return $self->{ST_MVFSDRIVE}; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub ccsymlink { |
226
|
0
|
|
|
0
|
0
|
|
my $dst = shift; |
227
|
0
|
0
|
|
|
|
|
return 1 if -l $dst; |
228
|
0
|
|
|
|
|
|
return 0 unless MSWIN || CYGWIN; |
229
|
0
|
|
|
|
|
|
my $ct = new ClearCase::Argv({autochomp=>1, stderr=>0}); |
230
|
0
|
|
|
|
|
|
return $ct->des([qw(-fmt %m)], $dst)->qx eq 'symbolic link'; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
# readlink might work under some conditions (CC version, mount options, ...) |
234
|
|
|
|
|
|
|
sub readcclink { |
235
|
0
|
|
|
0
|
0
|
|
my $dst = shift; |
236
|
0
|
|
|
|
|
|
my $ret = readlink $dst; |
237
|
0
|
0
|
0
|
|
|
|
return $ret if $ret || !(MSWIN || CYGWIN); |
238
|
0
|
|
|
|
|
|
my $ct = new ClearCase::Argv({autochomp=>1}); |
239
|
0
|
|
|
|
|
|
$ret = $ct->ls($dst)->qx; |
240
|
0
|
|
|
|
|
|
$ret =~ s%\\%/%g if MSWIN; |
241
|
0
|
0
|
|
|
|
|
return (($ret =~ s/^.*? --> (.*)$/$1/)? $ret : ''); |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
sub srcbase { |
245
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
246
|
0
|
0
|
|
|
|
|
if (@_) { |
247
|
0
|
|
|
|
|
|
my $sbase = File::Spec->rel2abs(shift); |
248
|
0
|
|
|
|
|
|
$sbase =~ s%\\%/%g; # rel2abs forces native (\) separator |
249
|
0
|
|
|
|
|
|
$sbase =~ s%/\.$%%; # workaround for bug in File::Spec 0.82 |
250
|
|
|
|
|
|
|
# File::Spec::Win32::rel2abs leaves trailing / on drive letter root. |
251
|
0
|
0
|
|
|
|
|
$sbase =~ s%/*$%% if $sbase ne '/'; |
252
|
0
|
|
|
|
|
|
$self->{ST_SRCBASE} = $sbase; |
253
|
0
|
|
|
0
|
|
|
*src_slink = sub { return -l shift }; |
|
0
|
|
|
|
|
|
|
254
|
0
|
|
|
0
|
|
|
*src_rlink = sub { return readlink shift }; |
|
0
|
|
|
|
|
|
|
255
|
0
|
|
|
|
|
|
if (MSWIN || CYGWIN) { |
256
|
|
|
|
|
|
|
my $ct = $self->clone_ct({autofail=>1, autochomp=>1}); |
257
|
|
|
|
|
|
|
my $olddir = getcwd; |
258
|
|
|
|
|
|
|
$ct->_chdir($sbase) || die "$0: Error: $sbase: $!"; |
259
|
|
|
|
|
|
|
if ($ct->pwv(['-s'])->qx !~ /\s+NONE\s+/) { |
260
|
|
|
|
|
|
|
*src_slink = \&ccsymlink; |
261
|
|
|
|
|
|
|
*src_rlink = \&readcclink; |
262
|
|
|
|
|
|
|
} |
263
|
|
|
|
|
|
|
$ct->_chdir($olddir); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
} |
266
|
0
|
|
|
|
|
|
return $self->{ST_SRCBASE}; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub dstbase { |
270
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
271
|
0
|
0
|
|
|
|
|
if (@_) { |
272
|
0
|
|
|
|
|
|
my $dbase = shift; |
273
|
0
|
0
|
0
|
|
|
|
-e $dbase || mkpath($dbase, 0, 0777) || die "$0: Error: $dbase: $!"; |
274
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autofail=>1, autochomp=>1}); |
275
|
0
|
|
|
|
|
|
my $olddir = getcwd; |
276
|
0
|
0
|
|
|
|
|
$ct->_chdir($dbase) || die "$0: Error: $dbase: $!"; |
277
|
0
|
|
|
|
|
|
$dbase = getcwd; |
278
|
0
|
|
|
|
|
|
my $dv = $ct->pwv(['-s'])->qx; |
279
|
0
|
0
|
0
|
|
|
|
die "$0: Error: destination base ($dbase) not in a view/VOB context" |
280
|
|
|
|
|
|
|
if !$dv || $dv =~ m%\sNONE\s%; |
281
|
0
|
|
|
|
|
|
$self->dstview($dv); |
282
|
|
|
|
|
|
|
# We need to derive the current vob of the dest path, which we |
283
|
|
|
|
|
|
|
# do by cd-ing there temporarily and running "ct desc -s vob:.". |
284
|
|
|
|
|
|
|
# But with a twist because of @%$* Windows. |
285
|
0
|
|
|
|
|
|
my $dvob; |
286
|
0
|
0
|
|
|
|
|
if (!($dvob = $self->dstvob)) { |
287
|
|
|
|
|
|
|
# We need this weird hack to get a case-correct version of the |
288
|
|
|
|
|
|
|
# dest path, in case the user typed it in random case. There |
289
|
|
|
|
|
|
|
# appears to be a bug in CC 4.2; "ct desc vob:foo" fails if |
290
|
|
|
|
|
|
|
# "foo" is not the right case even if MVFS is set to be |
291
|
|
|
|
|
|
|
# case insensitive. This is caseid v0869595, bugid CMBU00055321. |
292
|
|
|
|
|
|
|
# Since Windows mount points must be at the root level, |
293
|
|
|
|
|
|
|
# we assume the vob tag must be the root dir name. We must |
294
|
|
|
|
|
|
|
# still then look that up in lsvob to get the tag case right. |
295
|
0
|
|
|
|
|
|
if (MSWIN) { |
296
|
|
|
|
|
|
|
my @vobs = $ct->lsvob(['-s'])->qx; |
297
|
|
|
|
|
|
|
my $dirpart = (File::Spec->splitpath($dbase, 1))[1]; |
298
|
|
|
|
|
|
|
for my $name (File::Spec->splitdir($dirpart)) { |
299
|
|
|
|
|
|
|
last if $dvob; |
300
|
|
|
|
|
|
|
next unless $name; |
301
|
|
|
|
|
|
|
for my $vob (@vobs) { |
302
|
|
|
|
|
|
|
if ($vob =~ m%^[/\\]$name$%i) { |
303
|
|
|
|
|
|
|
($dvob = $vob) =~ s%\\%/%g; |
304
|
|
|
|
|
|
|
last; |
305
|
|
|
|
|
|
|
} |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
} else { |
309
|
0
|
|
|
|
|
|
$dvob = $ct->desc(['-s'], "vob:.")->qx; |
310
|
|
|
|
|
|
|
} |
311
|
0
|
|
|
|
|
|
$self->dstvob($dvob); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
# On Windows, normalize the specified dstbase to use the |
314
|
|
|
|
|
|
|
# MVFS drive (typically M:), e.g. M:\view-name\vob-tag\path... |
315
|
|
|
|
|
|
|
# This avoids all kinds of problems with using the view |
316
|
|
|
|
|
|
|
# via a different drive letter or a UNC (\\view) path. |
317
|
|
|
|
|
|
|
# Similarly, on UNIX we normalize to a view-extended path |
318
|
|
|
|
|
|
|
# even if we're already in a set view because it's the |
319
|
|
|
|
|
|
|
# lowest common denominator. Also, if the set view differs |
320
|
|
|
|
|
|
|
# from the 'dest view', the dest view should win. |
321
|
0
|
|
|
|
|
|
if (MSWIN) { |
322
|
|
|
|
|
|
|
$dbase =~ s%\\%/%g; |
323
|
1
|
|
|
1
|
|
5
|
use vars '%RegHash'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
10995
|
|
324
|
|
|
|
|
|
|
require Win32::TieRegistry; |
325
|
|
|
|
|
|
|
Win32::TieRegistry->import('TiedHash', '%RegHash'); |
326
|
|
|
|
|
|
|
my $mdrive = $self->mvfsdrive; |
327
|
|
|
|
|
|
|
$dbase = getcwd; |
328
|
|
|
|
|
|
|
$dbase =~ s%.*?$dvob%$mdrive:/$dv$dvob%i; |
329
|
|
|
|
|
|
|
} else { |
330
|
0
|
|
|
|
|
|
$dbase = getcwd; |
331
|
0
|
|
|
|
|
|
if (CYGWIN) { |
332
|
|
|
|
|
|
|
$dbase =~ s%^/(/?view/$dv|cygdrive/\w)%%; |
333
|
|
|
|
|
|
|
$dbase = "//view/$dv$dbase"; |
334
|
|
|
|
|
|
|
} else { |
335
|
0
|
|
|
|
|
|
$dbase =~ s%^/view/$dv%%; |
336
|
0
|
|
|
|
|
|
$dbase = "/view/$dv$dbase"; |
337
|
|
|
|
|
|
|
} |
338
|
|
|
|
|
|
|
} |
339
|
0
|
0
|
|
|
|
|
$ct->_chdir($olddir) || die "$0: Error: $olddir: $!"; |
340
|
0
|
|
|
|
|
|
$self->{ST_DSTBASE} = $dbase; |
341
|
0
|
|
|
|
|
|
(my $dvb = $dbase) =~ s%^(.*?$dvob).*$%$1%; |
342
|
0
|
0
|
|
|
|
|
$self->snapdest(1) unless -e "$dvb/@@"; |
343
|
|
|
|
|
|
|
} |
344
|
0
|
|
|
|
|
|
return $self->{ST_DSTBASE}; |
345
|
|
|
|
|
|
|
} |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# We may have created a view-private parent tree, so must |
348
|
|
|
|
|
|
|
# work our way upwards till we get to a versioned dir. |
349
|
|
|
|
|
|
|
sub _mkbase { |
350
|
0
|
|
|
0
|
|
|
my $self = shift; |
351
|
0
|
0
|
|
|
|
|
if (! $self->{ST_MKBASE}) { |
352
|
0
|
|
|
|
|
|
my $mbase = $self->dstbase; |
353
|
0
|
|
|
|
|
|
my $dvob = $self->dstvob; |
354
|
0
|
|
|
|
|
|
(my $dext = $mbase) =~ s%(.*?$dvob)/.*%$1%; |
355
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({stdout=>0, stderr=>0, autofail=>0}); |
356
|
0
|
|
|
|
|
|
while (1) { |
357
|
0
|
0
|
|
|
|
|
last if length($mbase) <= length($dext); |
358
|
0
|
0
|
0
|
|
|
|
last if -d $mbase && ! $ct->desc(['-s'], "$mbase/.@@")->system; |
359
|
0
|
|
|
|
|
|
push(@{$self->{ST_IMPLICIT_DIRS}}, $mbase); |
|
0
|
|
|
|
|
|
|
360
|
0
|
|
|
|
|
|
$mbase = dirname($mbase); |
361
|
|
|
|
|
|
|
} |
362
|
0
|
|
|
|
|
|
$self->{ST_MKBASE} = $mbase; |
363
|
|
|
|
|
|
|
} |
364
|
0
|
|
|
|
|
|
return $self->{ST_MKBASE}; |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
sub dstvob { |
368
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
369
|
0
|
0
|
|
|
|
|
if (@_) { |
370
|
0
|
|
|
|
|
|
$self->{ST_DSTVOB} = shift; |
371
|
0
|
|
|
|
|
|
$self->{ST_DSTVOB} =~ s%\\%/%g; |
372
|
|
|
|
|
|
|
} |
373
|
0
|
|
|
|
|
|
return $self->{ST_DSTVOB}; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
sub srclist { |
377
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
378
|
0
|
0
|
|
|
|
|
my $type = ref($_[0]) ? ${shift @_} : 'NORMAL'; |
|
0
|
|
|
|
|
|
|
379
|
0
|
|
|
|
|
|
my $sbase = $self->srcbase; |
380
|
0
|
0
|
|
|
|
|
die "$0: Error: must specify src base before src list" if !$sbase; |
381
|
0
|
|
|
|
|
|
for (@_) { |
382
|
0
|
0
|
|
|
|
|
next if $_ eq $sbase; |
383
|
0
|
0
|
|
|
|
|
if (m%^(?:[a-zA-Z]:)?$sbase[/\\]*(.+)%) { |
|
|
0
|
|
|
|
|
|
384
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$1}->{type} = $type; |
385
|
|
|
|
|
|
|
} elsif (-e "$sbase/$_") { |
386
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{type} = $type; |
387
|
|
|
|
|
|
|
} else { |
388
|
0
|
|
|
|
|
|
warn "Warning: $_: no such file or directory\n"; |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub srcmap { |
394
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
395
|
0
|
0
|
|
|
|
|
my $type = ref($_[0]) ? ${shift @_} : 'NORMAL'; |
|
0
|
|
|
|
|
|
|
396
|
0
|
|
|
|
|
|
my %sdmap = @_; |
397
|
0
|
|
|
|
|
|
my $sbase = $self->srcbase; |
398
|
0
|
|
|
|
|
|
my $dbase = $self->dstbase; |
399
|
0
|
0
|
|
|
|
|
die "$0: Error: must specify src base before src map" if !$sbase; |
400
|
0
|
0
|
|
|
|
|
die "$0: Error: must specify dst base before src map" if !$dbase; |
401
|
0
|
|
|
|
|
|
for (keys %sdmap) { |
402
|
0
|
0
|
|
|
|
|
if (m%^(?:[a-zA-Z]:)?\Q$sbase\E[/\\]*(.*)$%) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
403
|
0
|
|
|
|
|
|
my $key = $1; |
404
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$key}->{type} = $type; |
405
|
0
|
|
|
|
|
|
my($dst) = ($sdmap{$_} =~ m%^\Q$dbase\E[/\\]*(.+)$%); |
406
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$key}->{dst} = $dst; |
407
|
|
|
|
|
|
|
} elsif (-e $_) { |
408
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{type} = $type; |
409
|
0
|
0
|
|
|
|
|
if ($sdmap{$_} =~ m%^\Q$dbase\E[/\\]*(.+)$%) { |
410
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{dst} = $1; |
411
|
|
|
|
|
|
|
} else { |
412
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{dst} = $sdmap{$_}; |
413
|
|
|
|
|
|
|
} |
414
|
|
|
|
|
|
|
} elsif (-e "$sbase/$_") { |
415
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{type} = $type; |
416
|
0
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{dst} = $sdmap{$_}; |
417
|
|
|
|
|
|
|
} else { |
418
|
0
|
|
|
|
|
|
warn "Warning: $_: no such file or directory\n"; |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub eltypemap { |
424
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
425
|
0
|
0
|
|
|
|
|
%{$self->{ST_ELTYPEMAP}} = @_ if @_; |
|
0
|
|
|
|
|
|
|
426
|
0
|
0
|
|
|
|
|
return $self->{ST_ELTYPEMAP} ? %{$self->{ST_ELTYPEMAP}} : (); |
|
0
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
sub dstcheck { |
430
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
431
|
0
|
|
|
|
|
|
my $dbase = $self->dstbase; |
432
|
0
|
0
|
|
|
|
|
die "$0: Error: must specify dest base before dstcheck" if !$dbase; |
433
|
0
|
|
|
|
|
|
my @existing = (); |
434
|
0
|
0
|
|
|
|
|
if (-e $dbase) { |
435
|
|
|
|
|
|
|
# Check for view private files under the dest base. |
436
|
0
|
|
|
|
|
|
my @vp = $self->_lsprivate(0); |
437
|
0
|
|
|
|
|
|
my $n = @vp; |
438
|
0
|
0
|
|
|
|
|
my $s = $n == 1 ? '' : 's'; |
439
|
0
|
0
|
|
|
|
|
my $es = $n == 1 ? 's' : ''; |
440
|
0
|
0
|
|
|
|
|
die "$0: Error: $n view-private file$s exist$es under $dbase:\n @vp\n" |
441
|
|
|
|
|
|
|
if @vp; |
442
|
|
|
|
|
|
|
# Check for checkouts under the dest base. |
443
|
0
|
|
|
|
|
|
@existing = $self->_lsco; |
444
|
0
|
|
|
|
|
|
$n = @existing; |
445
|
0
|
0
|
|
|
|
|
$s = $n >= 2 ? 's' : ''; |
446
|
0
|
0
|
|
|
|
|
if ($n == 0) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# do nothing |
448
|
|
|
|
|
|
|
} elsif ($self->ignore_co) { |
449
|
0
|
|
|
|
|
|
warning "skipping $n checkout$s under $dbase"; |
450
|
|
|
|
|
|
|
} elsif ($self->overwrite_co) { |
451
|
0
|
|
|
|
|
|
warning "overwriting $n checkout$s under $dbase"; |
452
|
|
|
|
|
|
|
} else { |
453
|
0
|
|
|
|
|
|
fatal("$n checkout$s found under $dbase"); |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
} |
456
|
0
|
|
|
|
|
|
$self->{ST_PRE} = { map {$_ => 1} @existing }; |
|
0
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
} |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
# Comparator function used to implement the -vreuse option |
460
|
|
|
|
|
|
|
# If the default comparaison fails, look at versions of suitable size |
461
|
|
|
|
|
|
|
# in the version tree, and apply the comparaison to them. |
462
|
|
|
|
|
|
|
# If a suitable version is found, add it to a list of versions on which |
463
|
|
|
|
|
|
|
# to apply a label. |
464
|
|
|
|
|
|
|
sub vtcomp { |
465
|
0
|
|
|
0
|
0
|
|
my($self, $src, $dst) = @_; |
466
|
0
|
|
|
|
|
|
my $cmp = $self->cmp_func; |
467
|
0
|
|
|
|
|
|
my $lb = $self->lblver; |
468
|
0
|
0
|
|
|
|
|
if ($lb) { |
469
|
0
|
|
|
|
|
|
my $lblver = "$dst\@\@/$lb"; |
470
|
0
|
0
|
|
|
|
|
$dst = $lblver if -r $lblver; |
471
|
|
|
|
|
|
|
} |
472
|
0
|
0
|
|
|
|
|
return 0 unless $cmp->($src, $dst); |
473
|
0
|
|
|
|
|
|
my $vt = ClearCase::Argv->lsvtree([qw(-a -s -nco)]); |
474
|
0
|
|
|
|
|
|
my @vt = reverse grep {m%[\\/]\d*$%} $vt->args($dst)->qx; |
|
0
|
|
|
|
|
|
|
475
|
0
|
|
|
|
|
|
chomp @vt; |
476
|
0
|
|
|
|
|
|
my $sz = -s $src; |
477
|
0
|
|
|
|
|
|
for (@vt) { |
478
|
0
|
0
|
|
|
|
|
next if -s $_ != $sz; |
479
|
0
|
0
|
|
|
|
|
if (!$cmp->($src, $_)) { |
480
|
0
|
|
|
|
|
|
push @{$self->{ST_LBL}}, $_; |
|
0
|
|
|
|
|
|
|
481
|
0
|
|
|
|
|
|
return 0; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
} |
484
|
0
|
|
|
|
|
|
return 1; |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
sub _needs_update { |
488
|
0
|
|
|
0
|
|
|
my($self, $src, $dst, $comparator) = @_; |
489
|
0
|
|
|
|
|
|
my $update = 0; |
490
|
0
|
0
|
0
|
|
|
|
if (src_slink($src) && ccsymlink($dst)) { |
|
|
0
|
0
|
|
|
|
|
491
|
0
|
|
|
|
|
|
my $srctext = src_rlink($src); |
492
|
0
|
|
|
|
|
|
my $desttext = readcclink $dst; |
493
|
0
|
|
0
|
|
|
|
$update = !defined($comparator) || ($srctext ne $desttext); |
494
|
|
|
|
|
|
|
} elsif (! src_slink($src) && ! ccsymlink($dst)) { |
495
|
0
|
0
|
|
|
|
|
if (!defined($comparator)) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
496
|
0
|
|
|
|
|
|
$update = 1; |
497
|
|
|
|
|
|
|
} elsif ($self->vreuse) { |
498
|
0
|
|
|
|
|
|
$update = $self->vtcomp($src, $dst); |
499
|
|
|
|
|
|
|
} elsif (-s $src != -s $dst) { |
500
|
0
|
|
|
|
|
|
$update = 1; |
501
|
|
|
|
|
|
|
} else { |
502
|
0
|
|
|
|
|
|
$update = &$comparator($src, $dst); |
503
|
|
|
|
|
|
|
} |
504
|
0
|
0
|
|
|
|
|
$self->failm("failed comparing $src vs $dst: $!") if $update < 0; |
505
|
|
|
|
|
|
|
} else { |
506
|
0
|
|
|
|
|
|
$update = 1; |
507
|
|
|
|
|
|
|
} |
508
|
0
|
0
|
0
|
|
|
|
if ($update && (!exists($self->{ST_PRE}->{$dst}) || $self->overwrite_co)) { |
|
|
|
0
|
|
|
|
|
509
|
0
|
|
|
|
|
|
return 1; |
510
|
|
|
|
|
|
|
} else { |
511
|
0
|
|
|
|
|
|
return 0; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
sub checkcs { |
516
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
517
|
0
|
|
|
|
|
|
my($dest) = @_; |
518
|
0
|
|
|
|
|
|
my $ct = ClearCase::Argv->new({autofail=>1, autochomp=>1}); |
519
|
0
|
|
|
|
|
|
my $pwd = getcwd; |
520
|
0
|
0
|
|
|
|
|
$ct->_chdir($dest) || die "$0: Error: $dest: $!"; |
521
|
0
|
|
|
|
|
|
$dest = getcwd; |
522
|
0
|
|
|
|
|
|
my @cs = grep /^\#\#:BranchOff: *root/, $ct->argv('catcs')->qx; |
523
|
0
|
0
|
|
|
|
|
$ct->_chdir($pwd) || die "$0: Error: $pwd: $!"; |
524
|
0
|
|
|
|
|
|
return scalar @cs; |
525
|
|
|
|
|
|
|
} |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub analyze { |
528
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
529
|
0
|
0
|
|
|
|
|
my $type = ref($_[0]) ? ${shift @_} : 'NORMAL'; |
|
0
|
|
|
|
|
|
|
530
|
0
|
|
|
|
|
|
my $sbase = $self->srcbase; |
531
|
0
|
|
|
|
|
|
my $dbase = $self->dstbase; |
532
|
0
|
0
|
|
|
|
|
die "$0: Error: must specify dest base before analyzing" if !$dbase; |
533
|
0
|
0
|
|
|
|
|
die "$0: Error: must specify dest vob before analyzing" if !$self->dstvob; |
534
|
0
|
|
|
|
|
|
$self->_mkbase; |
535
|
0
|
|
|
|
|
|
$self->{branchoffroot} = $self->checkcs($dbase); |
536
|
|
|
|
|
|
|
# Derive the add and modify lists by traversing the src map and |
537
|
|
|
|
|
|
|
# comparing src/dst files. |
538
|
0
|
|
|
|
|
|
delete $self->{ST_ADD}; |
539
|
0
|
|
|
|
|
|
delete $self->{ST_MOD}; |
540
|
0
|
0
|
|
|
|
|
my @sl = $dbase eq $self->{ST_MKBASE}? sort grep{-d $_} |
|
0
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
$self->clone_ct->find($dbase, qw(-type l -print))->qx : (); |
542
|
0
|
|
|
|
|
|
map { $_ = "/$_" } @sl if CYGWIN; # mismatch between conventions |
543
|
0
|
0
|
|
|
|
|
if (@sl) { |
544
|
0
|
|
|
|
|
|
my %sl = map{ $_ => 1} @sl; |
|
0
|
|
|
|
|
|
|
545
|
0
|
|
|
|
|
|
for my $l (@sl) { |
546
|
0
|
|
|
|
|
|
my $s = $l; |
547
|
0
|
|
|
|
|
|
$s =~ s%^\Q$dbase\E/(.*)$%$1%; |
548
|
0
|
0
|
|
|
|
|
if (exists $self->{ST_SRCMAP}->{$s}) { |
549
|
0
|
|
|
|
|
|
$s = join('/', $sbase, $s); |
550
|
0
|
0
|
|
|
|
|
delete $sl{$l} if src_slink($s); |
551
|
|
|
|
|
|
|
} |
552
|
|
|
|
|
|
|
} |
553
|
0
|
|
|
|
|
|
@sl = sort keys %sl; |
554
|
|
|
|
|
|
|
} |
555
|
0
|
0
|
|
|
|
|
my $comparator = $self->no_cmp ? undef : $self->cmp_func; |
556
|
0
|
|
|
|
|
|
SRC: for (sort keys %{$self->{ST_SRCMAP}}) { |
|
0
|
|
|
|
|
|
|
557
|
0
|
0
|
0
|
|
|
|
next if $self->{ST_SRCMAP}->{$_}->{type} && |
558
|
|
|
|
|
|
|
$self->{ST_SRCMAP}->{$_}->{type} !~ /$type/; |
559
|
0
|
|
|
|
|
|
my $src = join('/', $sbase, $_); |
560
|
0
|
0
|
0
|
|
|
|
$src = $_ unless -e $src || src_slink($src); |
561
|
0
|
|
0
|
|
|
|
my $dst = join('/', $dbase, $self->{ST_SRCMAP}->{$_}->{dst} || $_); |
562
|
0
|
|
|
|
|
|
for my $s (@sl) { |
563
|
0
|
0
|
|
|
|
|
if ($dst =~ /^\Q$s\E/) { |
564
|
0
|
|
|
|
|
|
$self->{ST_DIRLNK}->{$s} = 1; |
565
|
0
|
|
|
|
|
|
$self->{ST_ADD}->{$_}->{src} = $src; |
566
|
0
|
|
|
|
|
|
$self->{ST_ADD}->{$_}->{dst} = $dst; |
567
|
0
|
|
|
|
|
|
next SRC; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
# It's possible for a symlink to not satisfy -e if it's dangling. |
571
|
|
|
|
|
|
|
# Case-insensitive file test operators are a problem on Windows. |
572
|
|
|
|
|
|
|
# You cannot modify files when they don't exist under the proper name. |
573
|
0
|
0
|
0
|
|
|
|
if (! ecs($dst) && ! ccsymlink($dst)) { |
|
|
0
|
0
|
|
|
|
|
574
|
0
|
|
|
|
|
|
$self->{ST_ADD}->{$_}->{src} = $src; |
575
|
0
|
|
|
|
|
|
$self->{ST_ADD}->{$_}->{dst} = $dst; |
576
|
|
|
|
|
|
|
} elsif (! -d $src || src_slink($src)) { |
577
|
0
|
0
|
|
|
|
|
if ($self->_needs_update($src, $dst, $comparator)) { |
578
|
0
|
|
|
|
|
|
$self->{ST_MOD}->{$_}->{src} = $src; |
579
|
0
|
|
|
|
|
|
$self->{ST_MOD}->{$_}->{dst} = $dst; |
580
|
|
|
|
|
|
|
} |
581
|
|
|
|
|
|
|
} |
582
|
|
|
|
|
|
|
} |
583
|
0
|
0
|
|
|
|
|
if ($self->{ST_DIRLNK}) { |
584
|
0
|
|
|
|
|
|
my @rem; |
585
|
0
|
|
|
|
|
|
my @slst = sort keys %{$self->{ST_DIRLNK}}; |
|
0
|
|
|
|
|
|
|
586
|
0
|
|
|
|
|
|
for (reverse @slst) { |
587
|
0
|
|
|
|
|
|
for my $l (@slst) { |
588
|
0
|
0
|
|
|
|
|
if (/^\Q$l\E./) { |
589
|
0
|
|
|
|
|
|
push @rem, $_; |
590
|
0
|
|
|
|
|
|
last; |
591
|
|
|
|
|
|
|
} |
592
|
|
|
|
|
|
|
} |
593
|
|
|
|
|
|
|
} |
594
|
0
|
0
|
|
|
|
|
delete @{$self->{ST_DIRLNK}}{@rem} if @rem; |
|
0
|
|
|
|
|
|
|
595
|
0
|
0
|
|
|
|
|
unlink $self->{ST_DIRLNK} unless keys %{$self->{ST_DIRLNK}}; |
|
0
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
} |
597
|
|
|
|
|
|
|
# Last, check for subtractions but only if asked - it's potentially |
598
|
|
|
|
|
|
|
# expensive and error-prone. |
599
|
0
|
0
|
|
|
|
|
return unless $self->remove; |
600
|
0
|
|
|
|
|
|
my(%dirs, %files, %xfiles); |
601
|
|
|
|
|
|
|
my $wanted = sub { |
602
|
0
|
|
|
0
|
|
|
my $path = $File::Find::name; |
603
|
0
|
0
|
|
|
|
|
return if $path eq $dbase; |
604
|
0
|
0
|
|
|
|
|
if ($path =~ /lost\+found/) { |
605
|
0
|
|
|
|
|
|
$File::Find::prune = 1; |
606
|
0
|
|
|
|
|
|
return; |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
# Get a relative path from the absolute path. |
609
|
0
|
|
|
|
|
|
(my $relpath = $path) =~ s%^\Q$dbase\E\W?%%; |
610
|
0
|
0
|
|
|
|
|
if (ccsymlink($path)) { # Vob link |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
611
|
0
|
|
|
|
|
|
$files{$relpath} = $path; |
612
|
|
|
|
|
|
|
} elsif (-d $path) { |
613
|
0
|
|
|
|
|
|
$dirs{$path} = $relpath; |
614
|
|
|
|
|
|
|
} elsif (-f $path) { |
615
|
0
|
|
|
|
|
|
$files{$relpath} = $path; |
616
|
|
|
|
|
|
|
} |
617
|
0
|
|
|
|
|
|
}; |
618
|
0
|
|
|
|
|
|
find($wanted, $dbase); |
619
|
0
|
|
|
|
|
|
my %dst2src; |
620
|
0
|
|
|
|
|
|
for (keys %{$self->{ST_SRCMAP}}) { |
|
0
|
|
|
|
|
|
|
621
|
0
|
|
|
|
|
|
my $dst = $self->{ST_SRCMAP}->{$_}->{dst}; |
622
|
0
|
0
|
|
|
|
|
$dst2src{$dst} = $_ if $dst; |
623
|
|
|
|
|
|
|
} |
624
|
0
|
|
|
|
|
|
for (sort keys %files) { |
625
|
0
|
0
|
0
|
|
|
|
next if $self->{ST_SRCMAP}->{$_} && !$self->{ST_SRCMAP}->{$_}->{dst}; |
626
|
0
|
0
|
|
|
|
|
$xfiles{$files{$_}}++ if !$dst2src{$_}; |
627
|
|
|
|
|
|
|
} |
628
|
0
|
|
|
|
|
|
$self->{ST_SUB}->{exfiles} = \%xfiles; |
629
|
0
|
|
|
|
|
|
$self->{ST_SUB}->{dirs} = \%dirs; |
630
|
|
|
|
|
|
|
} |
631
|
|
|
|
|
|
|
|
632
|
|
|
|
|
|
|
sub preview { |
633
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
634
|
0
|
|
|
|
|
|
my $indent = ' ' x 4; |
635
|
0
|
|
|
|
|
|
my($adds, $mods, $subs) = (0, 0, 0); |
636
|
0
|
0
|
|
|
|
|
if ($self->{ST_DIRLNK}) { |
637
|
0
|
|
|
|
|
|
my $dl = keys %{$self->{ST_DIRLNK}}; |
|
0
|
|
|
|
|
|
|
638
|
0
|
|
|
|
|
|
print "Removing $dl directory symlinks:\n"; |
639
|
0
|
|
|
|
|
|
for (sort keys %{$self->{ST_DIRLNK}}) { |
|
0
|
|
|
|
|
|
|
640
|
0
|
|
|
|
|
|
print "${indent}$_\n"; |
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
} |
643
|
0
|
0
|
|
|
|
|
if ($self->{ST_ADD}) { |
644
|
0
|
|
|
|
|
|
$adds = keys %{$self->{ST_ADD}}; |
|
0
|
|
|
|
|
|
|
645
|
0
|
|
|
|
|
|
print "Adding $adds elements:\n"; |
646
|
0
|
|
|
|
|
|
for (sort keys %{$self->{ST_ADD}}) { |
|
0
|
|
|
|
|
|
|
647
|
0
|
|
|
|
|
|
printf "$indent%s +=>\n\t%s\n", $self->{ST_ADD}->{$_}->{src}, |
648
|
|
|
|
|
|
|
$self->{ST_ADD}->{$_}->{dst}; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
} |
651
|
0
|
0
|
|
|
|
|
if ($self->{ST_MOD}) { |
652
|
0
|
|
|
|
|
|
$mods = keys %{$self->{ST_MOD}}; |
|
0
|
|
|
|
|
|
|
653
|
0
|
|
|
|
|
|
print "Modifying $mods elements:\n"; |
654
|
0
|
|
|
|
|
|
for (sort keys %{$self->{ST_MOD}}) { |
|
0
|
|
|
|
|
|
|
655
|
0
|
|
|
|
|
|
printf "$indent%s ==>\n\t%s\n", $self->{ST_MOD}->{$_}->{src}, |
656
|
|
|
|
|
|
|
$self->{ST_MOD}->{$_}->{dst}; |
657
|
|
|
|
|
|
|
} |
658
|
|
|
|
|
|
|
} |
659
|
0
|
0
|
0
|
|
|
|
if ($self->remove && $self->{ST_SUB}) { |
660
|
0
|
|
|
|
|
|
my @exfiles = sort keys %{$self->{ST_SUB}->{exfiles}}; |
|
0
|
|
|
|
|
|
|
661
|
0
|
|
|
|
|
|
$subs = @exfiles; |
662
|
0
|
0
|
|
|
|
|
print "Subtracting $subs elements:\n" if $subs; |
663
|
0
|
|
|
|
|
|
for (@exfiles) { |
664
|
0
|
|
|
|
|
|
printf "$indent%s\n", $_; |
665
|
|
|
|
|
|
|
} |
666
|
|
|
|
|
|
|
} |
667
|
0
|
|
|
|
|
|
my $total = $adds + $mods + $subs; |
668
|
0
|
|
|
|
|
|
print "Element change summary: add=$adds modify=$mods subtract=$subs\n"; |
669
|
0
|
|
|
|
|
|
return $total; |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
sub pbrtype { |
673
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
674
|
0
|
|
|
|
|
|
my $bt = shift; |
675
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct; |
676
|
0
|
|
|
|
|
|
my $vob = $self->{ST_DSTVOB}; |
677
|
0
|
0
|
|
|
|
|
if (!defined($self->{ST_PBTYPES}->{$bt})) { |
678
|
0
|
|
|
|
|
|
my $tc = $ct->des([qw(-fmt %[type_constraint]p)], |
679
|
|
|
|
|
|
|
"brtype:$bt\@$vob")->qx; |
680
|
0
|
|
|
|
|
|
$self->{ST_PBTYPES}->{$bt} = ($tc =~ /one version per branch/); |
681
|
|
|
|
|
|
|
} |
682
|
0
|
|
|
|
|
|
return $self->{ST_PBTYPES}->{$bt}; |
683
|
|
|
|
|
|
|
} |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
sub branchco { |
686
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
687
|
0
|
|
|
|
|
|
my $dir = shift; |
688
|
0
|
|
|
|
|
|
my @ele = @_; |
689
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autochomp=>0}); |
690
|
0
|
|
|
|
|
|
my $rc; |
691
|
0
|
0
|
|
|
|
|
if ($self->{branchoffroot}) { |
692
|
0
|
|
|
|
|
|
foreach my $e (@ele) { |
693
|
0
|
|
|
|
|
|
my $sel = $ct->ls(['-d'], $e)->autochomp(1)->qx; |
694
|
0
|
0
|
|
|
|
|
if ($sel =~ /^(.*?) +Rule:.*-mkbranch (.*?)\]?$/) { |
695
|
0
|
|
|
|
|
|
my ($ver, $bt) = ($1, $2); |
696
|
0
|
|
|
|
|
|
my $sil = $self->clone_ct({stdout=>0, stderr=>0}); |
697
|
0
|
|
|
|
|
|
my $main = 'main'; |
698
|
0
|
0
|
|
|
|
|
if ($sil->des(['-s'], "$e\@\@/main/0")->system) { |
699
|
0
|
|
|
|
|
|
$main = ($ct->lsvtree($e)->autochomp(1)->qx)[0]; |
700
|
0
|
|
|
|
|
|
$main =~ s%^[^@]*\@\@[\\/](.*)\r?$%$1%; |
701
|
|
|
|
|
|
|
} |
702
|
0
|
0
|
|
|
|
|
my $re = $self->pbrtype($bt) ? |
703
|
|
|
|
|
|
|
qr([\\/]${main}[\\/]$bt[\\/]\d+$) : qr([\\/]$bt[\\/]\d+$); |
704
|
0
|
0
|
|
|
|
|
if ($ver =~ m%$re%) { |
705
|
0
|
|
|
|
|
|
$rc |= $ct->co($self->comment, $e)->system; |
706
|
|
|
|
|
|
|
} else { |
707
|
0
|
|
|
|
|
|
my $r = $ct->mkbranch([@{$self->comment}, '-ver', |
|
0
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
"/${main}/0", $bt], $e)->system; |
709
|
0
|
0
|
|
|
|
|
if ($r) { |
710
|
0
|
|
|
|
|
|
$rc = 1; |
711
|
|
|
|
|
|
|
} else { |
712
|
0
|
0
|
|
|
|
|
if ($ver !~ m%\@\@[\\/]${main}[\\/]0$%) { |
713
|
0
|
0
|
|
|
|
|
$rc |= $dir ? |
714
|
|
|
|
|
|
|
$ct->merge(['-to', $e], |
715
|
|
|
|
|
|
|
$ver)->stdout(0)->system : |
716
|
|
|
|
|
|
|
$ct->merge(['-ndata', '-to', $e], |
717
|
|
|
|
|
|
|
$ver)->stdout(0)->system; |
718
|
0
|
|
|
|
|
|
unlink("$e.contrib"); |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
} |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
} else { |
723
|
0
|
|
|
|
|
|
$rc |= $ct->co($self->comment, $e)->system; |
724
|
|
|
|
|
|
|
} |
725
|
|
|
|
|
|
|
} |
726
|
|
|
|
|
|
|
} else { |
727
|
0
|
|
|
|
|
|
$rc = $ct->co($self->comment, @ele)->system; |
728
|
|
|
|
|
|
|
} |
729
|
0
|
|
|
|
|
|
return $rc; |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
sub rmdirlinks { |
733
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
734
|
0
|
0
|
|
|
|
|
return unless $self->{ST_DIRLNK}; |
735
|
0
|
|
|
|
|
|
my $lsco = ClearCase::Argv->lsco([qw(-s -d -cview)]); |
736
|
0
|
|
|
|
|
|
for (sort {$b cmp $a} keys %{$self->{ST_DIRLNK}}) { |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
737
|
0
|
|
|
|
|
|
my $dad = dirname $_; |
738
|
0
|
0
|
|
|
|
|
$self->branchco(1, $dad) unless $lsco->args($dad)->qx; |
739
|
0
|
|
|
|
|
|
$self->clone_ct->rm($_)->system; |
740
|
0
|
|
|
|
|
|
delete $self->{ST_SUB}->{exfiles}->{$_}; #If it is there |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
} |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
sub mkrellink { |
745
|
0
|
|
|
0
|
0
|
|
my ($self, $src) = @_; |
746
|
0
|
|
|
|
|
|
my $txt = src_rlink($src); |
747
|
0
|
|
|
|
|
|
my $sbase = $self->srcbase; |
748
|
0
|
0
|
0
|
|
|
|
return $txt unless $self->{ST_RELLINKS} and ($txt =~ /^$sbase/); |
749
|
0
|
|
|
|
|
|
$txt =~ s%^$sbase/(.*)%$1%; |
750
|
0
|
|
|
|
|
|
$src =~ s%^$sbase/(.*)%$1%; |
751
|
0
|
|
|
|
|
|
my @t = split m%/%, $txt; |
752
|
0
|
|
|
|
|
|
my @s = split m%/%, $src; |
753
|
0
|
|
|
|
|
|
my $i = 0; |
754
|
0
|
|
|
|
|
|
while ($t[$i] eq $s[$i]) { |
755
|
0
|
|
|
|
|
|
$i++; |
756
|
0
|
|
|
|
|
|
shift @t; |
757
|
0
|
|
|
|
|
|
shift @s; |
758
|
|
|
|
|
|
|
} |
759
|
0
|
|
|
|
|
|
while ($i++ < $#s) { unshift @t, '..'; } |
|
0
|
|
|
|
|
|
|
760
|
0
|
|
|
|
|
|
$txt = join '/', @t; |
761
|
0
|
|
|
|
|
|
return $txt; |
762
|
|
|
|
|
|
|
} |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
# Remove spurious names from restored directory |
765
|
|
|
|
|
|
|
sub skimdir { |
766
|
0
|
|
|
0
|
0
|
|
my ($self, $dst, $pfx) = @_; |
767
|
0
|
|
|
|
|
|
my $flt = qr{^(\Q$pfx\E.*?)(?:/.*)?$}; # paths normalized |
768
|
0
|
|
|
|
|
|
opendir(DIR, $dst); |
769
|
0
|
|
|
|
|
|
my @f = grep !m%^\.\.?$%, readdir DIR; |
770
|
0
|
|
|
|
|
|
closedir DIR; |
771
|
0
|
|
|
|
|
|
my %ok = map {$_ => 1} grep s%$flt%$1%, keys %{$self->{ST_SRCMAP}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
772
|
0
|
|
|
|
|
|
for (@f) { |
773
|
0
|
|
|
|
|
|
my $f = $pfx . $_; |
774
|
0
|
0
|
|
|
|
|
$self->{ST_SUB}->{exfiles}->{join('/', $dst, $_)}++ unless $ok{$f}; |
775
|
|
|
|
|
|
|
} |
776
|
|
|
|
|
|
|
} |
777
|
|
|
|
|
|
|
|
778
|
|
|
|
|
|
|
sub vtree { |
779
|
0
|
|
|
0
|
0
|
|
my ($self, $dir) = @_; |
780
|
0
|
0
|
|
|
|
|
if (!exists $self->{ST_VT}->{$dir}) { |
781
|
0
|
|
|
|
|
|
my $vt = ClearCase::Argv->lsvtree({autochomp=>1}, [qw(-a -s -nco)]); |
782
|
|
|
|
|
|
|
# optimization: branch/0 of a directory is either empty or duplicate |
783
|
0
|
0
|
|
|
|
|
my @vt = reverse grep { m%[/\\](\d+)$% && $1>=1 } $vt->args($dir)->qx; |
|
0
|
|
|
|
|
|
|
784
|
0
|
|
|
|
|
|
$self->{ST_VT}->{$dir} = \@vt; |
785
|
|
|
|
|
|
|
} |
786
|
0
|
|
|
|
|
|
return $self->{ST_VT}->{$dir}; |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
# Once a directory version was found, move it first in the list for next tries |
790
|
|
|
|
|
|
|
sub raise_dver { |
791
|
0
|
|
|
0
|
0
|
|
my ($self, $i, $dir) = @_; |
792
|
0
|
0
|
|
|
|
|
return unless $i; |
793
|
0
|
|
|
|
|
|
my $vt = $self->{ST_VT}->{$dir}; |
794
|
0
|
|
|
|
|
|
my $ver = splice @{$vt}, $i, 1; |
|
0
|
|
|
|
|
|
|
795
|
0
|
|
|
|
|
|
unshift @{$vt}, $ver; |
|
0
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
|
798
|
|
|
|
|
|
|
# Reuse from removed elements, or create as view private, directories |
799
|
|
|
|
|
|
|
sub reusemkdir { |
800
|
0
|
|
|
0
|
0
|
|
my ($self, $dref, $rref) = @_; |
801
|
0
|
|
|
|
|
|
my (%found, %dfound, %priv); |
802
|
0
|
|
|
|
|
|
my $snapview = $self->snapdest; |
803
|
0
|
|
|
|
|
|
my $ds = ClearCase::Argv->desc({stderr=>1},[qw(-s)]); |
804
|
0
|
|
|
|
|
|
my $dm = ClearCase::Argv->desc([qw(-fmt %m)]); |
805
|
0
|
|
|
|
|
|
my $rm = ClearCase::Argv->rm; |
806
|
0
|
|
|
|
|
|
my $lsco = ClearCase::Argv->lsco([qw(-s -d -cview)]); |
807
|
0
|
|
|
|
|
|
my $ln = ClearCase::Argv->ln; |
808
|
0
|
|
|
|
|
|
for my $dst (sort keys %{$dref}) { |
|
0
|
|
|
|
|
|
|
809
|
0
|
0
|
|
|
|
|
next if $dfound{$dst}; |
810
|
0
|
|
|
|
|
|
my $reused; |
811
|
0
|
|
|
|
|
|
my($name, $dir) = fileparse($dst); |
812
|
0
|
0
|
|
|
|
|
if (!$priv{$dir}) { |
813
|
0
|
0
|
|
|
|
|
if ($rref->{$dst}) { |
814
|
0
|
0
|
|
|
|
|
$self->branchco(1, $dir) unless $lsco->args($dir)->qx; |
815
|
0
|
|
|
|
|
|
$rm->args($dst)->system; |
816
|
|
|
|
|
|
|
} |
817
|
0
|
|
|
|
|
|
my $i = -1; #index in the vtree list |
818
|
0
|
|
|
|
|
|
VER: for (@{$self->vtree($dir)}) { |
|
0
|
|
|
|
|
|
|
819
|
0
|
|
|
|
|
|
$i++; |
820
|
0
|
|
|
|
|
|
my $dirext = "$_/$name"; |
821
|
|
|
|
|
|
|
# case-insensitive file test operator on Windows is a problem |
822
|
0
|
0
|
|
|
|
|
if ($snapview ? $ds->args($dirext)->qx !~ /Error:/ : ecs($dirext)) { |
|
|
0
|
|
|
|
|
|
823
|
0
|
0
|
|
|
|
|
next if $dm->args($dirext)->qx eq 'file element'; |
824
|
0
|
|
|
|
|
|
while (ccsymlink($dirext)) { |
825
|
0
|
|
|
|
|
|
$name = readcclink $dirext; |
826
|
0
|
|
|
|
|
|
$name =~ s/\@\@$//; |
827
|
0
|
|
|
|
|
|
$dirext = "$_/$name"; |
828
|
|
|
|
|
|
|
# consider only relative, and local symlinks |
829
|
0
|
0
|
0
|
|
|
|
next VER if !ecs($dirext) || |
830
|
|
|
|
|
|
|
$dm->args($dirext)->qx eq 'file element'; |
831
|
|
|
|
|
|
|
} |
832
|
0
|
|
|
|
|
|
$reused = 1; |
833
|
0
|
|
|
|
|
|
$self->raise_dver($i, $dir); |
834
|
0
|
0
|
|
|
|
|
$self->branchco(1, $dir) unless $lsco->args($dir)->qx; |
835
|
0
|
|
|
|
|
|
$ln->args($dirext, $dst)->system; |
836
|
|
|
|
|
|
|
# Need to reevaluate all the files under this dir |
837
|
|
|
|
|
|
|
# The case of implicit dirs, is recorded as '.' |
838
|
0
|
0
|
|
|
|
|
my $d = $dref->{$dst} eq '.'? '' : $dref->{$dst} . '/'; |
839
|
0
|
0
|
|
|
|
|
$self->skimdir($dst, $d) if $self->remove; |
840
|
0
|
0
|
|
|
|
|
my $cmp = $self->no_cmp ? undef : $self->cmp_func; |
841
|
0
|
|
|
|
|
|
my @keys = sort $d? grep m%^\Q$d\E%, keys %{$self->{ST_ADD}} |
|
0
|
|
|
|
|
|
|
842
|
0
|
0
|
|
|
|
|
: keys %{$self->{ST_ADD}}; |
843
|
0
|
|
|
|
|
|
for my $e (@keys) { |
844
|
0
|
|
|
|
|
|
my $edst = join '/', $self->dstbase, $e; |
845
|
0
|
|
|
|
|
|
my @intdir = split m%/%, $e; |
846
|
0
|
|
|
|
|
|
pop @intdir; |
847
|
0
|
0
|
|
|
|
|
if (@intdir) { |
848
|
0
|
|
|
|
|
|
my $dd = $self->dstbase; |
849
|
0
|
|
|
|
|
|
my $pf = ''; |
850
|
0
|
|
|
|
|
|
while (my $id = shift @intdir) { |
851
|
0
|
|
|
|
|
|
$dd = join '/', $dd, $id; |
852
|
0
|
|
|
|
|
|
$pf = $pf . $id . '/'; |
853
|
0
|
0
|
0
|
|
|
|
$self->skimdir($dd, $pf) if -d $dd && !$dfound{$dd}++; |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
} |
856
|
|
|
|
|
|
|
# Problem: does it match the type under srcbase? |
857
|
0
|
0
|
0
|
|
|
|
if (-d $edst and !ccsymlink($edst)) { # We know it is empty |
858
|
0
|
|
|
|
|
|
opendir(DIR, $edst); |
859
|
0
|
|
|
|
|
|
my @f = grep !m%^\.\.?$%, readdir DIR; |
860
|
0
|
|
|
|
|
|
closedir DIR; |
861
|
0
|
0
|
|
|
|
|
if (@f) { |
862
|
0
|
0
|
|
|
|
|
$self->branchco(1, $edst) |
863
|
|
|
|
|
|
|
unless $lsco->args($edst)->qx; |
864
|
0
|
|
|
|
|
|
$rm->args(map{join '/', $edst, $_} @f)->system; |
|
0
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
} |
866
|
0
|
|
|
|
|
|
$dfound{$edst}++; #Skip in this loop |
867
|
0
|
|
|
|
|
|
next; |
868
|
|
|
|
|
|
|
} |
869
|
0
|
0
|
|
|
|
|
if (exists($self->{ST_ADD}->{$e}->{dst})) { |
870
|
0
|
|
|
|
|
|
my $src = $self->{ST_ADD}->{$e}->{src}; |
871
|
0
|
|
|
|
|
|
my $dst = $self->{ST_ADD}->{$e}->{dst}; |
872
|
0
|
0
|
|
|
|
|
if (-e $dst) { |
873
|
0
|
0
|
|
|
|
|
$self->{ST_MOD}->{$e} = $self->{ST_ADD}->{$e} |
874
|
|
|
|
|
|
|
if $self->_needs_update($src, $dst, $cmp); |
875
|
0
|
|
|
|
|
|
$found{$e}++; #Remove from the add list |
876
|
|
|
|
|
|
|
} |
877
|
|
|
|
|
|
|
} |
878
|
|
|
|
|
|
|
} |
879
|
0
|
|
|
|
|
|
last; |
880
|
|
|
|
|
|
|
} |
881
|
|
|
|
|
|
|
} |
882
|
|
|
|
|
|
|
} |
883
|
0
|
0
|
|
|
|
|
if (!$reused) { |
884
|
0
|
|
|
|
|
|
my $err; |
885
|
0
|
|
|
|
|
|
mkpath($dst, {error => \$err, verbose => 0, mode => 0777}); |
886
|
0
|
0
|
0
|
|
|
|
$self->failm(join(': ', %{$err->[0]})) if $err and @{$err}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
887
|
0
|
|
|
|
|
|
$priv{"${dst}/"}++; |
888
|
|
|
|
|
|
|
} |
889
|
|
|
|
|
|
|
} |
890
|
0
|
|
|
|
|
|
return %found; |
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
|
893
|
|
|
|
|
|
|
# recursively record parent directories, and clashing objects to remove |
894
|
|
|
|
|
|
|
sub recadd { |
895
|
0
|
|
|
0
|
0
|
|
my ($self, $src, $dst, $dir, $rm, $seen) = @_; |
896
|
0
|
|
|
|
|
|
my $dad = dirname($dst); |
897
|
0
|
0
|
0
|
|
|
|
return if $seen->{$dad}++ || (-d $dad && !ccsymlink($dad)); #exists, normal |
|
|
|
0
|
|
|
|
|
898
|
0
|
|
|
|
|
|
my $sdad = dirname($src); |
899
|
0
|
|
|
|
|
|
$self->recadd($sdad, $dad, $dir, $rm, $seen); |
900
|
0
|
0
|
0
|
|
|
|
$rm->{$dad}++ if -f $dad || ccsymlink($dad); #something clashing: remove |
901
|
0
|
|
|
|
|
|
$dir->{$dad} = $sdad; |
902
|
|
|
|
|
|
|
} |
903
|
|
|
|
|
|
|
|
904
|
|
|
|
|
|
|
sub add { |
905
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
906
|
0
|
|
|
|
|
|
my $sbase = $self->srcbase; |
907
|
0
|
|
|
|
|
|
my $mbase = $self->_mkbase; |
908
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct; |
909
|
0
|
0
|
|
|
|
|
return if ! $self->{ST_ADD}; |
910
|
0
|
0
|
|
|
|
|
if ($self->reuse) { # First, reuse parent directories |
911
|
0
|
|
|
|
|
|
my (%dir, %rm, %dseen); |
912
|
|
|
|
|
|
|
# in the way if added in _mkbase as view private; ignore failures |
913
|
0
|
|
|
|
|
|
rmdir($_) for reverse sort @{$self->{ST_IMPLICIT_DIRS}}; |
|
0
|
|
|
|
|
|
|
914
|
0
|
|
|
|
|
|
for my $d (sort keys %{$self->{ST_ADD}}) { |
|
0
|
|
|
|
|
|
|
915
|
0
|
|
|
|
|
|
my $src = $self->{ST_ADD}->{$d}->{src}; |
916
|
0
|
|
|
|
|
|
my $dst = $self->{ST_ADD}->{$d}->{dst}; |
917
|
0
|
0
|
0
|
|
|
|
$dir{$dst} = $d if -d $src && !src_slink($src); # empty dir |
918
|
0
|
|
|
|
|
|
$self->recadd($d, $dst, \%dir, \%rm, \%dseen); |
919
|
|
|
|
|
|
|
} |
920
|
0
|
|
|
|
|
|
my %found = $self->reusemkdir(\%dir, \%rm); |
921
|
0
|
|
|
|
|
|
delete $self->{ST_ADD}->{$_} for keys %found; |
922
|
|
|
|
|
|
|
} |
923
|
0
|
|
|
|
|
|
for (sort keys %{$self->{ST_ADD}}) { |
|
0
|
|
|
|
|
|
|
924
|
0
|
|
|
|
|
|
my $src = $self->{ST_ADD}->{$_}->{src}; |
925
|
0
|
|
|
|
|
|
my $dst = $self->{ST_ADD}->{$_}->{dst}; |
926
|
0
|
|
|
|
|
|
my $err; |
927
|
0
|
0
|
0
|
|
|
|
if (-d $src && ! src_slink($src)) { # Already checked in the reuse case |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
928
|
0
|
0
|
|
|
|
|
-e $dst || mkpath($dst, {error=>\$err, verbose=>0, mode=>0777}); |
929
|
0
|
0
|
0
|
|
|
|
$self->failm(join(': ', %{$err->[0]})) if $err and @{$err}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
} elsif (-e $src) { |
931
|
0
|
|
|
|
|
|
my $dad = dirname($dst); |
932
|
0
|
0
|
|
|
|
|
-d $dad || mkpath($dad, {error=>\$err, verbose=>0, mode=>0777}); |
933
|
0
|
0
|
0
|
|
|
|
$self->failm(join(': ', %{$err->[0]})) if $err and @{$err}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
934
|
0
|
0
|
|
|
|
|
if (src_slink($src)) { |
935
|
0
|
0
|
|
|
|
|
open(SLINK, ">$dst$lext") || $self->failm("$dst$lext: $!"); |
936
|
0
|
|
|
|
|
|
print SLINK $self->mkrellink($src), "\n";; |
937
|
0
|
|
|
|
|
|
close(SLINK); |
938
|
|
|
|
|
|
|
} else { |
939
|
0
|
0
|
|
|
|
|
$self->{ST_CI_FROM}->{$_} = $self->{ST_ADD}->{$_} |
940
|
|
|
|
|
|
|
if !exists($self->{ST_PRE}->{$dst}); |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
} elsif (src_slink($src)) { #Dangling symlink: import |
943
|
0
|
0
|
|
|
|
|
open(SLINK, ">$dst$lext") || $self->failm("$dst$lext: $!"); |
944
|
0
|
|
|
|
|
|
print SLINK $self->mkrellink($src), "\n";; |
945
|
0
|
|
|
|
|
|
close(SLINK); |
946
|
|
|
|
|
|
|
} else { |
947
|
0
|
|
|
|
|
|
$ct->failm("$src: no such file or directory"); |
948
|
|
|
|
|
|
|
} |
949
|
|
|
|
|
|
|
} |
950
|
0
|
|
|
|
|
|
my @candidates = sort $self->_lsprivate(1), |
951
|
0
|
|
|
|
|
|
map { $_->{dst} } values %{$self->{ST_CI_FROM}}; |
|
0
|
|
|
|
|
|
|
952
|
0
|
0
|
|
|
|
|
return if !@candidates; |
953
|
|
|
|
|
|
|
# We'll be separating the elements-to-be into files and directories. |
954
|
0
|
|
|
|
|
|
my(%files, @symlinks, %dirs); |
955
|
|
|
|
|
|
|
# If the parent directories of any of the candidates are |
956
|
|
|
|
|
|
|
# already versioned, we'll need to check them out unless |
957
|
|
|
|
|
|
|
# it's already been done. |
958
|
0
|
|
|
|
|
|
my @dads = sort map {dirname($_)} @candidates; |
|
0
|
|
|
|
|
|
|
959
|
0
|
|
|
|
|
|
my %lsd = map {split(/\s+Rule:\s+/, $_, 2)} |
|
0
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
$ct->argv('ls', [qw(-d -nxn -vis -vob)], @dads)->qx; |
961
|
0
|
|
|
|
|
|
for my $dad (keys %lsd) { |
962
|
|
|
|
|
|
|
# If already checked out, nothing to do. |
963
|
0
|
0
|
0
|
|
|
|
next if ! $lsd{$dad} || $lsd{$dad} =~ /CHECKEDOUT$/; |
964
|
|
|
|
|
|
|
# Now we know it's an element which needs to be checked out. |
965
|
0
|
|
|
|
|
|
$dad =~ s%\\%/%g if MSWIN; |
966
|
0
|
|
|
|
|
|
$dirs{$dad}++; |
967
|
|
|
|
|
|
|
} |
968
|
0
|
0
|
|
|
|
|
$self->branchco(1, keys %dirs) if keys %dirs; |
969
|
|
|
|
|
|
|
# Process candidate directories here, then do files below. |
970
|
0
|
|
|
|
|
|
my $mkdir = $self->clone_ct->mkdir({autofail=>0, autochomp=>0}, |
971
|
|
|
|
|
|
|
$self->comment); |
972
|
0
|
|
|
|
|
|
for my $cand (@candidates) { |
973
|
0
|
0
|
|
|
|
|
if (! -d $cand) { |
974
|
0
|
0
|
|
|
|
|
if ($cand =~ /$lext$/) { |
975
|
0
|
|
|
|
|
|
push(@symlinks, $cand); |
976
|
|
|
|
|
|
|
} else { |
977
|
0
|
|
|
|
|
|
$files{$cand} = 1; |
978
|
|
|
|
|
|
|
} |
979
|
0
|
|
|
|
|
|
next; |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
# Now we know we're dealing with directories. These cannot |
982
|
|
|
|
|
|
|
# exist at mkelem time so we move them aside, make |
983
|
|
|
|
|
|
|
# a versioned dir, then move all the files from the original |
984
|
|
|
|
|
|
|
# back into the new dir (still as view-private files). |
985
|
0
|
|
|
|
|
|
my $tmpdir = "$cand.$$.keep.d"; |
986
|
0
|
0
|
|
|
|
|
if (!rename($cand, $tmpdir)) { |
987
|
0
|
|
|
|
|
|
warn "$0: Error: can't rename '$cand' to '$tmpdir': $!\n"; |
988
|
0
|
|
|
|
|
|
$ct->fail; |
989
|
0
|
|
|
|
|
|
next; |
990
|
|
|
|
|
|
|
} |
991
|
0
|
0
|
|
|
|
|
if ($mkdir->args($cand)->system) { |
992
|
0
|
0
|
|
|
|
|
warn "Warning: unable to rename $tmpdir back to $cand!" |
993
|
|
|
|
|
|
|
unless rename($tmpdir, $cand); |
994
|
0
|
|
|
|
|
|
$ct->fail; |
995
|
0
|
|
|
|
|
|
next; |
996
|
|
|
|
|
|
|
} |
997
|
0
|
0
|
|
|
|
|
if (!opendir(DIR, $tmpdir)) { |
998
|
0
|
|
|
|
|
|
warn "$0: Error: $tmpdir: $!"; |
999
|
0
|
|
|
|
|
|
$ct->fail; |
1000
|
0
|
|
|
|
|
|
next; |
1001
|
|
|
|
|
|
|
} |
1002
|
0
|
|
|
|
|
|
while (defined(my $i = readdir(DIR))) { |
1003
|
0
|
0
|
0
|
|
|
|
next if $i eq '.' || $i eq '..'; |
1004
|
0
|
0
|
|
|
|
|
rename("$tmpdir/$i", "$cand/$i") || $self->failm("$cand/$i: $!"); |
1005
|
|
|
|
|
|
|
} |
1006
|
0
|
|
|
|
|
|
closedir DIR; |
1007
|
0
|
0
|
|
|
|
|
rmdir $tmpdir || warn "$0: Error: $tmpdir: $!"; |
1008
|
|
|
|
|
|
|
} |
1009
|
|
|
|
|
|
|
|
1010
|
|
|
|
|
|
|
# Optionally, reconstitute an old element of the same name if present. |
1011
|
0
|
0
|
|
|
|
|
if ($self->reuse) { |
1012
|
0
|
|
|
|
|
|
my $snapview = $self->snapdest; |
1013
|
0
|
|
|
|
|
|
my $ds = ClearCase::Argv->desc({stderr=>1}, [qw(-s)]); |
1014
|
0
|
|
|
|
|
|
my $dm = ClearCase::Argv->desc([qw(-fmt %m)]); |
1015
|
0
|
|
|
|
|
|
my $ln = ClearCase::Argv->ln; |
1016
|
0
|
|
|
|
|
|
my %reused; |
1017
|
0
|
|
|
|
|
|
for my $elem (keys %files) { |
1018
|
0
|
|
|
|
|
|
my($name, $dir) = fileparse($elem); |
1019
|
0
|
|
|
|
|
|
my $i = -1; |
1020
|
0
|
|
|
|
|
|
DVER: |
1021
|
0
|
|
|
|
|
|
for (@{$self->vtree($dir)}) { |
1022
|
0
|
|
|
|
|
|
$i++; |
1023
|
0
|
|
|
|
|
|
my $dirext = "$_/$name@@"; |
1024
|
|
|
|
|
|
|
# case-insensitive file test operator on Windows is a problem |
1025
|
0
|
0
|
|
|
|
|
if ($snapview ? $ds->args($dirext)->qx !~ /Error:/ : |
|
|
0
|
|
|
|
|
|
1026
|
|
|
|
|
|
|
ecs("$_/$name")) { |
1027
|
0
|
0
|
|
|
|
|
next if $dm->args("$_/$name")->qx =~ /^directory /; |
1028
|
0
|
|
|
|
|
|
while (ccsymlink("$_/$name")) { |
1029
|
0
|
|
|
|
|
|
$name = readcclink "$_/$name"; |
1030
|
0
|
|
|
|
|
|
$name =~ s/\@\@$//; |
1031
|
0
|
0
|
0
|
|
|
|
next DVER if !ecs("$_/$name") || |
1032
|
|
|
|
|
|
|
$dm->args("$_/$name")->qx =~ /^directory /; |
1033
|
|
|
|
|
|
|
} |
1034
|
0
|
|
|
|
|
|
$reused{$elem} = 1; |
1035
|
0
|
|
|
|
|
|
delete $files{$elem}; |
1036
|
0
|
|
|
|
|
|
unlink($elem); |
1037
|
0
|
|
|
|
|
|
$ln->args("$_/$name", $elem)->system; |
1038
|
0
|
|
|
|
|
|
$self->raise_dver($i, $dir); |
1039
|
0
|
|
|
|
|
|
last; |
1040
|
|
|
|
|
|
|
} |
1041
|
|
|
|
|
|
|
} |
1042
|
|
|
|
|
|
|
} |
1043
|
|
|
|
|
|
|
# If any elements were "reconstituted", they must be taken off the |
1044
|
|
|
|
|
|
|
# list of elems to be checked in explicitly, since 'ct ln' is |
1045
|
|
|
|
|
|
|
# just a directory op. |
1046
|
0
|
|
|
|
|
|
my %xkeys; |
1047
|
0
|
0
|
0
|
|
|
|
if (!$self->no_cr && %reused) { |
1048
|
0
|
|
|
|
|
|
for (keys %{$self->{ST_CI_FROM}}) { |
|
0
|
|
|
|
|
|
|
1049
|
0
|
0
|
0
|
|
|
|
if (exists($self->{ST_CI_FROM}->{$_}) |
|
|
|
0
|
|
|
|
|
1050
|
|
|
|
|
|
|
&& exists($self->{ST_CI_FROM}->{$_}->{dst}) |
1051
|
|
|
|
|
|
|
&& exists($reused{$self->{ST_CI_FROM}->{$_}->{dst}})) { |
1052
|
0
|
|
|
|
|
|
$xkeys{$_} = 1; |
1053
|
|
|
|
|
|
|
} |
1054
|
|
|
|
|
|
|
} |
1055
|
0
|
|
|
|
|
|
for (keys %xkeys) { |
1056
|
0
|
|
|
|
|
|
delete $self->{ST_CI_FROM}->{$_}; |
1057
|
|
|
|
|
|
|
} |
1058
|
|
|
|
|
|
|
} |
1059
|
|
|
|
|
|
|
# Also, reconstituted elements may now be candidates for |
1060
|
|
|
|
|
|
|
# modification. Re-analyze the status for these. If any of |
1061
|
|
|
|
|
|
|
# them differ from their counterparts in the src area, copy |
1062
|
|
|
|
|
|
|
# them from the ADD list to the MOD list. |
1063
|
0
|
0
|
|
|
|
|
my $comparator = $self->no_cmp ? undef : $self->cmp_func; |
1064
|
0
|
|
|
|
|
|
for my $elem (keys %{$self->{ST_ADD}}) { |
|
0
|
|
|
|
|
|
|
1065
|
0
|
0
|
|
|
|
|
if (exists($reused{$self->{ST_ADD}->{$elem}->{dst}})) { |
1066
|
0
|
|
|
|
|
|
my $src = $self->{ST_ADD}->{$elem}->{src}; |
1067
|
0
|
|
|
|
|
|
my $dst = $self->{ST_ADD}->{$elem}->{dst}; |
1068
|
0
|
0
|
|
|
|
|
if ($self->_needs_update($src, $dst, $comparator)) { |
1069
|
0
|
|
|
|
|
|
$self->{ST_MOD}->{$elem} = $self->{ST_ADD}->{$elem}; |
1070
|
|
|
|
|
|
|
} |
1071
|
|
|
|
|
|
|
} |
1072
|
|
|
|
|
|
|
} |
1073
|
|
|
|
|
|
|
} |
1074
|
0
|
|
|
|
|
|
for (sort keys %{$self->{ST_CI_FROM}}) { |
|
0
|
|
|
|
|
|
|
1075
|
0
|
|
|
|
|
|
my $dst = $self->{ST_CI_FROM}->{$_}->{dst}; |
1076
|
0
|
0
|
|
|
|
|
if ($files{$dst}) { |
1077
|
0
|
|
|
|
|
|
my $src = $self->{ST_CI_FROM}->{$_}->{src}; |
1078
|
0
|
0
|
|
|
|
|
copy($src, $dst) || $ct->failm("$_: $!"); |
1079
|
0
|
0
|
|
|
|
|
utime(time(), (stat $src)[9], $dst) || |
1080
|
|
|
|
|
|
|
warn "Warning: $dst: touch failed"; |
1081
|
|
|
|
|
|
|
} |
1082
|
|
|
|
|
|
|
} |
1083
|
|
|
|
|
|
|
# Now do the files in one fell swoop. |
1084
|
0
|
0
|
|
|
|
|
$ct->mkelem($self->comment, sort keys %files)->system if %files; |
1085
|
|
|
|
|
|
|
|
1086
|
|
|
|
|
|
|
# Deal with symlinks. |
1087
|
0
|
|
|
|
|
|
for my $symlink (@symlinks) { |
1088
|
0
|
|
|
|
|
|
(my $lnk = $symlink) =~ s/$lext$//; |
1089
|
0
|
0
|
|
|
|
|
if (!open(SLINK, $symlink)) { |
1090
|
0
|
|
|
|
|
|
warn "$symlink: $!"; |
1091
|
0
|
|
|
|
|
|
next; |
1092
|
|
|
|
|
|
|
} |
1093
|
0
|
|
|
|
|
|
chomp(my $txt = ); |
1094
|
0
|
|
|
|
|
|
close SLINK; |
1095
|
0
|
|
|
|
|
|
unlink $symlink; |
1096
|
0
|
|
|
|
|
|
$ct->ln(['-s'], $txt, $lnk)->system; |
1097
|
|
|
|
|
|
|
} |
1098
|
|
|
|
|
|
|
} |
1099
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
# Tried to use Cwd::abs_path, but it behaves differently on Cygwin and UNIX |
1101
|
|
|
|
|
|
|
sub absdst { |
1102
|
0
|
|
|
0
|
0
|
|
my ($self, $dir, $f) = @_; |
1103
|
0
|
0
|
|
|
|
|
if ($f =~ /^\./) { |
1104
|
0
|
|
|
|
|
|
my $sep = qr{[/\\]}; |
1105
|
0
|
|
|
|
|
|
my @d = split $sep, $dir; |
1106
|
0
|
|
|
|
|
|
while ($f =~ s/^(\.\.?$sep)//) { |
1107
|
0
|
0
|
|
|
|
|
pop @d if $1 =~ /^\.{2}/; |
1108
|
|
|
|
|
|
|
} |
1109
|
0
|
|
|
|
|
|
$dir = join '/', @d; |
1110
|
|
|
|
|
|
|
} |
1111
|
0
|
|
|
|
|
|
return File::Spec->catfile($dir, $f); |
1112
|
|
|
|
|
|
|
} |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
sub modify { |
1115
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1116
|
0
|
0
|
|
|
|
|
return if !keys %{$self->{ST_MOD}}; |
|
0
|
|
|
|
|
|
|
1117
|
0
|
|
|
|
|
|
my(%files, %symlinks); |
1118
|
0
|
|
|
|
|
|
for (keys %{$self->{ST_MOD}}) { |
|
0
|
|
|
|
|
|
|
1119
|
0
|
0
|
|
|
|
|
if (src_slink($self->{ST_MOD}->{$_}->{src})) { |
1120
|
0
|
|
|
|
|
|
$symlinks{$_}++; |
1121
|
|
|
|
|
|
|
} else { |
1122
|
0
|
|
|
|
|
|
$files{$_}++; |
1123
|
|
|
|
|
|
|
} |
1124
|
|
|
|
|
|
|
} |
1125
|
0
|
|
|
|
|
|
my $rm = $self->clone_ct('rmname'); |
1126
|
0
|
|
|
|
|
|
my $ln = $self->clone_ct('ln'); |
1127
|
0
|
|
|
|
|
|
$ln->opts('-s', $ln->opts); |
1128
|
0
|
|
|
|
|
|
my $lsco = ClearCase::Argv->lsco([qw(-s -d -cview)]); |
1129
|
0
|
0
|
|
|
|
|
my $comparator = $self->no_cmp ? undef : $self->cmp_func; |
1130
|
0
|
0
|
|
|
|
|
if (keys %files) { |
1131
|
0
|
|
|
|
|
|
my (@toco, @del); |
1132
|
0
|
|
|
|
|
|
for my $key (sort keys %files) { |
1133
|
0
|
|
|
|
|
|
my $src = $self->{ST_MOD}->{$key}->{src}; |
1134
|
0
|
|
|
|
|
|
my $dst = $self->{ST_MOD}->{$key}->{dst}; |
1135
|
0
|
|
|
|
|
|
my $new; |
1136
|
0
|
0
|
|
|
|
|
if (ccsymlink($dst)) { |
1137
|
|
|
|
|
|
|
# The source is a file, but the destination is a symlink: look |
1138
|
|
|
|
|
|
|
# (recursively) at what this one points to, and link in this |
1139
|
|
|
|
|
|
|
# file. |
1140
|
|
|
|
|
|
|
# Build up the path of the destination, in such a way that it |
1141
|
|
|
|
|
|
|
# may be found, or not, in the hash. |
1142
|
0
|
|
|
|
|
|
my $dangling; |
1143
|
0
|
|
|
|
|
|
my $sep = qr%[/\\]%; |
1144
|
0
|
|
|
|
|
|
my $dst1 = $dst; |
1145
|
0
|
|
|
|
|
|
while (ccsymlink($dst1)) { |
1146
|
0
|
|
|
|
|
|
my $tgt = readcclink $dst1; |
1147
|
0
|
|
|
|
|
|
my $dir = dirname $dst1; |
1148
|
0
|
0
|
|
|
|
|
$tgt = $self->absdst($dir, $tgt) unless $tgt =~ m%^[/\\]%; |
1149
|
0
|
|
|
|
|
|
$tgt =~ s%\\%/%g if MSWIN; |
1150
|
0
|
0
|
|
|
|
|
if (-e $tgt) { |
1151
|
0
|
|
|
|
|
|
$dst1 = $tgt; |
1152
|
|
|
|
|
|
|
} else { |
1153
|
0
|
|
|
|
|
|
$dangling = 1; |
1154
|
0
|
|
|
|
|
|
last; |
1155
|
|
|
|
|
|
|
} |
1156
|
|
|
|
|
|
|
} |
1157
|
0
|
|
|
|
|
|
my $dir = dirname($dst); |
1158
|
0
|
0
|
|
|
|
|
$self->branchco(1, $dir) unless $lsco->args($dir)->qx; |
1159
|
0
|
|
|
|
|
|
$self->clone_ct->rm($dst)->system; #remove the first symlink |
1160
|
0
|
0
|
0
|
|
|
|
if ($dangling || !$self->{ST_SUB}->{exfiles}->{$dst1}) { |
1161
|
0
|
0
|
|
|
|
|
if (!copy($src, $dst)) { |
1162
|
0
|
|
|
|
|
|
warn "$0: Error: $dst: $!\n"; |
1163
|
0
|
|
|
|
|
|
$rm->fail; |
1164
|
|
|
|
|
|
|
} |
1165
|
0
|
0
|
|
|
|
|
utime(time(), (stat $src)[9], $dst) || |
1166
|
|
|
|
|
|
|
warn "Warning: $dst: touch failed"; |
1167
|
0
|
|
|
|
|
|
$self->clone_ct->mkelem($self->comment, $dst)->system; |
1168
|
0
|
|
|
|
|
|
$new = 1; |
1169
|
0
|
|
|
|
|
|
delete $self->{ST_MOD}->{$key}; |
1170
|
0
|
|
|
|
|
|
push @del, $key; |
1171
|
|
|
|
|
|
|
} else { |
1172
|
0
|
|
|
|
|
|
my $dir1 = dirname($dst1); |
1173
|
0
|
0
|
0
|
|
|
|
$self->branchco(1, $dir1) |
1174
|
|
|
|
|
|
|
unless ($dir eq $dir1) || $lsco->args($dir1)->qx; |
1175
|
0
|
|
|
|
|
|
$self->clone_ct->mv($dst1, $dst)->system; |
1176
|
0
|
|
|
|
|
|
delete $self->{ST_SUB}->{exfiles}->{$dst1}; #done already |
1177
|
0
|
0
|
|
|
|
|
if (!$self->_needs_update($src, $dst, $comparator)) { |
1178
|
0
|
|
|
|
|
|
delete $self->{ST_MOD}->{$key}; |
1179
|
0
|
|
|
|
|
|
push @del, $key; |
1180
|
|
|
|
|
|
|
} |
1181
|
|
|
|
|
|
|
} |
1182
|
|
|
|
|
|
|
} |
1183
|
0
|
0
|
0
|
|
|
|
push(@toco, $dst) unless exists($self->{ST_PRE}->{$dst}) || $new; |
1184
|
|
|
|
|
|
|
} |
1185
|
0
|
0
|
|
|
|
|
$self->branchco(0, @toco) if @toco; |
1186
|
0
|
|
|
|
|
|
delete @files{@del}; |
1187
|
0
|
|
|
|
|
|
for (sort keys %files) { |
1188
|
0
|
|
|
|
|
|
my $src = $self->{ST_MOD}->{$_}->{src}; |
1189
|
0
|
|
|
|
|
|
my $dst = $self->{ST_MOD}->{$_}->{dst}; |
1190
|
0
|
0
|
|
|
|
|
next if exists($self->{ST_PRE}->{$dst}); |
1191
|
0
|
0
|
|
|
|
|
if ($self->no_cr) { |
1192
|
0
|
0
|
|
|
|
|
if (!copy($src, $dst)) { |
1193
|
0
|
|
|
|
|
|
warn "$0: Error: $dst: $!\n"; |
1194
|
0
|
|
|
|
|
|
$rm->fail; |
1195
|
0
|
|
|
|
|
|
next; |
1196
|
|
|
|
|
|
|
} |
1197
|
0
|
0
|
|
|
|
|
utime(time(), (stat $src)[9], $dst) || |
1198
|
|
|
|
|
|
|
warn "Warning: $dst: touch failed"; |
1199
|
|
|
|
|
|
|
} else { |
1200
|
0
|
|
|
|
|
|
$self->{ST_CI_FROM}->{$_} = $self->{ST_MOD}->{$_}; |
1201
|
|
|
|
|
|
|
} |
1202
|
|
|
|
|
|
|
} |
1203
|
|
|
|
|
|
|
} |
1204
|
0
|
0
|
|
|
|
|
if (keys %symlinks) { |
1205
|
0
|
|
|
|
|
|
my %checkedout = map {$_ => 1} $self->_lsco; |
|
0
|
|
|
|
|
|
|
1206
|
0
|
|
|
|
|
|
for (sort keys %symlinks) { |
1207
|
0
|
|
|
|
|
|
my $txt = $self->mkrellink($self->{ST_MOD}->{$_}->{src}); |
1208
|
0
|
|
|
|
|
|
my $lnk = $self->{ST_MOD}->{$_}->{dst}; |
1209
|
0
|
|
|
|
|
|
my $dad = dirname($lnk); |
1210
|
0
|
0
|
|
|
|
|
if (!$checkedout{$dad}) { |
1211
|
0
|
0
|
|
|
|
|
$checkedout{$dad} = 1 if ! $self->branchco(1, $dad); |
1212
|
|
|
|
|
|
|
} |
1213
|
0
|
0
|
|
|
|
|
if (!$rm->args($lnk)->system) { |
1214
|
0
|
|
|
|
|
|
my @fil = grep /^\Q$lnk\E/, keys %{$self->{ST_SUB}->{exfiles}}; |
|
0
|
|
|
|
|
|
|
1215
|
0
|
|
|
|
|
|
delete @{$self->{ST_SUB}->{exfiles}}{@fil}; |
|
0
|
|
|
|
|
|
|
1216
|
0
|
|
|
|
|
|
delete $self->{ST_SUB}->{dirs}{$lnk}; |
1217
|
|
|
|
|
|
|
} |
1218
|
0
|
|
|
|
|
|
$ln->args($txt, $lnk)->system; |
1219
|
|
|
|
|
|
|
} |
1220
|
|
|
|
|
|
|
} |
1221
|
|
|
|
|
|
|
} |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
sub subtract { |
1224
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1225
|
0
|
0
|
|
|
|
|
return unless $self->{ST_SUB}; |
1226
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct; |
1227
|
0
|
|
|
|
|
|
my %co = map {$_ => 1} $self->_lsco; |
|
0
|
|
|
|
|
|
|
1228
|
0
|
|
|
|
|
|
my $exnames = $self->{ST_SUB}->{exfiles}; # Entries to remove |
1229
|
0
|
|
|
|
|
|
my (%dir, %keep); # Directories respectively to inspect, and to keep |
1230
|
0
|
|
|
|
|
|
$dir{dirname($_)}++ for keys %{$exnames}; |
|
0
|
|
|
|
|
|
|
1231
|
0
|
|
|
|
|
|
$dir{$_}++ for keys %{$self->{ST_SUB}->{dirs}}; # Existed originally |
|
0
|
|
|
|
|
|
|
1232
|
0
|
|
|
|
|
|
my $dbase = $self->dstbase; |
1233
|
0
|
|
|
|
|
|
for my $d (sort {$b cmp $a} keys %dir) { |
|
0
|
|
|
|
|
|
|
1234
|
0
|
0
|
|
|
|
|
next if $keep{$d}; |
1235
|
0
|
|
|
|
|
|
my ($k) = ($d =~ m%^\Q$dbase\E/(.*)$%); |
1236
|
0
|
0
|
0
|
|
|
|
if ($k and $self->{ST_SRCMAP}->{$k}) { |
1237
|
0
|
|
|
|
|
|
delete $exnames->{$d}; |
1238
|
0
|
|
|
|
|
|
my $dad = $d; |
1239
|
0
|
|
0
|
|
|
|
$keep{$dad}++ while $dad = dirname($dad) and $dad gt $dbase; |
1240
|
0
|
|
|
|
|
|
next; |
1241
|
|
|
|
|
|
|
} |
1242
|
0
|
0
|
|
|
|
|
if (opendir(DIR, $d)) { |
1243
|
0
|
|
|
|
|
|
my @entries = grep !/^\.\.?$/, readdir DIR; |
1244
|
0
|
|
|
|
|
|
closedir(DIR); |
1245
|
0
|
|
|
|
|
|
map { $_ = join('/', $d, $_) } @entries; |
|
0
|
|
|
|
|
|
|
1246
|
0
|
0
|
0
|
|
|
|
if (grep { !$exnames->{$_} and $ct->ls(['-s'], $_)->qx !~ /\@$/} |
|
0
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
@entries) { # Something not to delete--some version selected |
1248
|
0
|
|
|
|
|
|
my $dad = $d; |
1249
|
0
|
|
0
|
|
|
|
$keep{$dad}++ while $dad = dirname($dad) and $dad gt $dbase; |
1250
|
|
|
|
|
|
|
} else { |
1251
|
0
|
0
|
|
|
|
|
if (@entries) { |
1252
|
0
|
|
|
|
|
|
my @co = grep {$co{$_}} @entries; # Checkin before removing |
|
0
|
|
|
|
|
|
|
1253
|
0
|
0
|
|
|
|
|
$ct->ci($self->comment, @co)->system if @co; |
1254
|
0
|
|
|
|
|
|
delete @$exnames{@entries}; # Remove the contents |
1255
|
|
|
|
|
|
|
} |
1256
|
0
|
|
|
|
|
|
$exnames->{$d}++; # Add the container |
1257
|
|
|
|
|
|
|
} |
1258
|
|
|
|
|
|
|
} |
1259
|
|
|
|
|
|
|
} |
1260
|
0
|
|
|
|
|
|
delete @$exnames{keys %keep}; |
1261
|
0
|
|
|
|
|
|
my @exnames = keys %{$exnames}; |
|
0
|
|
|
|
|
|
|
1262
|
0
|
|
|
|
|
|
for my $dad (map {dirname($_)} @exnames) { |
|
0
|
|
|
|
|
|
|
1263
|
0
|
0
|
|
|
|
|
$self->branchco(1, $dad) unless $co{$dad}++; |
1264
|
|
|
|
|
|
|
} |
1265
|
|
|
|
|
|
|
# Force because of possible checkouts in other views. Fail for unreachable |
1266
|
0
|
0
|
|
|
|
|
$ct->rm([@{$self->comment}, '-f'], @exnames)->system if @exnames; |
|
0
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
} |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
sub label { |
1270
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1271
|
0
|
|
0
|
|
|
|
my $lbtype = shift || $self->lbtype; |
1272
|
0
|
0
|
|
|
|
|
return unless $lbtype; |
1273
|
0
|
|
|
|
|
|
my $dbase = $self->dstbase; |
1274
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autochomp=>0}); |
1275
|
0
|
|
|
|
|
|
my $ctq = $self->clone_ct({stdout=>0}); |
1276
|
0
|
|
|
|
|
|
my $ctbool = $self->clone_ct({autofail=>0, stdout=>0, stderr=>0}); |
1277
|
0
|
|
|
|
|
|
my $dvob = $self->dstvob; |
1278
|
0
|
|
|
|
|
|
my $locked; |
1279
|
0
|
0
|
|
|
|
|
if ($ctbool->lstype(['-s'], "lbtype:$lbtype\@$dvob")->system) { |
|
|
0
|
|
|
|
|
|
1280
|
0
|
|
|
|
|
|
$ct->mklbtype($self->comment, "lbtype:$lbtype\@$dvob")->system; |
1281
|
|
|
|
|
|
|
} elsif (!$self->inclb) { |
1282
|
0
|
|
|
|
|
|
$locked = $ct->lslock(['-s'], "lbtype:$lbtype\@$dvob")->qx; |
1283
|
0
|
0
|
|
|
|
|
$ct->unlock("lbtype:$lbtype\@$dvob")->system if $locked; |
1284
|
|
|
|
|
|
|
} |
1285
|
|
|
|
|
|
|
# Allow for labelling errors, in case of hard links: only the link |
1286
|
|
|
|
|
|
|
# recorded can be labelled, the other being seen as 'removed' |
1287
|
0
|
0
|
0
|
|
|
|
if ($self->label_mods || $self->inclb) { |
1288
|
0
|
|
|
|
|
|
my @mods = $self->_lsco; |
1289
|
0
|
0
|
|
|
|
|
push @mods, @{$self->{ST_LBL}} if $self->{ST_LBL}; |
|
0
|
|
|
|
|
|
|
1290
|
0
|
0
|
|
|
|
|
if (@mods) { |
1291
|
0
|
0
|
|
|
|
|
$ctbool->mklabel([qw(-nc -rep), $self->inclb], @mods)->system |
1292
|
|
|
|
|
|
|
if $self->inclb; |
1293
|
0
|
|
|
|
|
|
$ctbool->mklabel([qw(-nc -rep), $lbtype], @mods)->system; |
1294
|
|
|
|
|
|
|
} |
1295
|
|
|
|
|
|
|
} else { |
1296
|
0
|
|
|
|
|
|
my $lbl = $self->lblver; |
1297
|
0
|
0
|
|
|
|
|
if ($lbl) { |
1298
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autochomp=>1, autofail=>0, stderr=>0}); |
1299
|
0
|
0
|
0
|
|
|
|
my @rv = grep{ s/^(.*?)(?:@@(.*))/$1/ && |
|
0
|
|
|
|
|
|
|
1300
|
|
|
|
|
|
|
($2 =~ /CHECKEDOUT$/ || !-r "$_\@\@/$lbl") } |
1301
|
|
|
|
|
|
|
$ct->ls([qw(-r -vob -s)], $dbase)->qx, |
1302
|
|
|
|
|
|
|
$ct->ls([qw(-d -vob -s)], $dbase)->qx; |
1303
|
0
|
|
|
|
|
|
$ctbool->mklabel([qw(-nc -rep), $lbtype], $dbase, @rv)->system; |
1304
|
|
|
|
|
|
|
} else { |
1305
|
0
|
|
|
|
|
|
$ctbool->mklabel([qw(-nc -rep -rec), $lbtype], $dbase)->system; |
1306
|
|
|
|
|
|
|
} |
1307
|
|
|
|
|
|
|
# Possibly move the label back to the right versions |
1308
|
0
|
0
|
|
|
|
|
$ctbool->mklabel([qw(-nc -rep), $lbtype], @{$self->{ST_LBL}})->system |
|
0
|
|
|
|
|
|
|
1309
|
|
|
|
|
|
|
if $self->{ST_LBL}; |
1310
|
|
|
|
|
|
|
# Last, label the ancestors of the destination back to the vob tag. |
1311
|
0
|
|
|
|
|
|
my($dad, @ancestors); |
1312
|
0
|
|
|
|
|
|
my $min = length($self->normalize($dvob)); |
1313
|
0
|
|
|
|
|
|
for ($dad = dirname($dbase); |
1314
|
|
|
|
|
|
|
length($dad) >= $min; $dad = dirname($dad)) { |
1315
|
0
|
|
|
|
|
|
push(@ancestors, $dad); |
1316
|
|
|
|
|
|
|
} |
1317
|
0
|
0
|
|
|
|
|
$ctq->mklabel([qw(-rep -nc), $lbtype], @ancestors)->system |
1318
|
|
|
|
|
|
|
if @ancestors; |
1319
|
|
|
|
|
|
|
} |
1320
|
0
|
0
|
|
|
|
|
$self->clone_ct->lock("lbtype:$lbtype\@$dbase")->system if $locked; |
1321
|
|
|
|
|
|
|
} |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
sub get_addhash { |
1324
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1325
|
0
|
0
|
|
|
|
|
if ($self->{ST_ADD}) { |
1326
|
|
|
|
|
|
|
return |
1327
|
0
|
|
|
|
|
|
map { $self->{ST_ADD}->{$_}->{src}, $self->{ST_ADD}->{$_}->{dst} } |
|
0
|
|
|
|
|
|
|
1328
|
0
|
|
|
|
|
|
keys %{$self->{ST_ADD}}; |
1329
|
|
|
|
|
|
|
} else { |
1330
|
0
|
|
|
|
|
|
return (); |
1331
|
|
|
|
|
|
|
} |
1332
|
|
|
|
|
|
|
} |
1333
|
|
|
|
|
|
|
|
1334
|
|
|
|
|
|
|
sub get_modhash { |
1335
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1336
|
0
|
0
|
|
|
|
|
if ($self->{ST_MOD}) { |
1337
|
|
|
|
|
|
|
return |
1338
|
0
|
|
|
|
|
|
map { $self->{ST_MOD}->{$_}->{src}, $self->{ST_MOD}->{$_}->{dst} } |
|
0
|
|
|
|
|
|
|
1339
|
0
|
|
|
|
|
|
keys %{$self->{ST_MOD}}; |
1340
|
|
|
|
|
|
|
} else { |
1341
|
0
|
|
|
|
|
|
return (); |
1342
|
|
|
|
|
|
|
} |
1343
|
|
|
|
|
|
|
} |
1344
|
|
|
|
|
|
|
|
1345
|
|
|
|
|
|
|
sub get_sublist { |
1346
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1347
|
0
|
0
|
|
|
|
|
if ($self->{ST_SUB}) { |
1348
|
0
|
|
|
|
|
|
return sort keys %{$self->{ST_SUB}->{exfiles}}; |
|
0
|
|
|
|
|
|
|
1349
|
|
|
|
|
|
|
} else { |
1350
|
0
|
|
|
|
|
|
return (); |
1351
|
|
|
|
|
|
|
} |
1352
|
|
|
|
|
|
|
} |
1353
|
|
|
|
|
|
|
|
1354
|
|
|
|
|
|
|
sub checkin { |
1355
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1356
|
0
|
|
|
|
|
|
my $mbase = $self->_mkbase; |
1357
|
0
|
|
|
|
|
|
my $dad = dirname($mbase); |
1358
|
0
|
0
|
|
|
|
|
my @ptime = qw(-pti) unless $self->ctime; |
1359
|
0
|
|
|
|
|
|
my @cmnt = @{$self->comment}; |
|
0
|
|
|
|
|
|
|
1360
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autochomp=>0}); |
1361
|
|
|
|
|
|
|
# If special eltypes are registered, chtype them here. |
1362
|
0
|
0
|
|
|
|
|
if (my %emap = $self->eltypemap) { |
1363
|
0
|
|
|
|
|
|
for my $re (keys %emap) { |
1364
|
0
|
|
|
|
|
|
my @chtypes = grep {/$re/} map {$self->{ST_ADD}->{$_}->{dst}} |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1365
|
0
|
|
|
|
|
|
keys %{$self->{ST_ADD}}; |
1366
|
0
|
0
|
|
|
|
|
next unless @chtypes; |
1367
|
0
|
|
|
|
|
|
$ct->chtype([@cmnt, '-f', $emap{$re}], @chtypes)->system; |
1368
|
|
|
|
|
|
|
} |
1369
|
|
|
|
|
|
|
} |
1370
|
|
|
|
|
|
|
# Do one-by-one ci's with -from (to preserve CR's) unless |
1371
|
|
|
|
|
|
|
# otherwise requested. |
1372
|
0
|
0
|
|
|
|
|
if (! $self->no_cr) { |
1373
|
0
|
|
|
|
|
|
for (keys %{$self->{ST_CI_FROM}}) { |
|
0
|
|
|
|
|
|
|
1374
|
0
|
|
|
|
|
|
my $src = $self->{ST_CI_FROM}->{$_}->{src}; |
1375
|
0
|
|
|
|
|
|
my $dst = $self->{ST_CI_FROM}->{$_}->{dst}; |
1376
|
0
|
|
|
|
|
|
$ct->ci([@ptime, @cmnt, qw(-ide -rm -from), $src], $dst)->system; |
1377
|
|
|
|
|
|
|
} |
1378
|
0
|
|
|
|
|
|
delete @{$self->{ST_MOD}}{keys %{$self->{ST_CI_FROM}}}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
} |
1380
|
|
|
|
|
|
|
# Check-in first the files modified under the recorded names, |
1381
|
|
|
|
|
|
|
# in case of hardlinks, since checking the other link first |
1382
|
|
|
|
|
|
|
# in a pair would fail. |
1383
|
0
|
|
|
|
|
|
my @mods; |
1384
|
0
|
|
|
|
|
|
push @mods, $self->{ST_MOD}->{$_}->{dst} for |
|
0
|
|
|
|
|
|
|
1385
|
0
|
|
|
|
|
|
grep {!ccsymlink($self->{ST_MOD}->{$_}->{dst})} keys %{$self->{ST_MOD}}; |
1386
|
0
|
0
|
|
|
|
|
$ct->ci([@cmnt, '-ide', @ptime], sort @mods)->system if @mods; |
1387
|
|
|
|
|
|
|
# Check in anything not handled above. |
1388
|
0
|
|
|
|
|
|
my %checkedout = map {$_ => 1} $self->_lsco; |
|
0
|
|
|
|
|
|
|
1389
|
0
|
|
|
|
|
|
my @todo = grep {m%^\Q$mbase%} keys %checkedout; |
|
0
|
|
|
|
|
|
|
1390
|
0
|
0
|
|
|
|
|
@todo = grep {!exists($self->{ST_PRE}->{$_})} @todo if $self->ignore_co; |
|
0
|
|
|
|
|
|
|
1391
|
0
|
0
|
|
|
|
|
unshift(@todo, $dad) if $checkedout{$dad}; |
1392
|
|
|
|
|
|
|
# Sort reverse in case the checked in versions are not selected by the view |
1393
|
0
|
0
|
|
|
|
|
$ct->argv('ci', [@cmnt, '-ide', @ptime], sort {$b cmp $a} @todo)->system |
|
0
|
|
|
|
|
|
|
1394
|
|
|
|
|
|
|
if @todo; |
1395
|
|
|
|
|
|
|
# Fix the protections of the target files if requested. Unix files |
1396
|
|
|
|
|
|
|
# get careful consideration of bitmasks etc; Windows files just get |
1397
|
|
|
|
|
|
|
# promoted to a+x if their extension looks executable. |
1398
|
0
|
0
|
|
|
|
|
if ($self->protect) { |
1399
|
0
|
|
|
|
|
|
if (MSWIN) { |
1400
|
|
|
|
|
|
|
my @exes; |
1401
|
|
|
|
|
|
|
for (keys %{$self->{ST_ADD}}) { |
1402
|
|
|
|
|
|
|
next unless m%\.(bat|cmd|exe|dll|com|cgi|.?sh|pl)$%i; |
1403
|
|
|
|
|
|
|
push(@exes, $self->{ST_ADD}->{$_}->{dst}); |
1404
|
|
|
|
|
|
|
} |
1405
|
|
|
|
|
|
|
$ct->argv('protect', [qw(-chmod a+x)], @exes)->system if @exes; |
1406
|
|
|
|
|
|
|
} else { |
1407
|
0
|
|
|
|
|
|
my %perms; |
1408
|
0
|
|
|
|
|
|
for (keys %{$self->{ST_ADD}}) { |
|
0
|
|
|
|
|
|
|
1409
|
0
|
|
|
|
|
|
my $src = $self->{ST_ADD}->{$_}->{src}; |
1410
|
0
|
|
|
|
|
|
my $dst = $self->{ST_ADD}->{$_}->{dst}; |
1411
|
0
|
|
|
|
|
|
my $src_mode = (stat $src)[2]; |
1412
|
0
|
|
|
|
|
|
my $dst_mode = (stat $dst)[2]; |
1413
|
|
|
|
|
|
|
# 07551 represents the only bits that matter to clearcase |
1414
|
0
|
0
|
0
|
|
|
|
if (($src_mode & 07551) ne ($dst_mode & 07551) && |
1415
|
|
|
|
|
|
|
$src !~ m%\.(?:p|html?|gif|mak|rc|ini|java| |
1416
|
|
|
|
|
|
|
c|cpp|cxx|h|bmp|ico)$|akefile%x) { |
1417
|
0
|
|
|
|
|
|
my $sym = sprintf("%o", ($src_mode & 07775) | 0444); |
1418
|
0
|
|
|
|
|
|
push(@${$perms{$sym}}, $dst); |
|
0
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
} |
1420
|
|
|
|
|
|
|
} |
1421
|
0
|
|
|
|
|
|
for (keys %{$self->{ST_MOD}}) { |
|
0
|
|
|
|
|
|
|
1422
|
0
|
|
|
|
|
|
my $src = $self->{ST_MOD}->{$_}->{src}; |
1423
|
0
|
|
|
|
|
|
my $dst = $self->{ST_MOD}->{$_}->{dst}; |
1424
|
0
|
|
|
|
|
|
my $src_mode = (stat $src)[2]; |
1425
|
0
|
|
|
|
|
|
my $dst_mode = (stat $dst)[2]; |
1426
|
|
|
|
|
|
|
# 07551 represents the only bits that matter to clearcase |
1427
|
0
|
0
|
0
|
|
|
|
if (($src_mode & 07551) ne ($dst_mode & 07551) && |
1428
|
|
|
|
|
|
|
$src !~ m%\.(?:p|html?|gif|mak|rc|ini|java| |
1429
|
|
|
|
|
|
|
c|cpp|cxx|h|bmp|ico)$|akefile%x) { |
1430
|
0
|
|
|
|
|
|
my $sym = sprintf("%o", ($src_mode & 07775) | 0444); |
1431
|
0
|
|
|
|
|
|
push(@${$perms{$sym}}, $dst); |
|
0
|
|
|
|
|
|
|
1432
|
|
|
|
|
|
|
} |
1433
|
|
|
|
|
|
|
} |
1434
|
0
|
|
|
|
|
|
for (keys %perms) { |
1435
|
0
|
|
|
|
|
|
$ct->argv('protect', ['-chmod', $_], @${$perms{$_}})->system; |
|
0
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
} |
1437
|
|
|
|
|
|
|
} |
1438
|
|
|
|
|
|
|
} |
1439
|
|
|
|
|
|
|
} |
1440
|
|
|
|
|
|
|
|
1441
|
|
|
|
|
|
|
sub cleanup { |
1442
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1443
|
0
|
|
|
|
|
|
my $mbase = $self->_mkbase; |
1444
|
0
|
|
|
|
|
|
my $dad = dirname($mbase); |
1445
|
0
|
|
|
|
|
|
my $ct = $self->clone_ct({autofail=>0}); |
1446
|
0
|
|
|
|
|
|
my @vp = $self->_lsprivate(1); |
1447
|
0
|
|
|
|
|
|
for (sort {$b cmp $a} @vp) { |
|
0
|
|
|
|
|
|
|
1448
|
0
|
0
|
|
|
|
|
if (-d $_) { |
1449
|
0
|
0
|
|
|
|
|
rmdir $_ || warn "$0: Error: unable to remove $_\n"; |
1450
|
|
|
|
|
|
|
} else { |
1451
|
0
|
|
0
|
|
|
|
unlink $_ || warn "$0: Error: unable to remove $_\n"; |
1452
|
|
|
|
|
|
|
} |
1453
|
|
|
|
|
|
|
} |
1454
|
0
|
|
|
|
|
|
my %checkedout = map {$_ => 1} $self->_lsco; |
|
0
|
|
|
|
|
|
|
1455
|
0
|
|
|
|
|
|
my @todo = grep {m%^\Q$mbase%} keys %checkedout; |
|
0
|
|
|
|
|
|
|
1456
|
0
|
0
|
0
|
|
|
|
@todo = grep {!exists($self->{ST_PRE}->{$_})} @todo |
|
0
|
|
|
|
|
|
|
1457
|
|
|
|
|
|
|
if $self->ignore_co || $self->overwrite_co; |
1458
|
0
|
0
|
|
|
|
|
unshift(@todo, $dad) if $checkedout{$dad}; |
1459
|
0
|
0
|
|
|
|
|
if ($self->{branchoffroot}) { |
1460
|
0
|
|
|
|
|
|
for (sort {$b cmp $a} @todo) { |
|
0
|
|
|
|
|
|
|
1461
|
0
|
|
|
|
|
|
my $b = $ct->ls([qw(-s -d)], $_)->qx; |
1462
|
0
|
|
|
|
|
|
$ct->unco([qw(-rm)], $_)->system; |
1463
|
0
|
0
|
|
|
|
|
if ($b =~ s%^(.*)[\\/]CHECKEDOUT$%$1%) { |
1464
|
0
|
0
|
|
|
|
|
opendir BR, $b or next; |
1465
|
0
|
|
|
|
|
|
my @f = grep !/^(\.\.?|0|LATEST)$/, readdir BR; |
1466
|
0
|
|
|
|
|
|
closedir BR; |
1467
|
0
|
0
|
|
|
|
|
$ct->rmbranch([qw(-f)], $b)->system unless @f; |
1468
|
|
|
|
|
|
|
} |
1469
|
|
|
|
|
|
|
} |
1470
|
|
|
|
|
|
|
} else { |
1471
|
0
|
0
|
|
|
|
|
$ct->unco([qw(-rm)], sort {$b cmp $a} @todo)->system if @todo; |
|
0
|
|
|
|
|
|
|
1472
|
|
|
|
|
|
|
} |
1473
|
|
|
|
|
|
|
} |
1474
|
|
|
|
|
|
|
|
1475
|
|
|
|
|
|
|
# Undo current work and exit. May be called from an exception handler. |
1476
|
|
|
|
|
|
|
sub fail { |
1477
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
1478
|
0
|
|
|
|
|
|
my $rc = shift; |
1479
|
0
|
|
|
|
|
|
$self->ct->autofail(0); # avoid exception-handler loop |
1480
|
0
|
|
|
|
|
|
$self->cleanup; |
1481
|
0
|
0
|
|
|
|
|
exit(defined($rc) ? $rc : 2); |
1482
|
|
|
|
|
|
|
} |
1483
|
|
|
|
|
|
|
|
1484
|
|
|
|
|
|
|
sub failm { |
1485
|
0
|
|
|
0
|
0
|
|
my ($self, $msg, $rc) = @_; |
1486
|
0
|
|
|
|
|
|
warn "$0: Error: $msg\n"; |
1487
|
0
|
|
|
|
|
|
$self->fail($rc); |
1488
|
|
|
|
|
|
|
} |
1489
|
|
|
|
|
|
|
|
1490
|
|
|
|
|
|
|
sub version { |
1491
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1492
|
0
|
|
|
|
|
|
return $ClearCase::SyncTree::VERSION; |
1493
|
|
|
|
|
|
|
} |
1494
|
|
|
|
|
|
|
|
1495
|
|
|
|
|
|
|
# Here 'ecs' means Exists Case Sensitive. We don't generally |
1496
|
|
|
|
|
|
|
# want the case-insensitive file test operators on Windows. |
1497
|
|
|
|
|
|
|
# The underlying problem is that cleartool is always case |
1498
|
|
|
|
|
|
|
# sensitive. I.e. you can mkelem 'Foo' and then open 'foo' |
1499
|
|
|
|
|
|
|
# if you have the right MVFS settings, but you cannot check |
1500
|
|
|
|
|
|
|
# out or describe 'foo', only 'Foo'. |
1501
|
|
|
|
|
|
|
# This could lead to other problems on Windows though, since you |
1502
|
|
|
|
|
|
|
# may create evil twins if you subtract an old name and |
1503
|
|
|
|
|
|
|
# then add it under a name which differs only by case. But at |
1504
|
|
|
|
|
|
|
# least that does work, whereas trying to checkout a path |
1505
|
|
|
|
|
|
|
# with the wrong case does not work at all. Let the evil twin |
1506
|
|
|
|
|
|
|
# trigger handle the evil twin scenario. |
1507
|
|
|
|
|
|
|
sub ecs { |
1508
|
0
|
|
|
0
|
0
|
|
my $file = shift; |
1509
|
0
|
|
|
|
|
|
my $rc = 0; |
1510
|
0
|
|
|
|
|
|
if (MSWIN || CYGWIN) { |
1511
|
|
|
|
|
|
|
if (opendir DIR, dirname($file)) { |
1512
|
|
|
|
|
|
|
my $match = basename($file); |
1513
|
|
|
|
|
|
|
# Faster than for/last when not found! |
1514
|
|
|
|
|
|
|
$rc = 1 if grep {$_ eq $match} readdir DIR; |
1515
|
|
|
|
|
|
|
closedir DIR; |
1516
|
|
|
|
|
|
|
} |
1517
|
|
|
|
|
|
|
} else { |
1518
|
0
|
|
|
|
|
|
$rc = -e $file; |
1519
|
|
|
|
|
|
|
} |
1520
|
0
|
|
|
|
|
|
return $rc; |
1521
|
|
|
|
|
|
|
} |
1522
|
|
|
|
|
|
|
|
1523
|
|
|
|
|
|
|
1; |
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
__END__ |