5 =head1 NAME $RCSfile: etf.pl,v $
15 Andrew DeFaria <Andrew@ClearSCM.com>
23 Fri Apr 23 09:40:31 PDT 2010
27 $Date: 2011/01/09 01:00:28 $
33 Usage: eft.pl [-u|sage] [-ve|rbose] [-d|ebug] [-di|rectory <dir>]
37 -u|sage: Displays usage
39 -d|ebug: Output debug messages
41 -dir Directory to process
45 This script will search for existing evil twins in a Clearcase vob. It is
46 intended to be used in the context of a base Clearcase view with a default
49 An evil twin is defined as two or more Clearcase elements that share the same
50 path, and same name, but have different OIDs thus having different version
51 histories. This can occur when a user creates an element in a directory that
52 used to exist in this same directory on another branch or on a previous version
53 of the same branch. By default Clearcase will create an element with a new OID.
54 This new, evil twin will then develop it's own version history. This then
55 becomes a problem when you attempt to merge branches - which twin (OID) should
56 Clearcase keep track of?
58 Most Clearcase users implement an evil twin trigger to prevent the creation of
59 evil twins but sometimes evil twins have already been created. This script helps
60 identify these already existing evil twins.
62 Note: Evil twins can also happen if you only apply your evil twin trigger to the
63 mkelem Clearcase action. It should be applied to the lnname action as elements
64 come into creation by things like the cleartool ln, mv and mkdir commands. These
65 all eventually do an lnname so that's where you should put your evil twin
70 TODO: Is cleartool find really needed? I mean since we are going through
71 the extended version namespace don't we by default find all
74 This script will use cleartool find to process all directory elements from
75 $startingDir (Default '.'). For each version of the directory a hash will be
76 built up containing all of the element names in that directory version.
77 Elements are always added and never deleted in this hash as we are looking for
78 all elements that have ever existed in the directory at any point in time.
80 This script then dives into the view extended namespace for directory elements
81 examining the internal Clearcase structures. If we find a branch we recurse or
82 numbered directory version we recurse looking for file elements (TODO: What
83 about directory evil twins?). Note that we skip version 0 as version 0 is never
84 interesting - it is always a duplicate of what it branched from and empty.
86 Directory versions that are not numbered are labels or baselines that point to
87 numbered directory versions so we don't need to look at them again.
89 For each file element we find we use the cleartool dump command to get the OID
90 of this particiular versioned element and build up an array of hashes of all the
91 elements in the directory. For each element version we maintain a hash keyed by
92 the OID. The structure also contains a count of the number of times the OID was
93 found. An evil twin therefore will have multiple OIDs for the same element
96 After the directory is processed we look though the array of hashes for elements
97 that have multiple OIDs and report them. Then we proceed to the next directory.
109 use lib "$FindBin::Bin/../lib";
112 use Clearcase::Element;
120 my (%total, %dirInfo, $log, $startTime);
124 =head2 reportDir (%directoryInfo)
126 Report any evil twins found in %directoryInfo
130 =for html <blockquote>
136 Structure representing the OIDs of element in a direcotry
140 =for html </blockquote>
144 =for html <blockquote>
152 =for html </blockquote>
157 my (%directoryInfo) = @_;
161 foreach my $filename (sort keys %directoryInfo) {
162 my @oids = @{$directoryInfo{$filename}};
164 if (scalar @oids > 1) {
167 $log->msg ("File: $filename");
170 $log->msg ("\tOID: $$_{OID} ($$_{count})");
171 $log->msg ("\tFirst detected \@: $$_{version}");
181 =head2 proceedDir $dirName
183 Build up a data structure for $dirName looking for evil twins
187 =for html <blockquote>
197 =for html </blockquote>
201 =for html <blockquote>
207 Directory info hash keyed by element name whose value is an array of oidInfo
208 hashes containing a unique OID and a count of how many occurences of that OID
209 exist for that element.
213 =for html </blockquote>
221 opendir my $dir, $dirName
222 or $log->err ("Unable to open directory $dirName - $!", 1);
224 my @dirVersions = grep {!/^\./} readdir $dir;
228 my ($directory, $version) = split /$Clearcase::SFX/, $dirName;
230 $directory = basename (cwd)
231 if $directory eq '.';
233 my $displayName = "$directory$Clearcase::SFX$version";
235 # We only want to deal with branches and numbered versions. Non-numbered
236 # versions which are not branches represent labels and baselines which are
237 # just aliases for directory and file elements. Branches represent recursion
238 # points and numbered versions represent unique directory versions.
241 foreach (@dirVersions) {
242 my ($status, @output) = $Clearcase::CC->execute (
243 "describe -fmt %m $dirName/$_"
245 my $objkind = $output[0];
247 if ($objkind =~ / element/) {
249 } elsif (/^\d/ or $objkind eq 'branch') {
250 # Skip 0 element - it's never interesting.
253 # Recurse for branches and numbered directory versions
254 if ($objkind eq 'branch') {
257 $total{'directory versions'}++;
262 #$log->log ("Recurse:\t$displayName/$_");
264 %dirInfo = processDir "$dirName/$_";
270 foreach (@elements) {
271 $total{'element versions'}++;
273 #$log->log ("Element:\t$displayName/$_");
275 # Get oid using the helper function
276 my $oid = Clearcase::Element::oid "$dirName/$_";
281 # Search our %dirInfo for a version matching $version
282 foreach (@{$dirInfo{$_}}) {
283 # Increment count if we find a matching oid
284 if ($$_{OID} eq $oid) {
292 # If we didn't find a match then make a new %objInfo starting with a
293 # count of 1. Also save this current $version, which is the first
294 # instance of this new oid.
295 push @{$dirInfo{$_}}, {
298 version => "$dirName/$_",
305 version => "$dirName/$_",
315 =head2 proceedDirs $startingDir
317 Process all directories under $startingDir
322 =for html <blockquote>
328 Directory to start processing
332 =for html </blockquote>
336 =for html <blockquote>
342 Total number of evil twins found
346 =for html </blockquote>
350 sub processDirs ($) {
351 my ($startingDir) = @_;
353 my $cmd = "cleartool find \"$startingDir\" -type d -print";
355 open my $dirs, '-|', $cmd
356 or $log->err ("Unable to execute $cmd - $!", 1);
359 chomp; chop if /\r$/;
361 my $displayName = $_;
363 $displayName =~ s/\@\@$//;
365 if ($displayName eq '.') {
366 $displayName = basename (cwd);
369 $log->msg ("Processing $displayName");
371 my $startingTime = time;
372 my %directoryInfo = processDir $_;
376 display_duration $startingTime, $log;
378 $total{'evil twins'} += reportDir %dirInfo;
382 or $log->err ("Unable to close $cmd - $!");
384 return $total{'evil twins'};
390 my $startingDir = '.';
393 usage => sub { Usage },
394 verbose => sub { set_verbose },
395 debug => sub { set_debug },
396 'directory=s' => \$startingDir,
397 ) or Usage 'Invalid parameter';
403 $log->msg ("Evil Twin Finder $FindBin::Script v$VERSION");
405 processDirs $startingDir;
409 $log->msg ("$FindBin::Script finished @ " . localtime);
411 display_duration $startTime, $log;
415 =head1 CONFIGURATION AND ENVIRONMENT
417 DEBUG: If set then $debug is set to this level.
419 VERBOSE: If set then $verbose is set to this level.
421 TRACE: If set then $trace is set to this level.
429 L<File::Basename|File::Basename>
433 L<Getopt::Long|Getopt::Long>
435 =head2 ClearSCM Perl Modules
451 <a href="http://clearscm.com/php/cvs_man.php?file=lib/Clearcase.pm">Clearcase</a><br>
452 <a href="http://clearscm.com/php/cvs_man.php?file=lib/Clearcase/Element.pm">Element</a><br>
453 <a href="http://clearscm.com/php/cvs_man.php?file=lib/Display.pm">Display</a><br>
454 <a href="http://clearscm.com/php/cvs_man.php?file=lib/Logger.pm">Logger</a><br>
455 <a href="http://clearscm.com/php/cvs_man.php?file=lib/TimeUtils.pm">TimeUtils</a><br>
456 <a href="http://clearscm.com/php/cvs_man.php?file=lib/Utils.pm">Utils</a><br>
461 =head1 BUGS AND LIMITATIONS
463 There are no known bugs in this script
465 Please report problems to Andrew DeFaria <Andrew@ClearSCM.com>.
467 =head1 LICENSE AND COPYRIGHT
469 Copyright (c) 2010, ClearSCM, Inc. All rights reserved.