git-svn: rebuild convenience and bugfixes
[git.git] / contrib / git-svn / git-svn.perl
index 149149f..da0ff9a 100755 (executable)
@@ -42,7 +42,7 @@ my $_optimize_commits = 1 unless $ENV{GIT_SVN_NO_OPTIMIZE_COMMITS};
 my $sha1 = qr/[a-f\d]{40}/;
 my $sha1_short = qr/[a-f\d]{4,40}/;
 my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
-       $_find_copies_harder, $_l, $_cp_similarity,
+       $_find_copies_harder, $_l, $_cp_similarity, $_cp_remote,
        $_repack, $_repack_nr, $_repack_flags,
        $_template, $_shared, $_no_default_regex, $_no_graft_copy,
        $_limit, $_verbose, $_incremental, $_oneline, $_l_fmt, $_show_commit,
@@ -86,6 +86,7 @@ my %cmd = (
                        { 'revision|r=i' => \$_revision } ],
        rebuild => [ \&rebuild, "Rebuild git-svn metadata (after git clone)",
                        { 'no-ignore-externals' => \$_no_ignore_ext,
+                         'copy-remote|remote=s' => \$_cp_remote,
                          'upgrade' => \$_upgrade } ],
        'graft-branches' => [ \&graft_branches,
                        'Detect merges/branches from already imported history',
@@ -134,7 +135,7 @@ init_vars();
 load_authors() if $_authors;
 load_all_refs() if $_branch_all_refs;
 svn_compat_check();
-migration_check() unless $cmd =~ /^(?:init|multi-init)$/;
+migration_check() unless $cmd =~ /^(?:init|rebuild|multi-init)$/;
 $cmd{$cmd}->[0]->(@ARGV);
 exit 0;
 
@@ -174,6 +175,9 @@ sub version {
 }
 
 sub rebuild {
+       if (quiet_run(qw/git-rev-parse --verify/,"refs/remotes/$GIT_SVN^0")) {
+               copy_remote_ref();
+       }
        $SVN_URL = shift or undef;
        my $newest_rev = 0;
        if ($_upgrade) {
@@ -663,17 +667,15 @@ sub show_log {
        my $pid = open(my $log,'-|');
        defined $pid or croak $!;
        if (!$pid) {
-               my @rl = (qw/git-log --abbrev-commit --pretty=raw
-                               --default/, "remotes/$GIT_SVN");
-               push @rl, '--raw' if $_verbose;
-               exec(@rl, @args) or croak $!;
+               exec(git_svn_log_cmd($r_min,$r_max), @args) or croak $!;
        }
        setup_pager();
        my (@k, $c, $d);
+
        while (<$log>) {
                if (/^commit ($sha1_short)/o) {
                        my $cmt = $1;
-                       if ($c && defined $c->{r} && $c->{r} != $r_last) {
+                       if ($c && cmt_showable($c) && $c->{r} != $r_last) {
                                $r_last = $c->{r};
                                process_commit($c, $r_min, $r_max, \@k) or
                                                                goto out;
@@ -692,8 +694,7 @@ sub show_log {
                } elsif ($d) {
                        push @{$c->{diff}}, $_;
                } elsif (/^    (git-svn-id:.+)$/) {
-                       my ($url, $rev, $uuid) = extract_metadata($1);
-                       $c->{r} = $rev;
+                       (undef, $c->{r}, undef) = extract_metadata($1);
                } elsif (s/^    //) {
                        push @{$c->{l}}, $_;
                }
@@ -715,6 +716,52 @@ out:
 
 ########################### utility functions #########################
 
+sub cmt_showable {
+       my ($c) = @_;
+       return 1 if defined $c->{r};
+       if ($c->{l} && $c->{l}->[-1] eq "...\n" &&
+                               $c->{a_raw} =~ /\@([a-f\d\-]+)>$/) {
+               my @msg = safe_qx(qw/git-cat-file commit/, $c->{c});
+               shift @msg while ($msg[0] ne "\n");
+               shift @msg;
+               @{$c->{l}} = grep !/^git-svn-id: /, @msg;
+
+               (undef, $c->{r}, undef) = extract_metadata(
+                               (grep(/^git-svn-id: /, @msg))[-1]);
+       }
+       return defined $c->{r};
+}
+
+sub git_svn_log_cmd {
+       my ($r_min, $r_max) = @_;
+       my @cmd = (qw/git-log --abbrev-commit --pretty=raw
+                       --default/, "refs/remotes/$GIT_SVN");
+       push @cmd, '--summary' if $_verbose;
+       return @cmd unless defined $r_max;
+       if ($r_max == $r_min) {
+               push @cmd, '--max-count=1';
+               if (my $c = revdb_get($REVDB, $r_max)) {
+                       push @cmd, $c;
+               }
+       } else {
+               my ($c_min, $c_max);
+               $c_max = revdb_get($REVDB, $r_max);
+               $c_min = revdb_get($REVDB, $r_min);
+               if ($c_min && $c_max) {
+                       if ($r_max > $r_max) {
+                               push @cmd, "$c_min..$c_max";
+                       } else {
+                               push @cmd, "$c_max..$c_min";
+                       }
+               } elsif ($r_max > $r_min) {
+                       push @cmd, $c_max;
+               } else {
+                       push @cmd, $c_min;
+               }
+       }
+       return @cmd;
+}
+
 sub fetch_child_id {
        my $id = shift;
        print "Fetching $id\n";
@@ -1263,12 +1310,12 @@ sub svn_checkout_tree {
                } elsif ($m->{chg} eq 'T') {
                        sys(qw(svn rm --force),$m->{file_b});
                        apply_mod_line_blob($m);
-                       sys(qw(svn add --force), $m->{file_b});
+                       sys(qw(svn add), $m->{file_b});
                        svn_check_prop_executable($m);
                } elsif ($m->{chg} eq 'A') {
                        svn_ensure_parent_path( $m->{file_b} );
                        apply_mod_line_blob($m);
-                       sys(qw(svn add --force), $m->{file_b});
+                       sys(qw(svn add), $m->{file_b});
                        svn_check_prop_executable($m);
                } else {
                        croak "Invalid chg: $m->{chg}\n";
@@ -1897,6 +1944,7 @@ sub svn_cmd_checkout {
 
 sub check_upgrade_needed {
        if (!-r $REVDB) {
+               -d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]);
                open my $fh, '>>',$REVDB or croak $!;
                close $fh;
        }
@@ -2009,6 +2057,7 @@ sub migrate_revdb {
                        init_vars();
                        exit 0 if -r $REVDB;
                        print "Upgrading svn => git mapping...\n";
+                       -d $GIT_SVN_DIR or mkpath([$GIT_SVN_DIR]);
                        open my $fh, '>>',$REVDB or croak $!;
                        close $fh;
                        rebuild();
@@ -2206,6 +2255,7 @@ sub setup_pager { # translated to Perl from pager.c
 sub get_author_info {
        my ($dest, $author, $t, $tz) = @_;
        $author =~ s/(?:^\s*|\s*$)//g;
+       $dest->{a_raw} = $author;
        my $_a;
        if ($_authors) {
                $_a = $rusers{$author} || undef;
@@ -2440,7 +2490,7 @@ sub svn_grab_base_rev {
        close $fh;
        if (defined $c && length $c) {
                my ($url, $rev, $uuid) = extract_metadata((grep(/^git-svn-id: /,
-                       safe_qx(qw/git-cat-file commit/, $c)))[0]);
+                       safe_qx(qw/git-cat-file commit/, $c)))[-1]);
                return ($rev, $c);
        }
        return (undef, undef);
@@ -2719,6 +2769,17 @@ sub revdb_get {
        return $ret;
 }
 
+sub copy_remote_ref {
+       my $origin = $_cp_remote ? $_cp_remote : 'origin';
+       my $ref = "refs/remotes/$GIT_SVN";
+       if (safe_qx('git-ls-remote', $origin, $ref)) {
+               sys(qw/git fetch/, $origin, "$ref:$ref");
+       } else {
+               die "Unable to find remote reference: ",
+                               "refs/remotes/$GIT_SVN on $origin\n";
+       }
+}
+
 package SVN::Git::Editor;
 use vars qw/@ISA/;
 use strict;