X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=git-cvsexportcommit.perl;h=4863c91fe3c713c9e1b05d78b9bd22583ff4a4c0;hb=27e4dd8de76bdd60c88003e0f27cee8ad26c5cd6;hp=5e23851f8cf866112baf3e76973a8ca649d5c105;hpb=12f9b8a3152ff9fa48464187a83f7c6279fe5094;p=git.git diff --git a/git-cvsexportcommit.perl b/git-cvsexportcommit.perl index 5e23851f8..4863c91fe 100755 --- a/git-cvsexportcommit.perl +++ b/git-cvsexportcommit.perl @@ -1,10 +1,9 @@ #!/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) +# - error handling has not been extensively tested +# use strict; use Getopt::Std; @@ -68,9 +67,9 @@ foreach my $line (@commit) { if ($stage eq 'headers') { if ($line =~ m/^parent (\w{40})$/) { # found a parent push @parents, $1; - } elsif ($line =~ m/^author (.+) \d+ \+\d+$/) { + } elsif ($line =~ m/^author (.+) \d+ [-+]\d+$/) { $author = $1; - } elsif ($line =~ m/^committer (.+) \d+ \+\d+$/) { + } elsif ($line =~ m/^committer (.+) \d+ [-+]\d+$/) { $committer = $1; } } else { @@ -115,36 +114,40 @@ if ($opt_a) { } close MSG; -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); - if ($fields[4] eq 'A') { - 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]; - } - if ($fields[4] eq 'D') { - push @dfiles, $fields[5]; - } +`git-diff-tree --binary -p $parent $commit >.cvsexportcommit.diff`;# || die "Cannot diff"; + +## apply non-binary changes +my $fuzz = $opt_p ? 0 : 2; + +print "Checking if patch will apply\n"; + +my @stat; +open APPLY, "GIT_DIR= git-apply -C$fuzz --binary --summary --numstat<.cvsexportcommit.diff|" || die "cannot patch"; +@stat=; +close APPLY || die "Cannot patch"; +my (@bfiles,@files,@afiles,@dfiles); +chomp @stat; +foreach (@stat) { + push (@bfiles,$1) if m/^-\t-\t(.*)$/; + push (@files, $1) if m/^-\t-\t(.*)$/; + push (@files, $1) if m/^\d+\t\d+\t(.*)$/; + push (@afiles,$1) if m/^ create mode [0-7]+ (.*)$/; + push (@dfiles,$1) if m/^ delete mode [0-7]+ (.*)$/; } -$opt_v && print "The commit affects:\n "; -$opt_v && print join ("\n ", @afiles,@mfiles,@dfiles) . "\n\n"; -undef @files; # don't need it anymore +map { s/^"(.*)"$/$1/g } @bfiles,@files; +map { s/\\([0-7]{3})/sprintf('%c',oct $1)/eg } @bfiles,@files; # check that the files are clean and up to date according to cvs my $dirty; +my @dirs; +foreach my $p (@afiles) { + my $path = dirname $p; + while (!-d $path and ! grep { $_ eq $path } @dirs) { + unshift @dirs, $path; + $path = dirname $path; + } +} + foreach my $d (@dirs) { if (-e $d) { $dirty = 1; @@ -153,6 +156,10 @@ foreach my $d (@dirs) { } foreach my $f (@afiles) { # This should return only one value + if ($f =~ m,(.*)/[^/]*$,) { + my $p = $1; + next if (grep { $_ eq $p } @dirs); + } 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$/ @@ -162,7 +169,9 @@ foreach my $f (@afiles) { warn "Status was: $status[0]\n"; } } -foreach my $f (@mfiles, @dfiles) { + +foreach my $f (@files) { + next if grep { $_ eq $f } @afiles; # 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?'}; @@ -179,72 +188,26 @@ if ($dirty) { } } -### -### NOTE: if you are planning to die() past this point -### you MUST call cleanupcvs(@files) before die() -### +print "Applying\n"; +`GIT_DIR= git-apply -C$fuzz --binary --summary --numstat --apply <.cvsexportcommit.diff` || die "cannot patch"; - -print "Creating new directories\n"; +print "Patch applied successfully. Adding new files and directories to CVS\n"; +my $dirtypatch = 0; foreach my $d (@dirs) { - unless (mkdir $d) { - warn "Could not mkdir $d: $!"; - $dirty = 1; - } - `cvs add $d`; - if ($?) { - $dirty = 1; + if (system('cvs','add',$d)) { + $dirtypatch = 1; warn "Failed to cvs add directory $d -- you may need to do it manually"; } } -print "'Patching' binary files\n"; - -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 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`; - if (system('cmp', '-s', $f, "$tmpdir/blob")) { - warn "Binary file $f in CVS does not match parent.\n"; - $dirty = 1; - next; - } - - # replace with the new file - `git-cat-file blob $blob > $f`; - - # TODO: something smart with file modes - -} -if ($dirty) { - cleanupcvs(@files); - die "Exiting: Binary files in CVS do not match parent"; -} - -## apply non-binary changes -my $fuzz = $opt_p ? 0 : 2; - -print "Patching non-binary files\n"; -print `(git-diff-tree -p $parent -p $commit | patch -p1 -F $fuzz ) 2>&1`; - -my $dirtypatch = 0; -if (($? >> 8) == 2) { - cleanupcvs(@files); - die "Exiting: Patch reported serious trouble -- you will have to apply this patch manually"; -} elsif (($? >> 8) == 1) { # some hunks failed to apply - $dirtypatch = 1; -} - foreach my $f (@afiles) { - system('cvs', 'add', $f); + if (grep { $_ eq $f } @bfiles) { + system('cvs', 'add','-kb',$f); + } else { + system('cvs', 'add', $f); + } if ($?) { - $dirty = 1; + $dirtypatch = 1; warn "Failed to cvs add $f -- you may need to do it manually"; } } @@ -252,35 +215,40 @@ foreach my $f (@afiles) { foreach my $f (@dfiles) { system('cvs', 'rm', '-f', $f); if ($?) { - $dirty = 1; + $dirtypatch = 1; warn "Failed to cvs rm -f $f -- you may need to do it manually"; } } print "Commit to CVS\n"; -print "Patch: $title\n"; -my $commitfiles = join(' ', @afiles, @mfiles, @dfiles); -my $cmd = "cvs commit -F .msg $commitfiles"; +print "Patch title (first comment line): $title\n"; +my @commitfiles = map { unless (m/\s/) { '\''.$_.'\''; } else { $_; }; } (@files); +my $cmd = "cvs commit -F .msg @commitfiles"; if ($dirtypatch) { print "NOTE: One or more hunks failed to apply cleanly.\n"; - print "Resolve the conflicts and then commit using:\n"; + 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 $cmd\n\n"; exit(1); } - if ($opt_c) { print "Autocommit\n $cmd\n"; - print safe_pipe_capture('cvs', 'commit', '-F', '.msg', @afiles, @mfiles, @dfiles); + print safe_pipe_capture('cvs', 'commit', '-F', '.msg', @files); if ($?) { - cleanupcvs(@files); die "Exiting: The commit did not succeed"; } print "Committed successfully to CVS\n"; } else { print "Ready for you to commit, just run:\n\n $cmd\n"; } + +# clean up +unlink(".cvsexportcommit.diff"); +unlink(".msg"); + sub usage { print STDERR <); + close $child or die join(' ',@_).": $! $?"; + } else { + exec(@_) or die "$! $?"; # exec() can fail the executable can't be found + } + return $output; +}