(file) Return to UpdateTopics.pm CVS log (file) (dir) Up to [RizwankCVS] / geekymedia_web / twiki / lib / TWiki / Upgrade

  1 rizwank 1.1 # Support functionality for the TWiki Collaboration Platform, http://TWiki.org/
  2             #
  3             #
  4             # Jul 2004 - copied almost completely from Sven's updateTopics.pl script:
  5             #            put in a package and made a subroutine to work with UpgradeTWiki 
  6             #             by Martin "GreenAsJade" Gregory.
  7             
  8             # This version ignores symlinks in the existing wiki data: it creates a new 
  9             # wiki data tree updating the topics in the existing wiki data, even the
 10             # linked-in topics, but does not create new links like the old ones.
 11             
 12             package UpdateTopics;
 13             
 14             use strict;
 15             
 16             use File::Find;
 17             use File::Copy;
 18             use File::Basename;
 19             use Text::Diff;
 20             
 21             # Try to upgrade an installation's TWikiTopics using the rcs info in it.
 22 rizwank 1.1 
 23             use vars qw($CurrentDataDir $NewReleaseDataDir $DestinationDataDir $BaseDir $debug @DefaultWebTopics %LinkedDirPathsInWiki $RcsLogFile $TempDir);
 24             
 25             sub UpdateTopics 
 26             {
 27                 $CurrentDataDir = shift or die "UpdateTopics not provided with existing data directory!\n";
 28             
 29                 $NewReleaseDataDir = shift or die "UpdateTopics not provided with new data directory!\n";
 30             
 31                 $DestinationDataDir = (shift or "$BaseDir/newData");
 32             
 33                 my $whoCares = `which rcsdiff`;   # we should use File::Which to do this, except that would mean
 34                                                   # getting yet another .pm into lib, which seems like hard work?
 35                 ($? >> 8 == 0) or die "Uh-oh - couldn't see an rcs executable on your path!  I really need one of those!\n";
 36             
 37                 $whoCares = `which patch`;
 38             
 39                 ($? >> 8 == 0) or die "Uh-oh - couldn't see a patch executable on your path!  I really need one of those!\n";
 40             
 41                 $BaseDir = `pwd`;
 42                 chomp ($BaseDir);
 43 rizwank 1.1 
 44                 $TempDir = "$BaseDir/tmp";
 45             
 46                 while (-d $TempDir ) { $TempDir .= 'p' }   # we want our own previously non-existing directory!
 47             
 48                 mkdir $TempDir or die "Uhoh - couldn't make a temporary directory called $TempDir: $!\n";
 49             
 50             #Set if you want to see the debug output
 51             #$debug = "yes";
 52             
 53                 if ($debug) {print "$CurrentDataDir, $NewReleaseDataDir\n"; }
 54             
 55                 if ((! -d $CurrentDataDir ) || (! -d $NewReleaseDataDir)) {
 56             	print "\nUsage: UpdateTopics <CurrentDataDir> <NewReleaseDataDir> [<DestinationDataDir]>\n";
 57             	exit;
 58                 }
 59             
 60                 print "\n";
 61                 print "\t...new upgraded data will be put in $DestinationDataDir\n";
 62                 print "\t   there will be no changes made to either the source data directory or $NewReleaseDataDir.\n\n"; 
 63                 print "\t This progam will attempt to use the rcs versioning information to upgrade the\n";
 64 rizwank 1.1     print "\t   contents of your distributed topics in $CurrentDataDir to the content in $NewReleaseDataDir.\n\n";
 65                 print "Output:\n";
 66                 print "\tFor each file that has no versioning information in your existing twiki a _v_ will be printed\n";
 67                 print "\tFor each file that has no changes from the previous release a _c_ will be printed\n";
 68                 print "\tFor each file that has no changes made in your existing release, a _u_ will be printed\n";
 69                 print "\tFor each file where no commonality could be found, your existing one is used, and a _C_ will be printed\n";
 70                 print "\tFor each file that has changes and a patch is generated a _p_ will be printed\n";
 71                 print "\tFor each file that is new in the NewReleaseDataDir a _+_ will be printed\n";
 72                 print "\t When the script has attempted to patch the $NewReleaseDataDir, 
 73             \t *.rej files will contain the failed merges\n";
 74                 print "\t although many of these rejected chages will be discardable, 
 75             \t please check them to see if your configuration is still ok\n\n";
 76             
 77                 sussoutDefaultWebTopics();
 78             
 79                 mkdir $DestinationDataDir;
 80             
 81             #redirect stderr into a file (rcs dumps out heaps of info)
 82             
 83                 $RcsLogFile = $BaseDir."/rcs.log";
 84             
 85 rizwank 1.1     unlink($RcsLogFile);  # let's have just the messages from this session!
 86             
 87                 open(PATCH, "> $DestinationDataDir/patchTopics");
 88                 
 89                 print "\n\n ...checking existing files from $CurrentDataDir\n";
 90             #TODO: need to find a way to detect non-Web directories so we don't make a mess of them..
 91             # (should i just ignore Dirs without any ,v files?) - i can't upgrade tehm anyway..
 92             #upgrade templates..?
 93             
 94                 my %findOptions;
 95             
 96                 $findOptions{'wanted'} = \&getRLog;
 97                 $findOptions{'follow_fast'} = 1;   # surely the user hasn't put loops of links etc in their data!?
 98             
 99                 find(\%findOptions, $CurrentDataDir);
100             
101                 close(PATCH);
102             
103             #do a find through $NewReleaseDataDir and copy all missing files & dirs
104                 print "\n\n ... checking for new files in $NewReleaseDataDir";
105                 find(\&copyNewTopics, $NewReleaseDataDir);
106 rizwank 1.1     
107             #run `patch patchTopics` in $DestinationDataDir
108                 print "\nPatching topics (manually check the rejected patch (.rej) files)";
109                 chdir($DestinationDataDir);
110                 `patch -p0 < patchTopics > patch.log`;
111             #TODO: examing the .rej files to remove the ones that have already been applied
112                 find(\&listRejects, ".");
113             #TODO: run `ci` in $DestinationDataDir
114                 
115                 print "\n\n";
116                 
117                 rmdir($TempDir);
118                 chdir($BaseDir);  # seems the kind thing to do :-)
119             }
120                 
121             # ============================================
122             sub listRejects
123             {
124                 my ( $filename ) = @_;
125                 
126                 $filename = $File::Find::name;
127 rizwank 1.1 
128                 if ($filename =~ /.rej$/ ) {
129                     print "\nPatch rejected: $filename";
130                 }
131             }
132             
133             # ============================================
134             sub copyNewTopics
135             {
136                 my ( $filename ) = $File::Find::name;
137             
138                 my $destinationFilename = $filename;
139                 $destinationFilename =~ s/$NewReleaseDataDir/$DestinationDataDir/g;
140             
141             # Sven had these commeted out, so I've left them here commented out.
142             #    return if $filename =~ /,v$/;
143             #    return if $filename =~ /.lock$/;
144             #    return if $filename =~ /~$/;
145             
146                 if ( -d $filename) {
147                     print "\nprocessing directory $filename";
148 rizwank 1.1 	if ( !-d $destinationFilename ) {
149             	    print " (creating $destinationFilename)";
150             	    mkdir($destinationFilename);
151             	}
152             	print "\n";
153                     return;
154                 }
155                 
156                 if (! -e $destinationFilename ) { 
157                     print "\nadding $filename (new in this release)" if ($debug);
158                     print "+" if (!$debug);
159                     copy( $filename, $destinationFilename);
160                 }
161                 
162             }
163             
164             # ============================================
165             sub getRLog
166             {
167                 my ( $filename ) = $File::Find::name;
168             
169 rizwank 1.1 # (see above)
170             #    my ( $filename ) = @_;
171             #    $filename = $BaseDir."/".$File::Find::name if (! $filename );
172             
173                 my ( $newFilename ) = $filename;
174                 if (!$filename =~ /^$CurrentDataDir/)
175                 {
176             	die "getRLog found $filename that appears not to be in $CurrentDataDir tree! That's not supposed to happen: sorry!\n";
177                 }
178             	
179                 $newFilename =~ s/$CurrentDataDir/$NewReleaseDataDir/g;
180                 print "\n$filename -> $newFilename : "  if ( $debug);
181             
182                 my $destinationFilename = $filename;
183                 $destinationFilename =~ s/$CurrentDataDir/$DestinationDataDir/g;
184             
185                 if ($filename =~ /,v$/ or $filename =~ /.lock$/ or $filename =~ /~$/) {
186             	print "skipping\n" if $debug;
187             	return;
188                 }
189             
190 rizwank 1.1     if ( -d $filename ) {
191             	print "\nprocessing directory (creating $destinationFilename)\n";
192                     mkdir($destinationFilename);
193                     return;
194                 }
195                 
196             #    if ( isFromDefaultWeb($filename) )
197             #    {
198             #        $newFilename =~ s|^(.*)/[^/]*/([^/]*)|$1/_default/$2|g;
199             #        print "\n$filename appears to have been generated from from _default - merging with $newFilename from the new distribution" if ($debug);
200             #    }
201                 
202                 if (! -e $filename.",v" )
203                 {
204                     if ( $filename =~ /.txt$/ ) {
205             	    # here we defer making an RCS file for this file to someone else :-)
206             	    # (probably the process that checks all the new wiki files back in)
207                         print "\nWarning: $filename does not have any rcs information" if ($debug);
208                         print "v" if (! $debug);
209                     }
210                     copy( $filename, $destinationFilename);
211 rizwank 1.1         return;
212                 }
213             
214                 # make it easy for debugging to turn on or off the business about
215                 # checking in the existing files before rcsdiffing them
216                 # this is necessary, because you have to make changes in two places to make
217                 # this switch, and if you forget the second one you're gunna delete lots of files
218                 # you wanted to keep!
219             
220                 my $doCiCo = 1;
221             #   my $doCiCo = 0;
222             
223                 # Now - the main business: if we're looking at a file that has a new version in
224                 # the new distribution then we have to try merging etc...
225             
226                 if ( -e $newFilename ) { 
227                     # file that may need upgrading
228             
229             	my $workingFilename;
230             
231             	if (!$doCiCo)
232 rizwank 1.1 	{
233             	    $workingFilename = $filename;
234             	}
235             	else
236             	{
237             	    $workingFilename = "$TempDir/". basename($filename);
238             	    
239             	    print "Working file: $workingFilename\n" if ($debug);
240             	    
241             	    copy ( $filename, $workingFilename)
242             		or die "Couldn't make copy of $filename at $workingFilename: $!\n";
243             
244             	    copy ( "$filename,v", "$workingFilename,v")
245             		or die "Couldn't make copy of $filename,v at $workingFilename,v: $!\n";
246             	    
247             	    # This procedure copied from UI::Manage.pm
248             	    # could be perhaps performed in less steps, but who cares...
249             	    # break lock
250             	    system("rcs -q -u -M $workingFilename 2>>$RcsLogFile");
251             	    # relock
252             	    system("rcs -q -l $workingFilename 2>>$RcsLogFile");
253 rizwank 1.1 	    # check outstanding changes in (note that -t- option should never be used, but it's there for completeness,
254             	    #  and since it was in Manage.pm)
255             	    system("ci -u -mUpdateTopics -t-missing_v $workingFilename 2>>RcsLogFile");
256             	}
257             
258                     my $highestCommonRevision = findHighestCommonRevision( $workingFilename, $newFilename);
259             
260             	# is it the final version of $filename? 
261             	# (in which case:
262             #TODO: what about manually updated files?
263             
264                     if ( $highestCommonRevision =~ /\d*\.\d*/ ) 
265             	{
266                         my $diff = doDiffToHead( $workingFilename, $highestCommonRevision );
267             
268             	    $diff = removeVersionChangeDiff($diff);
269                         patchFile( $filename, $destinationFilename, $diff );
270             
271                         print "\npatching $newFilename from $filename ($highestCommonRevision)" if ($debug);
272                         print "\n$newFilename: p\n" if (!$debug);
273                         copy( $newFilename, $destinationFilename);
274 rizwank 1.1             copy( $newFilename.",v", $destinationFilename.",v");
275                     } elsif ($highestCommonRevision eq "head" ) {
276             	    # This uses the existing file rather than the new one, in case they manually
277             	    # changed the exisiting one without using RCS.
278                         print "\nhighest common revision is final revision in oldTopic (using new Version)" if ($debug);
279                         print "u" if (!$debug);
280                         copy( $newFilename, $destinationFilename);
281                         copy( $newFilename.",v", $destinationFilename.",v");
282                     } else {
283                         #no common versions - this might be a user created file, 
284                         #or a manual attempt at creating a topic off twiki.org?raw=on
285             #TODO: do something nicer about this.. I think i need to do lots of diffs 
286                         #to see if there is any commonality
287                         print "\nWarning: copying $filename (no common versions)" if ($debug);
288                         print "\nNo common versions for $filename: C\n" if (!$debug);
289                         copy( $filename, $destinationFilename);
290                         copy( $filename.",v", $destinationFilename.",v");
291                     }
292             
293             	if ( $doCiCo )
294             	{
295 rizwank 1.1 	    unlink ($workingFilename, "$workingFilename,v") or
296             		warn "Couldn't remove temporary files $workingFilename, $workingFilename,v: $! Could be trouble ahead...\n";
297             	}
298             
299                 } else {
300                     #new file created by users
301             #TODO: this will include topics copied using ManagingWebs (createWeb)
302                     print "\ncopying $filename (new user's file)" if ($debug);
303                     print "c" if (!$debug);
304                     copy( $filename, $destinationFilename);
305                     copy( $filename.",v", $destinationFilename.",v");
306                 }
307             }
308             
309             # ==============================================
310             sub isFromDefaultWeb
311             {
312                 my ($filename) = @_;
313             
314                 my $topic = basename($filename);
315                 return $topic if grep(/^$topic$/, @DefaultWebTopics);
316 rizwank 1.1 }
317             
318             sub sussoutDefaultWebTopics
319             {
320                 opendir(DEFAULTWEB, './data/_default') or die "Yikes - couldn't open ./data/_default: $! ... not safe to proceed!\n";
321                 @DefaultWebTopics = grep(/.txt$/, readdir(DEFAULTWEB));
322                 if ($debug) 
323                 {
324             	print "_default topics in new distro: @DefaultWebTopics\n";
325                 }
326             }
327             
328             # ==============================================
329             sub doDiffToHead
330             {
331                 my ( $filename, $highestCommonRevision ) = @_;
332                
333             #    print "$highestCommonRevision to ".getHeadRevisionNumber($filename)."\n";
334             #    print "\n----------------\n".getRevision($filename, $highestCommonRevision);
335             #     print "\n----------------\n".getRevision($filename, getHeadRevisionNumber($filename)) ;
336             #    return diff ( getRevision($filename, $highestCommonRevision), getRevision($filename, getHeadRevisionNumber($filename)) );
337 rizwank 1.1 
338                 my $cmd = "rcsdiff -r".$highestCommonRevision." -r".getHeadRevisionNumber($filename)." $filename";
339                 print "\n----------------\n".$cmd  if ($debug);
340                 my $diffs =  `$cmd 2>>$RcsLogFile`;
341                 return $diffs;
342             }
343             
344             # ==============================================
345             sub patchFile
346             {
347                 my ( $oldFilename, $destinationFilename, $diff ) = @_;
348             
349             
350                 # Here's where we do a total hack to get WIKIWEBLIST right.  
351                 # we have to check every stinkin line of diff just to intercept this one :-(
352             
353                 my @diff;
354             
355                 @diff = split(/\n/, $diff);
356             
357                 my $i;
358 rizwank 1.1 
359                 for($i = 0; $i < @diff ; $i++)
360                 {
361             	if ($diff[$i] =~ m/<\s*\* Set WIKIWEBLIST = /)
362             	{
363             	    # come to mama
364             	    # this had better be the value in the distribution!...
365             	    $diff[$i] = '< 		* Set WIKIWEBLIST = [[%MAINWEB%.%HOMETOPIC%][%MAINWEB%]] %SEP% [[%TWIKIWEB%.%HOMETOPIC%][%TWIKIWEB%]] %SEP% [[Sandbox.%HOMETOPIC%][Sandbox]]
';
366             	    last;
367             	}
368                 }
369                 
370                 $diff = join("\n", @diff);
371             
372                 # this looks odd: it's just telling patch to apply the patch to $destinationFilename
373                 # perhaps only one file name line would do, but better safe than sorry!    
374                 # (diffs always seem to have two lines)
375                 print(PATCH "--- $destinationFilename\n");
376                 print(PATCH "--- $destinationFilename\n");
377             
378                 print(PATCH "$diff\n");
379 rizwank 1.1 
380             #    print(PATCH, "");
381                 
382                 #patch ($newFilename, $diff);
383             # and then do an rcs ci (check-in)
384             }
385             
386             # ==============================================
387             sub getHeadRevisionNumber
388             {
389                 my ( $filename ) = @_;
390                 
391                 my ( $cmd ) = "rlog ".$filename.",v";
392             
393                 my $line;
394             
395                 my @response = `$cmd 2>>$RcsLogFile`;
396                 foreach $line (@response) {
397                     next unless $line =~ /^head: (\d*\.\d*)/;
398                     return $1;
399                 }
400 rizwank 1.1     return "";    
401             }
402             
403             # ==============================================
404             #returns, as a string, the highest revision number common to both files
405             #Note: we return nothing if the highestcommon verison is also the last version of $filename
406             #TODO: are teh rcs versions always 1.xxx ? if not, how do we know?
407             sub findHighestCommonRevision 
408             {
409                 my ( $filename, $newFilename) = @_;
410                 
411                 my $rev = 1;
412                 my $commonRev = "";
413             
414                 my $oldContent = "qwer";
415                 my $newContent = "qwer";
416                 while ( ( $oldContent ne "" ) & ($newContent ne "") ) {
417                     print "\ncomparing $filename and $newFilename revision 1.$rev " if ($debug);
418                     $oldContent = getRevision( $filename, "1.".$rev);
419                     $newContent = getRevision( $newFilename, "1.".$rev);
420                     if ( ( $oldContent ne "" ) & ($newContent ne "") ) {
421 rizwank 1.1             my $diffs = diff( \$oldContent, \$newContent, {STYLE => "Unified"} );
422             #            print "\n-----------------------|".$diffs."|-------------------\n";
423             #            print "\n-------------------[".$oldContent."]----|".$diffs."|-------[".$newContent."]--------------\n";
424                         if ( $diffs eq "" ) {
425                             #same!!
426                             $commonRev = "1.".$rev;
427                         }
428                     }
429                     $rev = $rev + 1;
430                 }
431             
432                 print "\nlastCommon = $commonRev (head = ".getHeadRevisionNumber( $filename).")" if ($debug);
433                 
434                 if ( $commonRev eq getHeadRevisionNumber( $filename) ) {
435                     return "head";
436                 }
437                 
438                 return $commonRev;
439             }
440             
441             # ==============================================
442 rizwank 1.1 #returns an empty string if the version does not exist
443             sub getRevision
444             {
445                 my ( $filename, $rev ) = @_;
446             
447             # use rlog to test if the revision exists..
448                 my ( $cmd ) = "rlog -r".$rev." ".$filename;
449             
450             #print $cmd."\n";
451                 my @response = `$cmd 2>>$RcsLogFile`;
452             
453                 my $revision;
454                 my $line;
455                 foreach $line (@response) {
456                     next unless $line =~ /^revision (\d*\.\d*)/;
457                     $revision = $1;
458                 }
459             
460                 my $content = "";
461             
462                 if ( $revision and ($revision eq $rev) ) {
463 rizwank 1.1         $cmd = "co -p".$rev." ".$filename;
464                     $content = `$cmd 2>>$RcsLogFile`;
465                 }
466             
467                 return $content;
468             }
469             
470             # $diff is assumed to contain the diff between two similar TWiki topics
471             # TWiki topics should, as a rule, differ in the first line with respect to
472             # their version number.   This routine gets rid of that component of the diff.
473             # It could be more rigorous (like testing if the 1c1 change relates to %META).
474             # The diff is assumed not to contain the preamble.
475             
476             sub removeVersionChangeDiff
477             {
478                 my ($diff) = @_;
479             
480                 my @diff = split( /\n/, $diff);
481             
482                 if ($diff[0] eq '1c1')
483                 {
484 rizwank 1.1 	splice(@diff, 0, 4);
485                 }
486             
487                 $diff = join "\n", @diff;
488             
489             #    print "rVCD returning: \n$diff\n";
490             
491                 return $diff;
492             }
493                 
494             1;

Rizwan Kassim
Powered by
ViewCVS 0.9.2