git-svn: correctly track revisions made to deleted branches
authorEric Wong <normalperson@yhbt.net>
Thu, 25 Jan 2007 23:44:54 +0000 (15:44 -0800)
committerEric Wong <normalperson@yhbt.net>
Fri, 23 Feb 2007 08:57:10 +0000 (00:57 -0800)
git-svn has never been able to handle deleted branches very well
because svn_ra_get_log() is all-or-nothing, meaning that if the
max revision passed to it does not contain the path we're
tracking, we miss all the revisions in the repository.

Branches fetched using --follow-parent still do this
sub-optimally (will be fixed soon).  --follow-parent will soon
become the default, so we will assume that when using get_log();

We will also avoid tracking revprops for revisions with no
path-related changes since otherwise we just end up pulling
logs to paths we don't care about.

Also added a test for this to t9104-git-svn-follow-parent.sh and
correctly commit the log message in the preceeding test (which
conflicted with a filename).

Signed-off-by: Eric Wong <normalperson@yhbt.net>
git-svn.perl
t/t9104-git-svn-follow-parent.sh

index 6ff3a8c5c56fc03a2b9851981bf174cb67bdc95c..0e2348af35a76617ad706680b5defdb893788454 100755 (executable)
@@ -1100,6 +1100,11 @@ sub revisions_eq {
 sub find_parent_branch {
        my ($self, $paths, $rev) = @_;
        return undef unless $::_follow_parent;
+       unless (defined $paths) {
+               $self->ra->get_log([''], $rev, $rev, 0, 1, 1,
+                                  sub { $paths = $_[0] });
+       }
+       return undef unless defined $paths;
 
        # look for a parent from another branch:
        my @b_path_components = split m#/#, $self->rel_path;
@@ -1146,11 +1151,11 @@ sub find_parent_branch {
        }
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        if ($::_follow_parent && (!defined $r0 || !defined $parent)) {
-               $gs->ra->get_log([$gs->{path}], 0, $r, 0, 1, 1, sub {
-                       my ($paths, $rev) = @_;
-                       my $log_entry = eval { $gs->do_fetch($paths, $rev) };
-                       $gs->do_git_commit($log_entry) if $log_entry;
-               });
+               foreach (1 .. $r) {
+                       if (my $log_entry = $gs->do_fetch(undef, $_)) {
+                               $gs->do_git_commit($log_entry);
+                       }
+               }
                ($r0, $parent) = $gs->last_rev_commit;
        }
        if (defined $r0 && defined $parent && $gs->revisions_eq($r0, $r)) {
@@ -1178,16 +1183,8 @@ sub find_parent_branch {
        }
 not_found:
        print STDERR "Branch parent for path: '/",
-                    $self->rel_path, "' not found\n";
-       return undef unless $paths;
-       foreach my $x (sort keys %$paths) {
-               my $p = $paths->{$x};
-               print STDERR '  ', $p->action, '  ', $x;
-               if (my $cp_from = $p->copyfrom_path) {
-                       print STDERR "(from $cp_from:", $p->copyfrom_rev, ')';
-               }
-               print STDERR "\n";
-       }
+                    $self->rel_path, "' @ $rev not found\n";
+       print STDERR '  ', $_, "\n" foreach (sort keys %$paths);
        return undef;
 }
 
@@ -1279,6 +1276,8 @@ sub make_log_entry {
        my ($self, $rev, $parents, $ed) = @_;
        my $untracked = $self->get_untracked($ed);
 
+       return undef if ($ed->{nr} == 0 && scalar @$untracked == 0);
+
        open my $un, '>>', "$self->{dir}/unhandled.log" or croak $!;
        print $un "r$rev\n" or croak $!;
        print $un $_, "\n" foreach @$untracked;
@@ -1296,10 +1295,6 @@ sub make_log_entry {
        }
        close $un or croak $!;
 
-       delete $rp->{'svn:date'}; # this is the only revprop for r0
-       return undef if ($ed->{nr} == 0 && scalar @$untracked == 0 &&
-                        scalar keys %$rp == 0);
-
        $log_entry{date} = parse_svn_date($log_entry{date});
        $log_entry{author} = check_author($log_entry{author});
        $log_entry{log} .= "\n";
@@ -1317,18 +1312,26 @@ sub fetch {
        my $inc = 1000;
        my ($min, $max) = ($base, $head < $base + $inc ? $head : $base + $inc);
        my $err_handler = $SVN::Error::handler;
-       $SVN::Error::handler = \&skip_unknown_revs;
+       my $err;
+       $SVN::Error::handler = sub { ($err) = @_; skip_unknown_revs($err); } ;
        while (1) {
                my @revs;
                $self->ra->get_log([$self->{path}], $min, $max, 0, 1, 1, sub {
                        my ($paths, $rev, $author, $date, $log) = @_;
                        push @revs, [ $paths, $rev ] });
+               if (! @revs && $err) {
+                       print STDERR "Branch probably deleted:\n  ",
+                                    $err->expanded_message,
+                                    "\nWill attempt to follow revisions ",
+                                    "committed before the deletion\n";
+                       @revs = map { [ undef, $_ ] } ($min .. $max);
+               }
                foreach (@revs) {
                        if (my $log_entry = $self->do_fetch(@$_)) {
                                $self->do_git_commit($log_entry, @parents);
                        }
                }
-               last if $max >= $head;
+               last if $max >= $head || $err;
                $min = $max + 1;
                $max += $inc;
                $max = $head if ($max > $head);
@@ -2226,7 +2229,6 @@ sub dup {
 sub get_log {
        my ($self, @args) = @_;
        my $pool = SVN::Pool->new;
-       $args[4]-- if $args[4] && ! $::_follow_parent;
        splice(@args, 3, 1) if ($SVN::Core::VERSION le '1.2.0');
        my $ret = $self->SUPER::get_log(@args, $pool);
        $pool->clear;
index 615c863b94b0c3efe6bc724b1f6849803b471a04..a6ba0faebd32fdbb349bd5ed9eb2af8bf59eafa6 100755 (executable)
@@ -85,7 +85,7 @@ test_expect_success 'follow higher-level parent' "
         cd blob &&
                 echo hi > hi &&
                 svn add hi &&
-                svn commit -m 'hi' &&
+                svn commit -m 'hihi' &&
                 cd ..
         svn mkdir -m 'new glob at top level' $svnrepo/glob &&
         svn mv -m 'move blob down a level' $svnrepo/blob $svnrepo/glob/blob &&
@@ -93,6 +93,15 @@ test_expect_success 'follow higher-level parent' "
         git-svn fetch -i blob --follow-parent
         "
 
+test_expect_success 'follow deleted directory' "
+       svn mv -m 'bye!' $svnrepo/glob/blob/hi $svnrepo/glob/blob/bye&&
+       svn rm -m 'remove glob' $svnrepo/glob &&
+       git-svn init -i glob $svnrepo/glob &&
+       git-svn fetch -i glob &&
+       test \"\`git cat-file blob refs/remotes/glob~1:blob/bye\`\" = hi &&
+       test -z \"\`git ls-tree -z refs/remotes/glob\`\"
+       "
+
 test_debug 'gitk --all &'
 
 test_done