#!/usr/bin/perl -w
#
# cvsSynchCheckout version 0.1
#
# Outputs a shell script (consisting of "mkdir", "cp", "rm", "cvs add"
# and "cvs remove" commands), which will "import" a directory tree
# into an existing cvs checkout.
#
# Usage: cvsSynchCheckout <source_dir> <target_cvs_dir>
#
# The difference between this script and "cvs import" is that no
# merging is done, and that "old" files present in the cvs working
# directory are removed.
#
# Directories called "CVS" in <source_dir> are ignored.
#
# Note: running the script modifies no files, it just prints out the
# required commands.
#
# Known bug: Changes to the execute permissions of files are ignored.
# Known bug: Spaces in filenames result in broken output
#
# Written by Hannes Reich (hannesATskynetDOTie), 14/10/2004.
# This code is in the public domain.
#

use strict;
use warnings;

use File::Find;
use File::Basename;

sub process_source_file($ );
sub process_target_file($ );

die "Usage: $0 <source_dir> <target_cvs_dir>\n"
    unless 1 == $#ARGV;

my($source_dir, $target_dir) = @ARGV;

# strip trailing slashes from arguments
map s&/+$&&g, ($source_dir, $target_dir);

print <<BLURB
# The following commands would make the CVS checkout in
#   $target_dir
# the same as
#   $source_dir
#
# (You still need to do a "cvs commit" afterwards)
#
BLURB
;

# Generate commands to remove stuff from the target dir that's not in
# the source dir.
finddepth({"wanted" => \&process_target_file,
	   "no_chdir" => 1
	  },
	  ($target_dir)
	 );


# Generate commands to bring over the contents of the source dir
find({"wanted" => \&process_source_file,
      "no_chdir" => 1
     },
     ($source_dir)
    );

# Note: find() processes directories _before_ their contents, which is
# what we need for "cvs add", while finddepth() processes directories
# _after_ their contents, which is the Right Thing for "cvs remove".

exit;

sub process_source_file($ )
  {
    my $source = $File::Find::name;
    return if $source eq $source_dir;

    my $target = $source;
    $target =~ s/^$source_dir/$target_dir/
      or die "Source file name '$source' doesn't contain '$source_dir'. I don't understand this.\n";

    if(-d $source and not -d $target)
      {
	print "cvs remove -f $target\n" if -e $target; # delete file with same name as the dir we want to create
	print "mkdir -p $target\n";
      }
    if(-f $source)
      {
	if(-d $target) # dir with same name as the file we want to
                       # create!
	  {
	    die "Error: '$target' is in CVS as a directory, so we can never check in '$source'!\n"
	      if -d "$target/CVS";
	    print "rm -rf $target\n";
	  }
	# Don't bother with compares, let CVS worry about that
	# Don't use -a because CVS likes timestamps to be real.
	print "cp -f $source $target\n";
	print "cvs add $target\n";
      }
  }

sub process_target_file($ )
  {
    my $target = $File::Find::name;
    return if $target eq $target_dir or ($target =~ m&/CVS(/|$)&);

    my $source = $target;
    $source =~ s/^$target_dir/$source_dir/
      or die "Target file name '$target' doesn't contain '$target_dir'. I don't understand this.\n";

    if(not -e $source)
      {
	# No need for special directory handling here because when
	# this is called by finddepth(), we will already have
	# processed the directory contents.
	print "cvs remove -f $target\n";
      }
  }

