#!/usr/bin/perl #try to upgrade an installation's TWikiTopics using the rcs info in it. #Run in the TWiki/data directory use File::Find; use File::Copy; use Text::Diff; #use Algorithm::Diff qw(diff); #$currentDataDir = "twikidata/data"; #$newReleaseDataDir = "twiki20031218beta/data"; my ($currentDataDir, $newReleaseDataDir); $destinationDataDir = "newData/"; $baseDir = `pwd`; chomp ($baseDir); #Set if you want to see the debug output #$debug = "yes"; $numArgs = $#ARGV + 1; foreach $argnum (0 .. $#ARGV) { print "$ARGV[$argnum]\n" if $debug; } $currentDataDir = $ARGV[0]; $newReleaseDataDir = $ARGV[1]; if ((! -d $currentDataDir ) || (! -d $newReleaseDataDir)) { print "\nUsage: ./updateTopics.pl \n"; print "\n\tthe new upgraded dataDir will be created in $baseDir/$destinationDataDir\n"; print "\tthere will be no changes made to either the sourceDataDir or newReleaseDataDir.\n\n"; print "\t this progam will attempt to use the rcs versioning information to upgrade the\n"; print "\t contents of your distributed topics in sourceDataDir to the content in newReleaseDataDir.\n\n"; print "Output:\n"; print "\tfor each file that has no versioning information a _v_ will be printed\n"; print "\tfor each file that has no changes from the previous release a _c_ will be printed\n"; print "\tfor each file that has changes and a patch is generated a _p_ will be printed\n"; print "\tfor each file that new in the newReleaseDataDir a _+_ will be printed\n"; print "\t when the script has attempted to patch the $destinationDataDir, *.rej files will contain the failed merges\n"; print "\t although many of these rejected chages will be discarable, please check them to see if your configuration is still ok\n\n"; #TODO: test for gnu patch, and rcs #TODO: point to patch.log and rcs.log? exit; } mkdir $destinationDataDir; #redirect stderr into a file (rcs dumps out heaps of info) $rcsLogFile = ">".$baseDir."/rcs.log"; open(STDERR, $rcsLogFile); open(PATCH, "> $destinationDataDir/patchTopics"); print "\nchecking existing files from $currentDataDir"; #TODO: need to find a way to detect non-Web directories so we don't make a mess of them.. # (should i just ignore Dirs without any ,v files?) - i can't upgrade tehm anyway.. #upgrade templates..? find(\&getRLog, $currentDataDir); close(PATCH); #do a find through $newReleaseDataDir and copy all missing files & dirs print "\nchecking for new files in $newReleaseDataDir"; find(\©NewTopics, $newReleaseDataDir); #run `patch patchTopics` in $destinationDataDir print "\nPatching topics (manually check the rejected patch (.rej) files)"; chdir($destinationDataDir); `patch -p2 < patchTopics > patch.log`; #TODO: examing the .rej files to remove the ones that have already been applied find(\&listRejects, "."); #TODO: run `ci` in $destinationDataDir print "\n\n"; exit; # ============================================ sub listRejects { my ( $filename ) = @_; $filename = $baseDir."/".$destinationDataDir."/".$File::Find::name if (! $filename ); if ($filename =~ /.rej$/ ) { print "\nPatch rejected: $filename"; } } # ============================================ sub copyNewTopics { # my ( $filename ) = $baseDir."/".$File::Find::name; my ( $filename ) = @_; $filename = $baseDir."/".$File::Find::name if (! $filename ); my $destinationFilename = $filename; $destinationFilename =~ s/$newReleaseDataDir/$destinationDataDir/g; # return if $filename =~ /,v$/; # return if $filename =~ /.lock$/; # return if $filename =~ /~$/; if ( -d $filename ) { print "\nprocessing directory (creating $destinationFilename)\n"; mkdir($destinationFilename); return; } if (! -e $destinationFilename ) { print "\nadding $filename (new in this release)" if ($debug); print "+" if (!$debug); copy( $filename, $destinationFilename); } } # ============================================ sub getRLog { # my ( $filename ) = $baseDir."/".$File::Find::name; my ( $filename ) = @_; $filename = $baseDir."/".$File::Find::name if (! $filename ); my ( $newFilename ) = $filename; $newFilename =~ s/$currentDataDir/$newReleaseDataDir/g; print "\n$filename - $newFilename" if ( $debug); my $destinationFilename = $filename; $destinationFilename =~ s/$currentDataDir/$destinationDataDir/g; return if $filename =~ /,v$/; return if $filename =~ /.lock$/; return if $filename =~ /~$/; if ( -d $filename ) { print "\nprocessing directory (creating $destinationFilename)\n"; mkdir($destinationFilename); return; } if ( isFromDefaultWeb($filename) ) { $newFilename =~ s|^(.*)/[^/]*/([^/]*)|$1/_default/$2|g; print "\nFound topic copied from _default ($newFilename)" if ($debug); } if (! -e $filename.",v" ){ #TODO: maybe copy this one too (this will inclure the .htpasswd file!!) if ( $filename =~ /.txt$/ ) { #TODO: in interactive mode ask if they want to create this topic's rcs file.. print "\nError: $filename does not have any rcs information" if ($debug); print "v" if (! $debug); } copy( $filename, $destinationFilename); return; } if ( -e $newFilename ) { #file that may need upgrading my $highestCommonRevision = findHighestCommonRevision( $filename, $newFilename); #print "-r".$highestCommonRevision."\n"; #is it the final version of $filename (in which case #TODO: what about manually updated files? if ( $highestCommonRevision =~ /\d*\.\d*/ ) { my $diff = doDiffToHead( $filename, $highestCommonRevision ); #print "\n========\n".$diff."\n========\n"; patchFile( $filename, $destinationFilename, $diff ); print "\npatching $newFileName from $filename ($highestCommonRevision)" if ($debug); print "p" if (!$debug); copy( $newFilename, $destinationFilename); copy( $newFilename.",v", $destinationFilename.",v"); } elsif ($highestCommonRevision eq "head" ) { print "\nhighest revision also final revision in oldTopic (using new Version)" if ($debug); print "+" if (!$debug); copy( $newFilename, $destinationFilename); copy( $newFilename.",v", $destinationFilename.",v"); } else { #no common versions - this might be a user created file, #or a manual attempt at creating a topic off twiki.org?raw=on #TODO: do something nicer about this.. I think i need to do lots of diffs #to see if there is any commonality print "\nWarning: copying $filename (no common versions)" if ($debug); print "c" if (!$debug); copy( $filename, $destinationFilename); copy( $filename.",v", $destinationFilename.",v"); } } else { #new file created by users #TODO: this will include topics copied using ManagingWebs (createWeb) print "\ncopying $filename (new user's file)" if ($debug); print "c" if (!$debug); copy( $filename, $destinationFilename); copy( $filename.",v", $destinationFilename.",v"); } } # ============================================== sub isFromDefaultWeb { my ($filename) = @_; $filename =~ /^(.*)\/[^\/]*\/([^\/]*)/; my $topic = $2; return $topic if ( $topic eq "WebChanges.txt") ; return $topic if ( $topic eq "WebHome.txt"); return $topic if ( $topic eq "WebIndex.txt"); return $topic if ( $topic eq "WebNotify.txt"); return $topic if ( $topic eq "WebPreferences.txt"); return $topic if ( $topic eq "WebRss.txt"); return $topic if ( $topic eq "WebSearch.txt"); return $topic if ( $topic eq "WebStatistics.txt"); return $topic if ( $topic eq "WebTopicList.txt"); } # ============================================== sub doDiffToHead { my ( $filename, $highestCommonRevision ) = @_; # print "$highestCommonRevision to ".getHeadRevisionNumber($filename)."\n"; # print "\n----------------\n".getRevision($filename, $highestCommonRevision); # print "\n----------------\n".getRevision($filename, getHeadRevisionNumber($filename)) ; # return diff ( getRevision($filename, $highestCommonRevision), getRevision($filename, getHeadRevisionNumber($filename)) ); my $cmd = "rcsdiff -r".$highestCommonRevision." -r".getHeadRevisionNumber($filename)." $filename"; print "\n----------------\n".$cmd if ($debug); return `$cmd`; } # ============================================== sub patchFile { my ( $oldFilename, $destinationFilename, $diff ) = @_; #make the paths relative again $oldFilename =~ s/$baseDir//g; $destinationFilename =~ s/$baseDir//g; print(PATCH "--- $oldFilename\n"); print(PATCH "--- $destinationFilename\n"); print(PATCH "$diff\n"); # print(PATCH, ""); #patch ($newFilename, $diff); # and then do an rcs ci (check-in) } # ============================================== sub getHeadRevisionNumber { my ( $filename ) = @_; my ( $cmd ) = "rlog ".$filename.",v"; my @response = `$cmd`; foreach $line (@response) { next unless $line =~ /^head: (\d*\.\d*)/; return $1; } return; } # ============================================== #returns, as a string, the highest revision number common to both files #Note: we return nothing if the highestcommon verison is also the last version of $filename #TODO: are teh rcs versions always 1.xxx ? if not, how do we know? sub findHighestCommonRevision { my ( $filename, $newFilename) = @_; my $rev = 1; my $commonRev; my $oldContent = "qwer"; my $newContent = "qwer"; while ( ( $oldContent ne "" ) & ($newContent ne "") ) { print "\ncomparing $filename and $newFilename revision 1.$rev " if ($debug); $oldContent = getRevision( $filename, "1.".$rev); $newContent = getRevision( $newFilename, "1.".$rev); if ( ( $oldContent ne "" ) & ($newContent ne "") ) { my $diffs = diff( \$oldContent, \$newContent, {STYLE => "Unified"} ); # print "\n-----------------------|".$diffs."|-------------------\n"; # print "\n-------------------[".$oldContent."]----|".$diffs."|-------[".$newContent."]--------------\n"; if ( $diffs eq "" ) { #same!! $commonRev = "1.".$rev; } } $rev = $rev + 1; } print "\nlastCommon = $commonRev (head = ".getHeadRevisionNumber( $filename).")" if ($debug); if ( $commonRev eq getHeadRevisionNumber( $filename) ) { return "head"; } return $commonRev; } # ============================================== #returns an empty string if the version does not exist sub getRevision { my ( $filename, $rev ) = @_; # use rlog to test if the revision exists.. my ( $cmd ) = "rlog -r".$rev." ".$filename; #print $cmd."\n"; my @response = `$cmd`; my $revision; foreach $line (@response) { next unless $line =~ /^revision (\d*\.\d*)/; $revision = $1; } my $content; if ( $revision eq $rev ) { $cmd = "co -p".$rev." ".$filename; $content = `$cmd`; } return $content; }