X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-cvsexportcommit.perl;h=99b3dc392afdd5ffb3e2aa8f6fe6b484c2e2f1fe;hb=a2a3bf7b2baf0ff64c5b5ffc78d54be82d9967f1;hp=5a8c011802c46356c9c842282baca8f6fb54f01b;hpb=5e80092f7e6db09a40a62e837ca3f74f0bc5ad73;p=git.git diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 5a8c01180..99b3dc392 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -1,18 +1,24 @@ #!/usr/bin/perl -w +# Known limitations: +# - cannot add or remove binary files +# - does not propagate permissions +# - tells "ready for commit" even when things could not be completed +# (eg addition of a binary file) + use strict; use Getopt::Std; use File::Temp qw(tempdir); use Data::Dumper; -use File::Basename qw(basename); +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_v, $opt_c ); +our ($opt_h, $opt_p, $opt_v, $opt_c, $opt_f, $opt_a, $opt_m ); -getopts('hpvc'); +getopts('hpvcfam:'); $opt_h && usage(); @@ -23,11 +29,10 @@ our ($tmpdir, $tmpdirname) = tempdir('git-cvsapplycommit-XXXXXX', TMPDIR => 1, CLEANUP => 1); -print Dumper(@ARGV); # resolve target commit my $commit; $commit = pop @ARGV; -$commit = `git-rev-parse --verify "$commit"^0`; +$commit = safe_pipe_capture('git-rev-parse', '--verify', "$commit^0"); chomp $commit; if ($?) { die "The commit reference $commit did not resolve!"; @@ -37,7 +42,7 @@ if ($?) { my $parent; if (@ARGV) { $parent = pop @ARGV; - $parent = `git-rev-parse --verify "$parent"^0"`; + $parent = safe_pipe_capture('git-rev-parse', '--verify', "$parent^0"); chomp $parent; if ($?) { die "The parent reference did not resolve!"; @@ -45,27 +50,47 @@ if (@ARGV) { } # find parents from the commit itself -my @commit = `git-cat-file commit $commit`; +my @commit = safe_pipe_capture('git-cat-file', 'commit', $commit); my @parents; -foreach my $p (@commit) { - if ($p =~ m/^$/) { # end of commit headers, we're done - last; +my $committer; +my $author; +my $stage = 'headers'; # headers, msg +my $title; +my $msg = ''; + +foreach my $line (@commit) { + chomp $line; + if ($stage eq 'headers' && $line eq '') { + $stage = 'msg'; + next; } - if ($p =~ m/^parent (\w{40})$/) { # found a parent - push @parents, $1; + + if ($stage eq 'headers') { + if ($line =~ m/^parent (\w{40})$/) { # found a parent + push @parents, $1; + } elsif ($line =~ m/^author (.+) \d+ \+\d+$/) { + $author = $1; + } elsif ($line =~ m/^committer (.+) \d+ \+\d+$/) { + $committer = $1; + } + } else { + $msg .= $line . "\n"; + unless ($title) { + $title = $line; + } } } if ($parent) { + my $found; # double check that it's a valid parent foreach my $p (@parents) { - my $found; if ($p eq $parent) { $found = 1; last; }; # found it - die "Did not find $parent in the parents for this commit!"; } + die "Did not find $parent in the parents for this commit!" if !$found; } 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]; @@ -77,18 +102,35 @@ if ($parent) { $opt_v && print "Applying to CVS commit $commit from parent $parent\n"; # grab the commit message -`git-cat-file commit $commit | sed -e '1,/^\$/d' > .msg`; -$? && die "Error extracting the commit message"; +open(MSG, ">.msg") or die "Cannot open .msg for writing"; +if ($opt_m) { + print MSG $opt_m; +} +print MSG $msg; +if ($opt_a) { + print MSG "\n\nAuthor: $author\n"; + if ($author ne $committer) { + print MSG "Committer: $committer\n"; + } +} +close MSG; -my (@afiles, @dfiles, @mfiles); -my @files = `git-diff-tree -r $parent $commit`; -print @files; +my (@afiles, @dfiles, @mfiles, @dirs); +my @files = safe_pipe_capture('git-diff-tree', '-r', $parent, $commit); +#print @files; $? && die "Error in git-diff-tree"; foreach my $f (@files) { chomp $f; - my @fields = split(m/\s+/, $f); + my @fields = split(m!\s+!, $f); if ($fields[4] eq 'A') { - push @afiles, $fields[5]; + my $path = $fields[5]; + push @afiles, $path; + # add any needed parent directories + $path = dirname $path; + while (!-d $path and ! grep { $_ eq $path } @dirs) { + unshift @dirs, $path; + $path = dirname $path; + } } if ($fields[4] eq 'M') { push @mfiles, $fields[5]; @@ -103,17 +145,38 @@ undef @files; # don't need it anymore # check that the files are clean and up to date according to cvs my $dirty; -foreach my $f (@afiles, @mfiles, @dfiles) { - # TODO:we need to handle removed in cvs and/or new (from git) - my $status = `cvs -q status "$f" | grep '^File: '`; - - unless ($status =~ m/Status: Up-to-date$/) { +foreach my $d (@dirs) { + if (-e $d) { + $dirty = 1; + warn "$d exists and is not a directory!\n"; + } +} +foreach my $f (@afiles) { + # This should return only one value + my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f)); + if (@status > 1) { warn 'Strange! cvs status returned more than one line?'}; + if (-d dirname $f and $status[0] !~ m/Status: Unknown$/ + and $status[0] !~ m/^File: no file /) { + $dirty = 1; + warn "File $f is already known in your CVS checkout -- perhaps it has been added by another user. Or this may indicate that it exists on a different branch. If this is the case, use -f to force the merge.\n"; + warn "Status was: $status[0]\n"; + } +} +foreach my $f (@mfiles, @dfiles) { + # TODO:we need to handle removed in cvs + my @status = grep(m/^File/, safe_pipe_capture('cvs', '-q', 'status' ,$f)); + if (@status > 1) { warn 'Strange! cvs status returned more than one line?'}; + unless ($status[0] =~ m/Status: Up-to-date$/) { $dirty = 1; warn "File $f not up to date in your CVS checkout!\n"; } } if ($dirty) { - die "Exiting: your CVS tree is not clean for this merge."; + if ($opt_f) { warn "The tree is not clean -- forced merge\n"; + $dirty = 0; + } else { + die "Exiting: your CVS tree is not clean for this merge."; + } } ### @@ -122,20 +185,32 @@ if ($dirty) { ### +print "Creating new directories\n"; +foreach my $d (@dirs) { + unless (mkdir $d) { + warn "Could not mkdir $d: $!"; + $dirty = 1; + } + `cvs add $d`; + if ($?) { + $dirty = 1; + warn "Failed to cvs add directory $d -- you may need to do it manually"; + } +} + print "'Patching' binary files\n"; -my @bfiles = `git-diff-tree -p $parent $commit | grep '^Binary'`; +my @bfiles = grep(m/^Binary/, safe_pipe_capture('git-diff-tree', '-p', $parent, $commit)); @bfiles = map { chomp } @bfiles; foreach my $f (@bfiles) { # check that the file in cvs matches the "old" file - # extract the file to $tmpdir and comparre with cmp - my $tree = `git-rev-parse $parent^{tree} `; + # extract the file to $tmpdir and compare with cmp + my $tree = safe_pipe_capture('git-rev-parse', "$parent^{tree}"); chomp $tree; my $blob = `git-ls-tree $tree "$f" | cut -f 1 | cut -d ' ' -f 3`; chomp $blob; `git-cat-file blob $blob > $tmpdir/blob`; - `cmp -q $f $tmpdir/blob`; - if ($?) { + if (system('cmp', '-s', $f, "$tmpdir/blob")) { warn "Binary file $f in CVS does not match parent.\n"; $dirty = 1; next; @@ -167,7 +242,7 @@ if (($? >> 8) == 2) { } foreach my $f (@afiles) { - `cvs add $f`; + system('cvs', 'add', $f); if ($?) { $dirty = 1; warn "Failed to cvs add $f -- you may need to do it manually"; @@ -175,7 +250,7 @@ foreach my $f (@afiles) { } foreach my $f (@dfiles) { - `cvs rm -f $f`; + system('cvs', 'rm', '-f', $f); if ($?) { $dirty = 1; warn "Failed to cvs rm -f $f -- you may need to do it manually"; @@ -183,6 +258,7 @@ foreach my $f (@dfiles) { } print "Commit to CVS\n"; +print "Patch: $title\n"; my $commitfiles = join(' ', @afiles, @mfiles, @dfiles); my $cmd = "cvs commit -F .msg $commitfiles"; @@ -196,7 +272,7 @@ if ($dirtypatch) { if ($opt_c) { print "Autocommit\n $cmd\n"; - print `cvs commit -F .msg $commitfiles 2>&1`; + print safe_pipe_capture('cvs', 'commit', '-F', '.msg', @afiles, @mfiles, @dfiles); if ($?) { cleanupcvs(@files); die "Exiting: The commit did not succeed"; @@ -207,7 +283,7 @@ if ($opt_c) { } sub usage { print STDERR <); + close $child or die join(' ',@_).": $! $?"; + } else { + exec(@_) or die "$! $?"; # exec() can fail the executable can't be found + } + return wantarray ? @output : join('',@output); +}