#!/usr/bin/perl -w
-# Known limitations:
-# - does not propagate permissions
-# - error handling has not been extensively tested
-#
-
use strict;
use Getopt::Std;
use File::Temp qw(tempdir);
use Data::Dumper;
use File::Basename qw(basename dirname);
-unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
- die "GIT_DIR is not defined or is unreadable";
-}
-
-our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u);
+our ($opt_h, $opt_P, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m, $opt_d, $opt_u, $opt_w);
-getopts('uhPpvcfam:d:');
+getopts('uhPpvcfam:d:w:');
$opt_h && usage();
die "Need at least one commit identifier!" unless @ARGV;
+if ($opt_w) {
+ unless ($ENV{GIT_DIR}) {
+ # Remember where our GIT_DIR is before changing to CVS checkout
+ my $gd =`git-rev-parse --git-dir`;
+ chomp($gd);
+ if ($gd eq '.git') {
+ my $wd = `pwd`;
+ chomp($wd);
+ $gd = $wd."/.git" ;
+ }
+ $ENV{GIT_DIR} = $gd;
+ }
+
+ if (! -d $opt_w."/CVS" ) {
+ die "$opt_w is not a CVS checkout";
+ }
+ chdir $opt_w or die "Cannot change to CVS checkout at $opt_w";
+}
+unless ($ENV{GIT_DIR} && -r $ENV{GIT_DIR}){
+ die "GIT_DIR is not defined or is unreadable";
+}
+
+
my @cvs;
if ($opt_d) {
@cvs = ('cvs', '-d', $opt_d);
@cvs = ('cvs');
}
-# setup a tempdir
-our ($tmpdir, $tmpdirname) = tempdir('git-cvsapplycommit-XXXXXX',
- TMPDIR => 1,
- CLEANUP => 1);
-
# resolve target commit
my $commit;
$commit = pop @ARGV;
}
}
+my $noparent = "0000000000000000000000000000000000000000";
if ($parent) {
my $found;
# double check that it's a valid parent
} else { # we don't have a parent from the cmdline...
if (@parents == 1) { # it's safe to get it from the commit
$parent = $parents[0];
- } else { # or perhaps not!
- die "This commit has more than one parent -- please name the parent you want to use explicitly";
+ } elsif (@parents == 0) { # there is no parent
+ $parent = $noparent;
+ } else { # cannot choose automatically from multiple parents
+ die "This commit has more than one parent -- please name the parent you want to use explicitly";
}
}
}
close MSG;
-`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+if ($parent eq $noparent) {
+ `git-diff-tree --binary -p --root $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+} else {
+ `git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff";
+}
## apply non-binary changes
print "Checking if patch will apply\n";
my @stat;
-open APPLY, "GIT_DIR= git-apply $context --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
+open APPLY, "GIT_DIR= git-apply $context --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch";
@stat=<APPLY>;
close APPLY || die "Cannot patch";
my (@bfiles,@files,@afiles,@dfiles);
my %cvsstat;
if (@canstatusfiles) {
if ($opt_u) {
- my @updated = safe_pipe_capture(@cvs, 'update', @canstatusfiles);
+ my @updated = xargs_safe_pipe_capture([@cvs, 'update'], @canstatusfiles);
print @updated;
}
my @cvsoutput;
- @cvsoutput= safe_pipe_capture(@cvs, 'status', @canstatusfiles);
+ @cvsoutput = xargs_safe_pipe_capture([@cvs, 'status'], @canstatusfiles);
my $matchcount = 0;
foreach my $l (@cvsoutput) {
chomp $l;
}
print "Applying\n";
-`GIT_DIR= git-apply $context --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
+`GIT_DIR= git-apply $context --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch";
print "Patch applied successfully. Adding new files and directories to CVS\n";
my $dirtypatch = 0;
+
+#
+# We have to add the directories in order otherwise we will have
+# problems when we try and add the sub-directory of a directory we
+# have not added yet.
+#
+# Luckily this is easy to deal with by sorting the directories and
+# dealing with the shortest ones first.
+#
+@dirs = sort { length $a <=> length $b} @dirs;
+
foreach my $d (@dirs) {
if (system(@cvs,'add',$d)) {
$dirtypatch = 1;
print "You'll need to apply the patch in .cvsexportcommit.diff manually\n";
print "using a patch program. After applying the patch and resolving the\n";
print "problems you may commit using:";
+ print "\n cd \"$opt_w\"" if $opt_w;
print "\n $cmd\n\n";
exit(1);
}
if ($opt_c) {
print "Autocommit\n $cmd\n";
- print safe_pipe_capture(@cvs, 'commit', '-F', '.msg', @files);
+ print xargs_safe_pipe_capture([@cvs, 'commit', '-F', '.msg'], @files);
if ($?) {
die "Exiting: The commit did not succeed";
}
sub usage {
print STDERR <<END;
-Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-m msgprefix] [ parent ] commit
+Usage: GIT_DIR=/path/to/.git ${\basename $0} [-h] [-p] [-v] [-c] [-f] [-u] [-w cvsworkdir] [-m msgprefix] [ parent ] commit
END
exit(1);
}
return wantarray ? @output : join('',@output);
}
-sub safe_pipe_capture_blob {
- my $output;
- if (my $pid = open my $child, '-|') {
- local $/;
- undef $/;
- $output = (<$child>);
- close $child or die join(' ',@_).": $! $?";
- } else {
- exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
- }
- return $output;
+sub xargs_safe_pipe_capture {
+ my $MAX_ARG_LENGTH = 65536;
+ my $cmd = shift;
+ my @output;
+ my $output;
+ while(@_) {
+ my @args;
+ my $length = 0;
+ while(@_ && $length < $MAX_ARG_LENGTH) {
+ push @args, shift;
+ $length += length($args[$#args]);
+ }
+ if (wantarray) {
+ push @output, safe_pipe_capture(@$cmd, @args);
+ }
+ else {
+ $output .= safe_pipe_capture(@$cmd, @args);
+ }
+ }
+ return wantarray ? @output : $output;
}