| 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__ |