git-svn: correctly handle do_{switch,update} in deep directories
authorEric Wong <normalperson@yhbt.net>
Sun, 28 Jan 2007 12:59:05 +0000 (04:59 -0800)
committerEric Wong <normalperson@yhbt.net>
Fri, 23 Feb 2007 08:57:10 +0000 (00:57 -0800)
The do_update or do_switch functions in SVN only allow for a
single path component; so 'path/to/deep/dir' would be
interpreted as 'path'.

SVN 1.4.x has a reparent function that can let us change the
session to use a higher-level root of the repository, so we can
use that for do_switch (which still doesn't seem to work in SVN
1.4.3 (a fix was attempted, but they missed the rest of the
typemap changes needed in trunk...)).

On the do_update side, we can use set_path on higher level
directories and set them to a newer revision so they don't get
updated.  We can't do this with do_switch, either, because the
relative path we're tracking can change (directory moving into
a child of itself).

Because of these changes, we need to double check that our Fetch
editor is correctly performing stripping on any prefixed paths
from update, otherwise we'll just die() because that would be
a bug.

Added a test case which helped me notice and fix problems with
do_switch, too.

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

index 36e5c57adf61434717188cb62989425877ef3b43..dcc7fd460330330d96f92870d6db8920afa0f27a 100755 (executable)
@@ -1153,7 +1153,7 @@ sub find_parent_branch {
                if ($self->ra->can_do_switch) {
                        print STDERR "Following parent with do_switch\n";
                        # do_switch works with svn/trunk >= r22312, but that
-                       # is not included with SVN 1.4.2 (the latest version
+                       # is not included with SVN 1.4.3 (the latest version
                        # at the moment), so we can't rely on it
                        $self->{last_commit} = $parent;
                        $ed = SVN::Git::Fetcher->new($self);
@@ -1621,7 +1621,10 @@ sub open_directory {
 
 sub git_path {
        my ($self, $path) = @_;
-       $path =~ s!$self->{path_strip}!! if $self->{path_strip};
+       if ($self->{path_strip}) {
+               $path =~ s!$self->{path_strip}!! or
+                 die "Failed to strip path '$path' ($self->{path_strip})\n";
+       }
        $path;
 }
 
@@ -2249,25 +2252,52 @@ sub gs_do_update {
        my ($self, $rev_a, $rev_b, $path, $recurse, $editor) = @_;
        my $pool = SVN::Pool->new;
        $editor->set_path_strip($path);
-       my $reporter = $self->do_update($rev_b, $path, $recurse,
-                                       $editor, $pool);
+       my (@pc) = split m#/#, $path;
+       my $reporter = $self->do_update($rev_b, (@pc ? shift @pc : ''),
+                                       $recurse, $editor, $pool);
        my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
+
+       # Since we can't rely on svn_ra_reparent being available, we'll
+       # just have to do some magic with set_path to make it so
+       # we only want a partial path.
+       my $sp = '';
+       my $final = join('/', @pc);
+       while (@pc) {
+               $reporter->set_path($sp, $rev_b, 0, @lock, $pool);
+               $sp .= '/' if length $sp;
+               $sp .= shift @pc;
+       }
+       die "BUG: '$sp' != '$final'\n" if ($sp ne $final);
+
        my $new = ($rev_a == $rev_b);
-       $reporter->set_path('', $rev_a, $new, @lock, $pool);
+       $reporter->set_path($sp, $rev_a, $new, @lock, $pool);
+
        $reporter->finish_report($pool);
        $pool->clear;
        $editor->{git_commit_ok};
 }
 
+# this requires SVN 1.4.3 or later (do_switch didn't work before 1.4.3, and
+# svn_ra_reparent didn't work before 1.4)
 sub gs_do_switch {
        my ($self, $rev_a, $rev_b, $path, $recurse, $url_b, $editor) = @_;
        my $pool = SVN::Pool->new;
-       $editor->set_path_strip($path);
-       my $reporter = $self->do_switch($rev_b, $path, $recurse,
-                                       $url_b, $editor, $pool);
+
+       my $full_url = $self->{url};
+       my $old_url = $full_url;
+       $full_url .= "/$path" if length $path;
+       SVN::_Ra::svn_ra_reparent($self->{session}, $full_url, $pool);
+       $self->{url} = $full_url;
+
+       my $reporter = $self->do_switch($rev_b, '',
+                                       $recurse, $url_b, $editor, $pool);
        my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef) : ();
        $reporter->set_path('', $rev_a, 0, @lock, $pool);
        $reporter->finish_report($pool);
+
+       SVN::_Ra::svn_ra_reparent($self->{session}, $old_url, $pool);
+       $self->{url} = $old_url;
+
        $pool->clear;
        $editor->{git_commit_ok};
 }
index bfb718886f70689c3e586e431b6f109bb7e3e362..6d243f84887735c0ad82fe3a3134c04950629662 100755 (executable)
@@ -101,6 +101,37 @@ test_expect_success 'follow deleted directory' "
        test -z \"\`git ls-tree -z refs/remotes/glob\`\"
        "
 
+# ref: r9270 of the Subversion repository: (http://svn.collab.net/repos/svn)
+# in trunk/subversion/bindings/swig/perl
+test_expect_success '' "
+       mkdir -p import/trunk/subversion/bindings/swig/perl/t &&
+       for i in a b c ; do \
+         echo \$i > import/trunk/subversion/bindings/swig/perl/\$i.pm &&
+         echo _\$i > import/trunk/subversion/bindings/swig/perl/t/\$i.t; \
+       done &&
+         echo 'bad delete test' > \
+          import/trunk/subversion/bindings/swig/perl/t/larger-parent &&
+         echo 'bad delete test 2' > \
+          import/trunk/subversion/bindings/swig/perl/another-larger &&
+       cd import &&
+         svn import -m 'r9270 test' . $svnrepo/r9270 &&
+       cd .. &&
+       svn co $svnrepo/r9270/trunk/subversion/bindings/swig/perl r9270 &&
+       cd r9270 &&
+         svn mkdir native &&
+         svn mv t native/t &&
+         for i in a b c; do svn mv \$i.pm native/\$i.pm; done &&
+         echo z >> native/t/c.t &&
+         svn commit -m 'reorg test' &&
+       cd .. &&
+       git-svn init -i r9270-t \
+         $svnrepo/r9270/trunk/subversion/bindings/swig/perl/native/t &&
+       git-svn fetch -i r9270-t --follow-parent &&
+       test \`git rev-list r9270-t | wc -l\` -eq 2 &&
+       test \"\`git ls-tree --name-only r9270-t~1\`\" = \
+            \"\`git ls-tree --name-only r9270-t\`\"
+       "
+
 test_debug 'gitk --all &'
 
 test_done