git-svn: hopefully make 'fetch' more user-friendly
[gitweb.git] / git-svn.perl
index d55691216cca8f0070bd97898da6e0e8b134103f..3eed62fc0bcacd74827b1e7c901d352f6b843986 100755 (executable)
@@ -53,7 +53,7 @@ BEGIN
 my ($_stdin, $_help, $_edit,
        $_message, $_file,
        $_template, $_shared,
-       $_version,
+       $_version, $_fetch_all,
        $_merge, $_strategy, $_dry_run,
        $_prefix);
 $Git::SVN::_follow_parent = 1;
@@ -84,7 +84,9 @@ BEGIN
 
 my %cmd = (
        fetch => [ \&cmd_fetch, "Download new revisions from SVN",
-                       { 'revision|r=s' => \$_revision, %fc_opts } ],
+                       { 'revision|r=s' => \$_revision,
+                         'all|a' => \$_fetch_all,
+                          %fc_opts } ],
        init => [ \&cmd_init, "Initialize a repo for tracking" .
                          " (requires URL argument)",
                          \%init_opts ],
@@ -106,8 +108,8 @@ BEGIN
                         'prefix=s' => \$_prefix,
                        } ],
        'multi-fetch' => [ \&cmd_multi_fetch,
-                       'Fetch multiple trees (like git-svnimport)',
-                       \%fc_opts ],
+                          "Deprecated alias for $0 fetch --all",
+                          { 'revision|r=s' => \$_revision, %fc_opts } ],
        'migrate' => [ sub { },
                       # no-op, we automatically run this anyways,
                       'Migrate configuration/metadata/layout from
@@ -226,16 +228,19 @@ sub cmd_init {
 }
 
 sub cmd_fetch {
-       if (@_) {
-               die "Additional fetch arguments are no longer supported.\n",
-                   "Use --follow-parent if you have moved/copied directories
-                   instead.\n";
+       if (grep /^\d+=./, @_) {
+               die "'<rev>=<commit>' fetch arguments are ",
+                   "no longer supported.\n";
        }
-       my $gs = Git::SVN->new;
-       $gs->fetch(parse_revision_argument());
-       if ($gs->{last_commit} && !verify_ref('refs/heads/master^0')) {
-               command_noisy(qw(update-ref refs/heads/master),
-                             $gs->{last_commit});
+       my ($remote) = @_;
+       if (@_ > 1) {
+               die "Usage: $0 fetch [--all|-a] [svn-remote]\n";
+       }
+       $remote ||= $Git::SVN::default_repo_id;
+       if ($_fetch_all) {
+               cmd_multi_fetch();
+       } else {
+               Git::SVN::fetch_all($remote, Git::SVN::read_all_remotes());
        }
 }
 
@@ -276,11 +281,27 @@ sub cmd_set_tree {
 
 sub cmd_dcommit {
        my $head = shift;
-       my $gs = Git::SVN->new;
        $head ||= 'HEAD';
-       my @refs = command(qw/rev-list --no-merges/, $gs->refname."..$head");
+       my ($url, $rev, $uuid);
+       my ($fh, $ctx) = command_output_pipe('rev-list', $head);
+       my @refs;
+       my $c;
+       while (<$fh>) {
+               $c = $_;
+               chomp $c;
+               ($url, $rev, $uuid) = cmt_metadata($c);
+               last if (defined $url && defined $rev && defined $uuid);
+               unshift @refs, $c;
+       }
+       close $fh; # most likely breaking the pipe
+       unless (defined $url && defined $rev && defined $uuid) {
+               die "Unable to determine upstream SVN information from ",
+                   "$head history:\n  $ctx\n";
+       }
+       my $gs = Git::SVN->find_by_url($url) or
+                          die "Can't determine fetch information for $url\n";
        my $last_rev;
-       foreach my $d (reverse @refs) {
+       foreach my $d (@refs) {
                if (!verify_ref("$d~1")) {
                        fatal "Commit $d\n",
                              "has no parent commit, and therefore ",
@@ -300,13 +321,13 @@ sub cmd_dcommit {
                } else {
                        my %ed_opts = ( r => $last_rev,
                                        log => get_commit_entry($d)->{log},
-                                       ra => $gs->ra,
+                                       ra => Git::SVN::Ra->new($url),
                                        tree_a => "$d~1",
                                        tree_b => $d,
                                        editor_cb => sub {
                                               print "Committed r$_[0]\n";
                                               $last_rev = $_[0]; },
-                                       svn_path => $gs->{path} );
+                                       svn_path => '');
                        if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) {
                                print "No changes\n$d~1 == $d\n";
                        }
@@ -424,18 +445,6 @@ sub cmd_commit_diff {
 
 ########################### utility functions #########################
 
-sub parse_revision_argument {
-       if (!defined $_revision || $_revision eq 'BASE:HEAD') {
-               return (undef, undef);
-       }
-       return ($1, $2) if ($_revision =~ /^(\d+):(\d+)$/);
-       return ($_revision, $_revision) if ($_revision =~ /^\d+$/);
-       return (undef, $1) if ($_revision =~ /^BASE:(\d+)$/);
-       return ($1, undef) if ($_revision =~ /^(\d+):HEAD$/);
-       die "revision argument: $_revision not understood by git-svn\n",
-           "Try using the command-line svn client instead\n";
-}
-
 sub complete_svn_url {
        my ($url, $path) = @_;
        $path =~ s#/+$##;
@@ -739,6 +748,19 @@ sub resolve_local_globs {
        }
 }
 
+sub parse_revision_argument {
+       my ($base, $head) = @_;
+       if (!defined $::_revision || $::_revision eq 'BASE:HEAD') {
+               return ($base, $head);
+       }
+       return ($1, $2) if ($::_revision =~ /^(\d+):(\d+)$/);
+       return ($::_revision, $::_revision) if ($::_revision =~ /^\d+$/);
+       return ($head, $head) if ($::_revision eq 'HEAD');
+       return ($base, $1) if ($::_revision =~ /^BASE:(\d+)$/);
+       return ($1, $head) if ($::_revision =~ /^(\d+):HEAD$/);
+       die "revision argument: $::_revision not understood by git-svn\n";
+}
+
 sub fetch_all {
        my ($repo_id, $remotes) = @_;
        my $remote = $remotes->{$repo_id};
@@ -771,6 +793,8 @@ sub fetch_all {
                        push @gs, $gs;
                }
        }
+
+       ($base, $head) = parse_revision_argument($base, $head);
        $ra->gs_fetch_loop_common($base, $head, \@gs, \@globs);
 }
 
@@ -904,6 +928,35 @@ sub init_remote_config {
        $self->{url} = $url;
 }
 
+sub find_by_url { # repos_root and, path are optional
+       my ($class, $full_url, $repos_root, $path) = @_;
+       my $remotes = read_all_remotes();
+       if (defined $full_url && defined $repos_root && !defined $path) {
+               $path = $full_url;
+               $path =~ s#^\Q$repos_root\E(?:/|$)##;
+       }
+       foreach my $repo_id (keys %$remotes) {
+               my $u = $remotes->{$repo_id}->{url} or next;
+               next if defined $repos_root && $repos_root ne $u;
+
+               my $fetch = $remotes->{$repo_id}->{fetch} || {};
+               foreach (qw/branches tags/) {
+                       resolve_local_globs($u, $fetch,
+                                           $remotes->{$repo_id}->{$_});
+               }
+               my $p = $path;
+               unless (defined $p) {
+                       $p = $full_url;
+                       $p =~ s#^\Q$u\E(?:/|$)## or next;
+               }
+               foreach my $f (keys %$fetch) {
+                       next if $f ne $p;
+                       return Git::SVN->new($fetch->{$f}, $repo_id, $f);
+               }
+       }
+       undef;
+}
+
 sub init {
        my ($class, $url, $path, $repo_id, $ref_id, $no_write) = @_;
        my $self = _new($class, $repo_id, $ref_id, $path);
@@ -1387,23 +1440,7 @@ sub find_parent_branch {
        print STDERR  "Found possible branch point: ",
                      "$new_url => ", $self->full_url, ", $r\n";
        $branch_from =~ s#^/##;
-       my $remotes = read_all_remotes();
-       my $gs;
-       foreach my $repo_id (keys %$remotes) {
-               my $u = $remotes->{$repo_id}->{url} or next;
-               next if $url ne $u;
-               my $fetch = $remotes->{$repo_id}->{fetch};
-               foreach (qw/branches tags/) {
-                       resolve_local_globs($url, $fetch,
-                                           $remotes->{$repo_id}->{$_});
-               }
-               foreach my $f (keys %$fetch) {
-                       next if $f ne $branch_from;
-                       $gs = Git::SVN->new($fetch->{$f}, $repo_id, $f);
-                       last;
-               }
-               last if $gs;
-       }
+       my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
        unless ($gs) {
                my $ref_id = $self->{ref_id};
                $ref_id =~ s/\@\d+$//;
@@ -3004,8 +3041,25 @@ sub log_use_color {
 }
 
 sub git_svn_log_cmd {
-       my ($r_min, $r_max) = @_;
-       my $gs = Git::SVN->_new;
+       my ($r_min, $r_max, @args) = @_;
+       my $head = 'HEAD';
+       foreach my $x (@args) {
+               last if $x eq '--';
+               next unless ::verify_ref("$x^0");
+               $head = $x;
+               last;
+       }
+
+       my $url;
+       my ($fh, $ctx) = command_output_pipe('rev-list', $head);
+       while (<$fh>) {
+               chomp;
+               $url = (::cmt_metadata($_))[0];
+               last if defined $url;
+       }
+       close $fh; # break the pipe
+
+       my $gs = Git::SVN->find_by_url($url) || Git::SVN->_new;
        my @cmd = (qw/log --abbrev-commit --pretty=raw --default/,
                   $gs->refname);
        push @cmd, '-r' unless $non_recursive;
@@ -3198,7 +3252,7 @@ sub cmd_show_log {
        }
 
        config_pager();
-       @args = (git_svn_log_cmd($r_min, $r_max), @args);
+       @args = (git_svn_log_cmd($r_min, $r_max, @args), @args);
        my $log = command_output_pipe(@args);
        run_pager();
        my (@k, $c, $d);