#!perl -w # This script copies modified files from a source CVS tree to a destination # tree. Modified files are detected by comparing their modification dates # with the CVS Entries file. # # Modified files are copied in their entirety to the destination tree # (no diffing is done). Files are only copied of the CVS version of the # file is the same in both trees. If the destination file is modified # already, it is backed up and replaced. # # To use this on your tree/platform, do the following: # # 1. Fix the !perl line, if necessary. # 2. Fix $dirsep to be the directory separator on your platform. # 3. Uncomment the appropriate $dst_linebreaks file specify what linebreaks # you want for the copied files. This variable defines the *destination* linebreaks # that you want your changes to be converted to. # For example, if you have a linux volume # mounted (via SAMBA perhaps) to your windows box where you've made changes to # source files, you'd want $dst_linebreaks to be set for unix. This ensures that # linebreaks are converted to the appropriate OS linebreak scheme for your *target* tree. # 4. Set $src_tree and $dest_tree to point to the directories you want # to sync up. These don't have to point to the root of the tree, # but should be equivalent directories in the two trees. # # First version: # Simon Fraser use File::stat; use Time::Local; # change for your platform ('\' == windows, ':' == mac, '/' == unix) $dirsep = "\\"; # Set this to the native OS of the *destination* tree # $dst_linebreaks = pack("cc", 13); # Mac #$dst_linebreaks = pack("cc", 13, 10); # DOS $dst_linebreaks = pack("cc", 10); # UNIX #change for your src and dest trees $src_tree = "x:\\mozilla\\xpfe\\components"; $dest_tree = "h:\\builds\\mozilla\\xpfe\\components"; #//-------------------------------------------------------------------------------------------------- #// _copyFile. Copy file from src to dest, converting linebreaks if necessary #//-------------------------------------------------------------------------------------------------- sub _copyFile($;$;$;$) { my($srcdir, $destdir, $file, $backup) = @_; my($srcfile) = $srcdir.$dirsep.$file; my($dstfile) = $destdir.$dirsep.$file; if ($backup) { my($counter) = 0; while (-f $dstfile."-".$counter) { $counter ++; } rename($dstfile, $dstfile."-".$counter) or die "Failed to rename file\n"; } print "Copying $file over to dest\n"; my($newdest) = $dstfile."_temp"; open(SRCFILE, "< $srcfile") or die "Can't open source file $srcfile\n"; open(NEWDEST, "> $newdest") or die "Can't open dest file $newdest\n"; while () { chomp($_); print NEWDEST $_.$dst_linebreaks; } close(SRCFILE); close(NEWDEST); if (!$backup) { unlink($dstfile) or die "Failed to remove $dstfile\n"; } rename($newdest, $dstfile) or die "Failed to rename $newdest\n"; } #//-------------------------------------------------------------------------------------------------- #// _readCVSInfo. Suck in the CVS info from the Entries file #//-------------------------------------------------------------------------------------------------- sub _readCVSInfo($) { my($cvsDir) = @_; my($entries_file_name) = $cvsDir.$dirsep."CVS".$dirsep."Entries"; # print "Reading $entries_file_name\n"; open(ENTRIES, $entries_file_name) || die "Could not open file $entries_file_name"; my(%cvs_entries); # Read in the path if available while (defined ($line = )) { chomp($line); #parse out the line. Format is: # files: /filename/version/date/options/tag # dirs: D/dirname//// # dir? D # because we might be reading an entries file from another platform, with # different linebreaks, be anal about cleaning up $line. if ($line =~ /^?\/(.+)\/(.+)\/(.+)\/(.*)\/(.*)?$/) { my($filename) = $1; my($version) = $2; my($date) = $3; my($options) = $4; my($tag) = $5; my(%cvs_file) = ( "version" => $version, "date" => $date, "options" => $options, "tag" => $tag ); # print $filename." ".$version." ".$date." ".$options." ".$tag."\n"; $cvs_entries{$filename} = \%cvs_file; } } close ENTRIES; return %cvs_entries; } #//-------------------------------------------------------------------------------------------------- #// _fileIsModified. compare mod date with CVS entries to see if a file is modified #//-------------------------------------------------------------------------------------------------- sub _fileIsModified($;$;$) { my($entries, $dir, $file) = @_; my($abs_file) = $dir.$dirsep.$file; if (exists($entries->{$file})) { my($date) = $entries->{$file}->{"date"}; # stat the file to get its date my($file_data) = stat($abs_file) || die "Could not stat $file\n"; my($mod_string) = scalar(gmtime($file_data->mtime)); return ($mod_string ne $date); } else { return 0; } } #//-------------------------------------------------------------------------------------------------- #// _processFile. args: entries hash, dir, filename #//-------------------------------------------------------------------------------------------------- sub _processFile($;$;$;$;$) { my($src_entries, $dest_entries, $srcdir, $destdir, $file) = @_; my($abs_file) = $srcdir.$dirsep.$file; if (exists($src_entries->{$file})) { my($file_entry) = $src_entries->{$file}; my($version) = $file_entry->{"version"}; if (_fileIsModified($src_entries, $srcdir, $file)) { my($rel_file) = $abs_file; $rel_file =~ s/^$src_tree//; # print "¥ÊFile $rel_file is modified\n"; # check CVS version in dest my($dest_entry) = $dest_entries->{$file}; if ($dest_entry) { my($dest_version) = $dest_entry->{"version"}; my($versions_match) = ($version == $dest_version); my($dest_modified) = _fileIsModified($dest_entries, $destdir, $file); if ($versions_match) { # ok, we can copy the file over now, backing up dest if it is modified _copyFile($srcdir, $destdir, $file, $dest_modified); } else { print "File $rel_file is version $version in the src tree, but $dest_version in dest. This file will not be copied.\n"; } } else { print "No CVS entry found in destination tree for $rel_file\n"; } } } else { print "No entry for file $file\n"; } } #//-------------------------------------------------------------------------------------------------- #// _traverseDir. Traverse one dir, recurse for each found dir. #//-------------------------------------------------------------------------------------------------- sub _traverseDir($;$) { my($srcdir, $destdir) = @_; opendir(DIR, $srcdir) or die "Cannot open dir $srcdir\n"; my @files = readdir(DIR); closedir DIR; # suck in the CVS info for this dir, if there is a CVS dir unless (-e $srcdir.$dirsep."CVS".$dirsep."Entries" && -e $destdir.$dirsep."CVS".$dirsep."Entries") { print "$srcdir is not a CVS directory in both source and dest\n"; return; } print " Doing $srcdir\n"; my(%src_entries) = _readCVSInfo($srcdir); my(%dest_entries) = _readCVSInfo($destdir); my $file; foreach $file (@files) { my $filepath = $srcdir.$dirsep.$file; if (-d $filepath) { if ($file ne "CVS") # ignore 'CVS' dirs { # print "Going into $filepath\n"; _traverseDir($filepath, $destdir.$dirsep.$file); } } else { # process this file _processFile(\%src_entries, \%dest_entries, $srcdir, $destdir, $file); } } } #//-------------------------------------------------------------------------------------------------- #// MigrateChanges #//-------------------------------------------------------------------------------------------------- sub MigrateChanges($;$) { my($srcdir, $destdir) = @_; # Check that src and dest exist if (! -d $srcdir) { die "Source directory $srcdir does not exist\n"; } if (! -d $destdir) { die "Dest directory $destdir does not exist\n"; } print "---------------------------------------------------------\n"; print "Migrating changes from\n $srcdir\nto\n $destdir\n"; print "---------------------------------------------------------\n"; _traverseDir($srcdir, $destdir); print "---------------------------------------------------------\n"; } MigrateChanges($src_tree, $dest_tree);