git-svn: correctly display fatal() error messages
[gitweb.git] / git-svn.perl
index 4a56f1871a6e185c4504d2464f264a8d8adc124b..599edc3dd5b3d4305ab2c39a0b1ee566669f4074 100755 (executable)
@@ -21,6 +21,7 @@
 $ENV{LC_ALL} = 'C';
 $| = 1; # unbuffer STDOUT
 
+sub fatal (@) { print STDERR @_; exit 1 }
 # If SVN:: library support is added, please make the dependencies
 # optional and preserve the capability to use the command-line client.
 # use eval { require SVN::... } to make it lazy load
@@ -134,6 +135,7 @@ sub nag_lib {
        'commit-diff' => [ \&commit_diff, 'Commit a diff between two trees',
                        { 'message|m=s' => \$_message,
                          'file|F=s' => \$_file,
+                         'revision|r=s' => \$_revision,
                        %cmt_opts } ],
        dcommit => [ \&dcommit, 'Commit several diffs to merge with upstream',
                        { 'merge|m|M' => \$_merge,
@@ -231,7 +233,7 @@ sub rebuild {
                my @commit = grep(/^git-svn-id: /,`git-cat-file commit $c`);
                next if (!@commit); # skip merges
                my ($url, $rev, $uuid) = extract_metadata($commit[$#commit]);
-               if (!$rev || !$uuid) {
+               if (!defined $rev || !$uuid) {
                        croak "Unable to extract revision or UUID from ",
                                "$c, $commit[$#commit]\n";
                }
@@ -570,7 +572,7 @@ sub commit_lib {
                                $no = 1;
                        }
                }
-               close $fh or croak $?;
+               close $fh or exit 1;
                if (! defined $r_new && ! defined $cmt_new) {
                        unless ($no) {
                                die "Failed to parse revision information\n";
@@ -586,11 +588,28 @@ sub commit_lib {
 sub dcommit {
        my $gs = "refs/remotes/$GIT_SVN";
        chomp(my @refs = safe_qx(qw/git-rev-list --no-merges/, "$gs..HEAD"));
+       my $last_rev;
        foreach my $d (reverse @refs) {
+               if (quiet_run('git-rev-parse','--verify',"$d~1") != 0) {
+                       die "Commit $d\n",
+                           "has no parent commit, and therefore ",
+                           "nothing to diff against.\n",
+                           "You should be working from a repository ",
+                           "originally created by git-svn\n";
+               }
+               unless (defined $last_rev) {
+                       (undef, $last_rev, undef) = cmt_metadata("$d~1");
+                       unless (defined $last_rev) {
+                               die "Unable to extract revision information ",
+                                   "from commit $d~1\n";
+                       }
+               }
                if ($_dry_run) {
                        print "diff-tree $d~1 $d\n";
                } else {
-                       commit_diff("$d~1", $d);
+                       if (my $r = commit_diff("$d~1", $d, undef, $last_rev)) {
+                               $last_rev = $r;
+                       } # else: no changes, same $last_rev
                }
        }
        return if $_dry_run;
@@ -605,7 +624,7 @@ sub dcommit {
        } else {
                print "No changes between current HEAD and $gs\n",
                      "Hard resetting to the latest $gs\n";
-               @finish = qw/reset --hard/;
+               @finish = qw/reset --mixed/;
        }
        sys('git', @finish, $gs);
 }
@@ -814,6 +833,14 @@ sub commit_diff {
                print STDERR "Needed URL or usable git-svn id command-line\n";
                commit_diff_usage();
        }
+       my $r = shift;
+       unless (defined $r) {
+               if (defined $_revision) {
+                       $r = $_revision
+               } else {
+                       die "-r|--revision is a required argument\n";
+               }
+       }
        if (defined $_message && defined $_file) {
                print STDERR "Both --message/-m and --file/-F specified ",
                                "for the commit message.\n",
@@ -830,22 +857,35 @@ sub commit_diff {
        ($repo, $SVN_PATH) = repo_path_split($SVN_URL);
        $SVN_LOG ||= libsvn_connect($repo);
        $SVN ||= libsvn_connect($repo);
+       if ($r eq 'HEAD') {
+               $r = $SVN->get_latest_revnum;
+       } elsif ($r !~ /^\d+$/) {
+               die "revision argument: $r not understood by git-svn\n";
+       }
        my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
-       my $ed = SVN::Git::Editor->new({        r => $SVN->get_latest_revnum,
+       my $rev_committed;
+       my $ed = SVN::Git::Editor->new({        r => $r,
                                                ra => $SVN_LOG, c => $tb,
                                                svn_path => $SVN_PATH
                                        },
                                $SVN->get_commit_editor($_message,
-                                       sub {print "Committed $_[0]\n"},@lock)
+                                       sub {
+                                               $rev_committed = $_[0];
+                                               print "Committed $_[0]\n";
+                                       }, @lock)
                                );
-       my $mods = libsvn_checkout_tree($ta, $tb, $ed);
-       if (@$mods == 0) {
-               print "No changes\n$ta == $tb\n";
-               $ed->abort_edit;
-       } else {
-               $ed->close_edit;
-       }
+       eval {
+               my $mods = libsvn_checkout_tree($ta, $tb, $ed);
+               if (@$mods == 0) {
+                       print "No changes\n$ta == $tb\n";
+                       $ed->abort_edit;
+               } else {
+                       $ed->close_edit;
+               }
+       };
+       fatal "$@\n" if $@;
        $_message = $_file = undef;
+       return $rev_committed;
 }
 
 ########################### utility functions #########################
@@ -2463,7 +2503,7 @@ sub extract_metadata {
        my $id = shift or return (undef, undef, undef);
        my ($url, $rev, $uuid) = ($id =~ /^git-svn-id:\s(\S+?)\@(\d+)
                                                        \s([a-f\d\-]+)$/x);
-       if (!$rev || !$uuid || !$url) {
+       if (!defined $rev || !$uuid || !$url) {
                # some of the original repositories I made had
                # identifiers like this:
                ($rev, $uuid) = ($id =~/^git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
@@ -2742,7 +2782,7 @@ sub process_rm {
 sub libsvn_fetch {
        my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
        open my $gui, '| git-update-index -z --index-info' or croak $!;
-       my @amr;
+       my %amr;
        foreach my $f (keys %$paths) {
                my $m = $paths->{$f}->action();
                $f =~ s#^/+##;
@@ -2756,7 +2796,7 @@ sub libsvn_fetch {
                my $t = $SVN->check_path($f, $rev, $pool);
                if ($t == $SVN::Node::file) {
                        if ($m =~ /^[AMR]$/) {
-                               push @amr, [ $m, $f ];
+                               $amr{$f} = $m;
                        } else {
                                die "Unrecognized action: $m, ($f r$rev)\n";
                        }
@@ -2764,13 +2804,13 @@ sub libsvn_fetch {
                        my @traversed = ();
                        libsvn_traverse($gui, '', $f, $rev, \@traversed);
                        foreach (@traversed) {
-                               push @amr, [ $m, $_ ]
+                               $amr{$_} = $m;
                        }
                }
                $pool->clear;
        }
-       foreach (@amr) {
-               libsvn_get_file($gui, $_->[1], $rev, $_->[0]);
+       foreach (keys %amr) {
+               libsvn_get_file($gui, $_, $rev, $amr{$_});
        }
        close $gui or croak $?;
        return libsvn_log_entry($rev, $author, $date, $msg, [$last_commit]);