[svk-commit] r2693 - in trunk: . lib/SVK/Command lib/SVK/Editor lib/SVK/Mirror/Backend t/mirror

nobody at bestpractical.com nobody at bestpractical.com
Fri Feb 8 04:10:41 EST 2008


Author: clkao
Date: Fri Feb  8 04:10:40 2008
New Revision: 2693

Added:
   trunk/lib/SVK/Editor/FilterProp.pm
   trunk/t/mirror/sync-rebase-delta.t
   trunk/t/mirror/sync-rebase-multi.t
   trunk/t/mirror/sync-rebase.t
Modified:
   trunk/   (props changed)
   trunk/MANIFEST
   trunk/lib/SVK/Command/Sync.pm
   trunk/lib/SVK/Editor/Composite.pm
   trunk/lib/SVK/Mirror.pm
   trunk/lib/SVK/Mirror/Backend/SVNRa.pm
   trunk/lib/SVK/Mirror/Backend/SVNRaPipe.pm

Log:
Support mirroring source that the anchor or parent was
renamed.

 r4412 at mtl:  clkao | 2008-02-07 21:23:23 +0800
 - Create branch sync-rebase
 r4413 at mtl:  clkao | 2008-02-08 01:21:17 +0800
 first cut of sync following anchor copy support.
 
 r4414 at mtl:  clkao | 2008-02-08 02:41:01 +0800
 make follow-anchor-copy not defualt.
 r4415 at mtl:  clkao | 2008-02-08 04:29:38 +0800
 rebase - deal with additional changes on the revision of rebase.
 
 r4420 at mtl:  clkao | 2008-02-08 14:43:31 +0800
 filterprop editor.
 r4421 at mtl:  clkao | 2008-02-08 14:44:17 +0800
 in composite editor, use exists rather than defined to
 see if anchor_baton is given.
 r4422 at mtl:  clkao | 2008-02-08 14:48:06 +0800
 new tests.
 r4423 at mtl:  clkao | 2008-02-08 16:43:14 +0800
 refactor _sync_edge_changeset.
 r4424 at mtl:  clkao | 2008-02-08 17:03:46 +0800
 make mutliple rebase and rebase at parent dir work.
 


Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST	(original)
+++ trunk/MANIFEST	Fri Feb  8 04:10:40 2008
@@ -76,6 +76,7 @@
 lib/SVK/Editor/Delay.pm
 lib/SVK/Editor/Diff.pm
 lib/SVK/Editor/Dynamic.pm
+lib/SVK/Editor/FilterProp.pm
 lib/SVK/Editor/InteractiveCommitter.pm
 lib/SVK/Editor/InteractiveStatus.pm
 lib/SVK/Editor/MapRev.pm
@@ -307,6 +308,9 @@
 t/mirror/sync-empty.t
 t/mirror/sync-escape.t
 t/mirror/sync-failed-hook.t
+t/mirror/sync-rebase-delta.t
+t/mirror/sync-rebase-mutli.t
+t/mirror/sync-rebase.t
 t/mirror/sync-replaced-nocopy.t
 t/mirror/sync-replaced.t
 t/mirror/sync-replicate.t

Modified: trunk/lib/SVK/Command/Sync.pm
==============================================================================
--- trunk/lib/SVK/Command/Sync.pm	(original)
+++ trunk/lib/SVK/Command/Sync.pm	Fri Feb  8 04:10:40 2008
@@ -59,6 +59,7 @@
 sub options {
     ('s|skipto=s'	=> 'skip_to',
      'a|all'		=> 'sync_all',
+     'follow-anchor-copy' => 'follow_anchor_copy',
      't|torev=s'	=> 'torev');
 }
 
@@ -87,7 +88,7 @@
                 $logger->error(loc( "%1 does not contain a valid depotname",
                     $orig_arg ));
                 next;
-            }
+           }
 
             my @tempnewarg = grep { SVK::Path->_to_pclass( "/$path", 'Unix' )->subsumes($_) }
                 $depot->mirror->entries;
@@ -104,6 +105,9 @@
 
     my $error;
     for my $m (@mirrors) {
+        # XXX: in svk::mirrorcatalog, mirror objects are cached. we
+        # might want per-instance options applied when using ->get.
+        $m->follow_anchor_copy(1) if $self->{follow_anchor_copy};
 	my $run_sync = sub {
 	    $m->sync_snapshot($self->{skip_to}) if $self->{skip_to};
 	    $m->run( $self->{torev} );

Modified: trunk/lib/SVK/Editor/Composite.pm
==============================================================================
--- trunk/lib/SVK/Editor/Composite.pm	(original)
+++ trunk/lib/SVK/Editor/Composite.pm	Fri Feb  8 04:10:40 2008
@@ -53,6 +53,8 @@
 use SVK::Version;  our $VERSION = $SVK::VERSION;
 use base 'SVK::Editor';
 
+__PACKAGE__->mk_accessors(qw(master_editor));
+
 =head1 NAME
 
 SVK::Editor::Composite - composite editor
@@ -75,11 +77,14 @@
     $func =~ s/^.*:://;
     return if $func =~ m/^[A-Z]/;
 
-    if ($func =~ m/^(?:add|open|delete)/) {
+    if ($func eq 'open_root') {
+    }
+    elsif ($func =~ m/^(?:add|open|delete)/) {
 	return $self->{target_baton}
 	    if defined $self->{target} && $arg[0] eq $self->{target};
 	$arg[0] = length $arg[0] ?
-	    "$self->{anchor}/$arg[0]" : $self->{anchor};
+	    "$self->{anchor}/$arg[0]" : $self->{anchor}
+                if defined $self->{anchor};
     }
     elsif ($func =~ m/^close_(?:file|directory)/) {
 	if (defined $arg[0]) {
@@ -90,14 +95,16 @@
 	}
     }
 
-    $self->{master_editor}->$func(@arg);
+    $self->master_editor->$func(@arg);
 }
 
 sub set_target_revision {}
 
 sub open_root {
-    my ($self, $base_revision) = @_;
-    return $self->{anchor_baton};
+    my $self = shift;
+    return $self->{anchor_baton} if exists $self->{anchor_baton};
+
+    return $self->master_editor->open_root(@_)
 }
 
 sub close_edit {}

Added: trunk/lib/SVK/Editor/FilterProp.pm
==============================================================================
--- (empty file)
+++ trunk/lib/SVK/Editor/FilterProp.pm	Fri Feb  8 04:10:40 2008
@@ -0,0 +1,74 @@
+# BEGIN BPS TAGGED BLOCK {{{
+# COPYRIGHT:
+# 
+# This software is Copyright (c) 2003-2006 Best Practical Solutions, LLC
+#                                          <clkao at bestpractical.com>
+# 
+# (Except where explicitly superseded by other copyright notices)
+# 
+# 
+# LICENSE:
+# 
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of either:
+# 
+#   a) Version 2 of the GNU General Public License.  You should have
+#      received a copy of the GNU General Public License along with this
+#      program.  If not, write to the Free Software Foundation, Inc., 51
+#      Franklin Street, Fifth Floor, Boston, MA 02110-1301 or visit
+#      their web page on the internet at
+#      http://www.gnu.org/copyleft/gpl.html.
+# 
+#   b) Version 1 of Perl's "Artistic License".  You should have received
+#      a copy of the Artistic License with this package, in the file
+#      named "ARTISTIC".  The license is also available at
+#      http://opensource.org/licenses/artistic-license.php.
+# 
+# This work is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+# 
+# CONTRIBUTION SUBMISSION POLICY:
+# 
+# (The following paragraph is not intended to limit the rights granted
+# to you to modify and distribute this software under the terms of the
+# GNU General Public License and is only of importance to you if you
+# choose to contribute your changes and enhancements to the community
+# by submitting them to Best Practical Solutions, LLC.)
+# 
+# By intentionally submitting any modifications, corrections or
+# derivatives to this work, or any other work intended for use with SVK,
+# to Best Practical Solutions, LLC, you confirm that you are the
+# copyright holder for those contributions and you grant Best Practical
+# Solutions, LLC a nonexclusive, worldwide, irrevocable, royalty-free,
+# perpetual, license to use, copy, create derivative works based on
+# those contributions, and sublicense and distribute those contributions
+# and any derivatives thereof.
+# 
+# END BPS TAGGED BLOCK }}}
+package SVK::Editor::FilterProp;
+use strict;
+use warnings;
+
+use base 'SVK::Editor::ByPass';
+
+__PACKAGE__->mk_accessors(qw(cb_prop));
+
+sub AUTOLOAD {
+    my ($self, @arg) = @_;
+    my $func = our $AUTOLOAD;
+    $func =~ s/^.*:://;
+    return if $func =~ m/^[A-Z]+$/;
+
+    if ($func =~ m/^change.*prop/) {
+        $self->cb_prop->(@arg[1,2]) or return;
+        warn "==> $arg[1]";
+    }
+
+    $func = "SUPER::$func";
+    $self->$func(@arg);
+}
+
+1;

Modified: trunk/lib/SVK/Mirror.pm
==============================================================================
--- trunk/lib/SVK/Mirror.pm	(original)
+++ trunk/lib/SVK/Mirror.pm	Fri Feb  8 04:10:40 2008
@@ -61,7 +61,7 @@
 
 use base 'Class::Accessor::Fast';
 
-__PACKAGE__->mk_accessors(qw(depot path server_uuid source_uuid pool url _backend _locked));
+__PACKAGE__->mk_accessors(qw(depot path server_uuid source_uuid pool url _backend _locked follow_anchor_copy));
 
 *repos = sub { Carp::cluck unless $_[0]->depot; shift->depot->repos };
 

Modified: trunk/lib/SVK/Mirror/Backend/SVNRa.pm
==============================================================================
--- trunk/lib/SVK/Mirror/Backend/SVNRa.pm	(original)
+++ trunk/lib/SVK/Mirror/Backend/SVNRa.pm	Fri Feb  8 04:10:40 2008
@@ -60,6 +60,8 @@
 use SVK::Editor::MapRev;
 use SVK::Util 'IS_WIN32';
 use SVK::Logger;
+use SVK::Editor::FilterProp;
+use SVK::Editor::Composite;
 
 use Class::Autouse qw(SVK::Editor::SubTree SVK::Editor::CopyHandler SVK::Editor::Translate);
 
@@ -94,7 +96,7 @@
 sub new {
     my ( $class, $args ) = @_;
     unless ( defined $args->{use_pipeline} ) {
-        $args->{use_pipeline} = IS_WIN32 ? 0 : 1;
+        $args->{use_pipeline} = 0;#IS_WIN32 ? 0 : 1;
     }
     return $class->SUPER::new($args);
 }
@@ -452,7 +454,7 @@
 =cut
 
 sub traverse_new_changesets {
-    my ($self, $code, $torev) = @_;
+    my ($self, $code, $torev, $cross, $want_paths) = @_;
     $self->refresh;
     my $from = ($self->fromrev || 0)+1;
     my $to = defined $torev ? $torev : -1;
@@ -462,11 +464,11 @@
     return if $from > $to;
     $logger->info( "Retrieving log information from $from to $to");
     eval {
-    $ra->get_log([''], $from, $to, 0,
-		  0, 1,
+        $ra->get_log([''], $from, $to, 0,
+		  $want_paths, !$cross,
 		  sub {
 		      my ($paths, $rev, $author, $date, $msg, $pool) = @_;
-		      $code->($rev, { author => $author, date => $date, message => $msg });
+		      $code->($rev, { author => $author, date => $date, message => $msg }, $paths);
 		  });
     };
     $self->_ra_finished($ra);
@@ -478,7 +480,7 @@
 =cut
 
 sub sync_changeset {
-    my ( $self, $changeset, $metadata, $ra, $extra_prop, $callback ) = @_;
+    my ( $self, $changeset, $metadata, $extra_prop, $callback, $delta_generator, $translate_from ) = @_;
     my $t = $self->mirror->get_svkpath;
     my ( $editor, undef, %opt ) = $t->get_editor(
         ignore_mirror => 1,
@@ -497,9 +499,9 @@
     }
     $self->_revmap_prop( $opt{txn}, $changeset );
 
-    $editor = $self->_get_sync_editor($editor, $changeset);
-    $ra->replay( $changeset, 0, 1, $editor );
-    $self->_after_replay($ra, $editor);
+    $editor = $self->_get_sync_editor($editor, $changeset, $translate_from);
+
+    $delta_generator->( $editor, $opt{txn});
 
     return;
 
@@ -525,7 +527,7 @@
 }
 
 sub _get_sync_editor {
-    my ($self, $oeditor, $changeset) = @_;
+    my ($self, $oeditor, $changeset, $translate_from) = @_;
 
     my $editor = SVK::Editor::CopyHandler->new(
         _editor => $oeditor,
@@ -533,7 +535,8 @@
             my ( undef, $path, $rev, $current_path, $pb ) = @_;
             return ( $path, $rev ) if $rev == -1;
             my $source_path = $self->source_path;
-            $path =~ s/^\Q$self->{source_path}//;
+            my $copy_prefix = $translate_from || $self->source_path;
+            $path =~ s/^\Q$copy_prefix//;
 	    my $lrev = $self->find_rev_from_changeset($rev, 1);
 	    if ($lrev == -1) {
 		# vivify the copy that we don't have
@@ -567,8 +570,8 @@
     # ra->replay gives us editor calls based on repos root not
     # base uri, so we need to get the correct subtree.
     my $baton;
-    if ( length $self->source_path ) {
-        my $anchor = substr( $self->source_path, 1 );
+    if ( $translate_from || length $self->source_path ) {
+        my $anchor = substr( $translate_from || $self->source_path, 1 );
         $baton  = $editor->open_root(-1);      # XXX: should use $t->revision
         $editor = SVK::Editor::SubTree->new(
             {   master_editor => $editor,
@@ -577,6 +580,7 @@
             }
         );
     }
+
     return $editor;
 }
 
@@ -594,30 +598,134 @@
     my ( $self, $torev, $callback, $fake_last ) = @_;
     $self->mirror->with_lock(
         'mirror',
-        sub { $self->_mirror_changesets( $torev, $callback, $fake_last ) } );
+        sub { $self->refresh;
+              $self->_mirror_changesets( $torev, $callback, $fake_last ) } );
+}
+
+sub _sync_edge_changeset {
+    my ($self, $revdata, $callback, $translate_from) = @_;
+
+    my $paths = $revdata->[2];
+    unless ($paths) {
+        my $ra = $self->_new_ra;
+
+        $ra->get_log([''], $revdata->[0], $revdata->[0], 0,
+                     1, 1, sub { $paths = shift; } );
+        $self->_ra_finished($ra);
+    }
+
+    my ($entry, $old_path, $old_rev) = $self->_find_edge_entry( $paths, $translate_from || $self->source_path ) or return;
+
+    $self->_mirror_changesets( $revdata->[0], $callback, 0, $old_path );
+
+    my $ra = $self->_new_ra;
+    $ra->reparent( $self->source_root . $entry->copyfrom_path );
+
+    $self->sync_changeset
+        ( $revdata->[0], $revdata->[1], {},
+          $callback,
+          sub {
+              my ($editor, $txn) = @_;
+              $editor = $editor->master_editor;
+              $editor = SVK::Editor::Composite->new( { master_editor => $editor } );
+              $editor = SVK::Editor::FilterProp->new
+                  ( { cb_prop => sub { return $_[0] !~ m/^svn:(wc|entry)/; },
+                      _editor => [ $editor ] } );
+              my $report = $ra->do_diff($revdata->[0], '', 1, 1, $self->source_root.$self->source_path, $editor);
+              $report->set_path('', $entry->copyfrom_rev, 0, undef );
+              $report->finish_report;
+              if ( %{$txn->root->paths_changed} ) {
+                  $editor->master_editor->close_edit;
+              }
+ }) ;
+
+}
+
+sub _find_edge_entry {
+    my ($self, $paths, $translate_from) = @_;
+
+    for (reverse sort keys %$paths) {
+        if (Path::Class::Dir->new_foreign("Unix", $_)
+            ->subsumes($translate_from)) {
+            my $entry = $paths->{$_};
+            if ($entry->action eq 'A' && $entry->copyfrom_path) {
+                return ($entry,
+                        SVK::Util::abs2rel($translate_from, $_ => $entry->copyfrom_path),
+                        $entry->copyfrom_rev);
+            }
+        }
+    }
+    return;
 }
 
 sub _mirror_changesets {
-    my ( $self, $torev, $callback, $fake_last ) = @_;
-    $self->refresh;
+    my ( $self, $torev, $callback, $fake_last, $translate_from ) = @_;
     my @revs;
-    $self->traverse_new_changesets( sub { push @revs, [@_] unless $fake_last && $torev && $_[0] == $torev}, $torev );
+    my $cross = $translate_from ? 1 : 0;
+    $self->traverse_new_changesets( sub { push @revs, [@_] unless $fake_last && $torev && $_[0] == $torev}, $torev, $cross, $cross );
+
+    # the last revision belongs to our caller, so don't sync it.
+    pop @revs if $cross;
+
     return unless @revs;
+    if ($cross) {
+        # if we are in cross mode, our @revs might already contain
+        # renames that we need to segment with different
+        # translate_from
+        my $tmp_translate_from = $translate_from;
+        my (@batch, @newrev);
+        for (reverse @revs) {
+            my $paths = $_->[-1];
+            my ($entry, $old_path, $oldrev) = $self->_find_edge_entry($paths, $tmp_translate_from || $self->source_path);
+            unless ($entry) {
+                unshift @newrev, $_;
+                next;
+            }
+
+            unshift @batch, [\@newrev, $_, $old_path];
+            @newrev = ();
+            $tmp_translate_from = $old_path;
+        }
+        for (@batch) {
+            my ($revs, $edge, $t) = @$_;
+            $self->_sync_changesets($callback, $revs, $t);
+            $self->_sync_edge_changeset($edge, $callback, $_);
+        }
+
+    }
+    # get the first revision and see if it's renamed from somewhere else
+
+    if ($self->mirror->follow_anchor_copy && !$cross) {
+        $self->_sync_edge_changeset(shift @revs, $callback, $translate_from);
+    }
+
+    $self->_sync_changesets($callback, \@revs, $translate_from);
+
+}
+sub _sync_changesets {
+    my ($self, $callback, $revs, $translate_from) = @_;
 
     # prepare generator for pipelined ra
     my @gen;
     # XXX: this is so wrong
     my $revprop = $self->mirror->depot->mirror->revprop;
+
     my $ra = $self->_new_ra;
+
+    $ra->reparent( $translate_from
+                   ? $self->source_root . $translate_from 
+                   : $self->mirror->url)
+        if $translate_from;
+
     if ( $self->use_pipeline ) {
-        for (@revs) {
+        for (@$revs) {
             push @gen, [ 'rev_proplist', $_->[0] ] if $revprop;
             push @gen, [ 'replay', $_->[0], 0, 1, 'EDITOR' ];
         }
         $ra = SVK::Mirror::Backend::SVNRaPipe->new( $ra, sub { shift @gen } );
     }
     my $pool = SVN::Pool->new_default;
-    for (@revs) {
+    for (@$revs) {
         $pool->clear;
         my ( $changeset, $metadata ) = @$_;
         my $extra_prop = {};
@@ -628,8 +736,12 @@
                     if exists $prop->{$_};
             }
         }
-        $self->sync_changeset( $changeset, $metadata, $ra, $extra_prop,
-            $callback );
+        $self->sync_changeset( $changeset, $metadata, $extra_prop,
+            $callback, sub {
+                my $editor = shift;
+                $ra->replay( $changeset, 0, 1, $editor );
+                $self->_after_replay($ra, $editor);
+            }, $translate_from );
     }
     $self->_ra_finished($ra);
 }

Modified: trunk/lib/SVK/Mirror/Backend/SVNRaPipe.pm
==============================================================================
--- trunk/lib/SVK/Mirror/Backend/SVNRaPipe.pm	(original)
+++ trunk/lib/SVK/Mirror/Backend/SVNRaPipe.pm	Fri Feb  8 04:10:40 2008
@@ -241,6 +241,14 @@
     return $data->[0];
 }
 
+sub reparent {
+    my $self = shift;
+    $self->ensure_client_cmd('reparent', @_);
+    # read synchronous msg
+    my $data = thaw( ${$self->read_msg} );
+    die 'inconsistent response' unless $data->[1] eq 'reparent';
+    return $data->[0];
+}
 
 sub replay {
     my $self = shift;

Added: trunk/t/mirror/sync-rebase-delta.t
==============================================================================
--- (empty file)
+++ trunk/t/mirror/sync-rebase-delta.t	Fri Feb  8 04:10:40 2008
@@ -0,0 +1,52 @@
+#!/usr/bin/perl -w
+use strict;
+use Test::More;
+use SVK::Test;
+eval { require SVN::Mirror; 1 } or plan skip_all => 'require SVN::Mirror';
+plan tests => 5;
+
+my ($xd, $svk) = build_test('test');
+
+our $output;
+
+$svk->mkdir(-pm => 'init', '/test/foo/proj/trunk');
+$svk->mkdir(-pm => 'init', '/test/foo/proj/branches');
+my $tree = create_basic_tree ($xd, '/test/foo/proj/trunk');
+
+$svk->cp('-m' => 'branch', '/test/foo/proj/trunk' => '/test/foo/proj/branches/branchA');
+
+my ($copath, $corpath) = get_copath ('sync-rebase-delta');
+
+$svk->checkout ('/test/', $copath);
+$svk->mv("$copath/foo/proj" => "$copath/proj");
+$svk->mkdir("$copath/proj/lalala");
+# XXX: after the commit there are stalled sticky entries in checkout
+# DH, please check.
+is_output($svk, 'ci', [-m => 'mv but with some modification', $copath],
+          [ "Committed revision 6." ]);
+
+$svk->cp(-m => 'branch2', '/test/proj/trunk' => '/test/proj/branches/branchB');
+
+my ($srepospath, $spath, $srepos) = $xd->find_repos ('/test/proj', 1);
+my $uri = uri($srepospath.($spath eq '/' ? '' : $spath));
+
+
+$svk->mi('//mirror/proj', $uri);
+
+is_output($svk, 'sync', ['--follow-anchor-copy','//mirror/proj'],
+          ["Syncing $uri",
+           'Retrieving log information from 1 to 7',
+           'Retrieving log information from 1 to 6',
+           'Committed revision 2 from revision 1.',
+           'Committed revision 3 from revision 2.',
+           'Committed revision 4 from revision 3.',
+           'Committed revision 5 from revision 4.',
+           'Committed revision 6 from revision 5.',
+           'Committed revision 7 from revision 6.',
+           'Committed revision 8 from revision 7.']);
+
+is_output($svk, 'ls', ['//mirror/proj'],
+          ['branches/', 'lalala/', 'trunk/'] );
+is_ancestor($svk, "//mirror/proj/branches/branchA", '/mirror/proj/trunk', 5);
+is_ancestor($svk, "//mirror/proj/branches/branchB", '/mirror/proj/trunk', 5);
+

Added: trunk/t/mirror/sync-rebase-multi.t
==============================================================================
--- (empty file)
+++ trunk/t/mirror/sync-rebase-multi.t	Fri Feb  8 04:10:40 2008
@@ -0,0 +1,49 @@
+#!/usr/bin/perl -w
+use strict;
+use Test::More;
+use SVK::Test;
+eval { require SVN::Mirror; 1 } or plan skip_all => 'require SVN::Mirror';
+plan tests => 4;
+
+my ($xd, $svk) = build_test('test');
+
+our $output;
+
+$svk->mkdir(-pm => 'init', '/test/foo/bar/proj/trunk');
+$svk->mkdir(-pm => 'init', '/test/foo/bar/proj/branches');
+my $tree = create_basic_tree ($xd, '/test/foo/bar/proj/trunk');
+
+$svk->cp('-m' => 'branch', '/test/foo/bar/proj/trunk' => '/test/foo/bar/proj/branches/branchA');
+
+my ($copath, $corpath) = get_copath ('sync-rebase-delta');
+
+$svk->mv(-m => 'move it', '/test/foo' => '/test/blah');
+
+$svk->cp(-m => 'branch2', '/test/blah/bar/proj/trunk' => '/test/blah/bar/proj/branches/branchB');
+
+$svk->mv(-m => 'move it', '/test/blah/bar/proj' => '/test/proj');
+
+$svk->cp(-m => 'branch3', '/test/proj/trunk' => '/test/proj/branches/branchC');
+
+my ($srepospath, $spath, $srepos) = $xd->find_repos ('/test/proj', 1);
+my $uri = uri($srepospath.($spath eq '/' ? '' : $spath));
+
+
+$svk->mi('//mirror/proj', $uri);
+
+is_output($svk, 'sync', ['--follow-anchor-copy','//mirror/proj'],
+          ["Syncing $uri",
+           'Retrieving log information from 1 to 9',
+           'Retrieving log information from 1 to 8',
+           'Committed revision 2 from revision 1.',
+           'Committed revision 3 from revision 2.',
+           'Committed revision 4 from revision 3.',
+           'Committed revision 5 from revision 4.',
+           'Committed revision 6 from revision 5.',
+           'Committed revision 7 from revision 7.',
+           'Committed revision 8 from revision 9.']);
+
+is_ancestor($svk, "//mirror/proj/branches/branchA", '/mirror/proj/trunk', 5);
+is_ancestor($svk, "//mirror/proj/branches/branchB", '/mirror/proj/trunk', 5);
+is_ancestor($svk, "//mirror/proj/branches/branchC", '/mirror/proj/trunk', 5);
+

Added: trunk/t/mirror/sync-rebase.t
==============================================================================
--- (empty file)
+++ trunk/t/mirror/sync-rebase.t	Fri Feb  8 04:10:40 2008
@@ -0,0 +1,42 @@
+#!/usr/bin/perl -w
+use strict;
+use Test::More;
+use SVK::Test;
+eval { require SVN::Mirror; 1 } or plan skip_all => 'require SVN::Mirror';
+plan tests => 3;
+
+my ($xd, $svk) = build_test('test');
+
+our $output;
+
+$svk->mkdir(-pm => 'init', '/test/foo/proj/trunk');
+$svk->mkdir(-pm => 'init', '/test/foo/proj/branches');
+my $tree = create_basic_tree ($xd, '/test/foo/proj/trunk');
+
+$svk->cp('-m' => 'branch', '/test/foo/proj/trunk' => '/test/foo/proj/branches/branchA');
+
+$svk->mv(-m => 'relocate proj base', '/test/foo/proj' => '/test/proj');
+
+$svk->cp(-m => 'branch2', '/test/proj/trunk' => '/test/proj/branches/branchB');
+
+my ($srepospath, $spath, $srepos) = $xd->find_repos ('/test/proj', 1);
+my $uri = uri($srepospath.($spath eq '/' ? '' : $spath));
+
+
+$svk->mi('//mirror/proj', $uri);
+
+is_output($svk, 'sync', ['--follow-anchor-copy','//mirror/proj'],
+          ["Syncing $uri",
+           'Retrieving log information from 1 to 7',
+           'Retrieving log information from 1 to 6',
+           'Committed revision 2 from revision 1.',
+           'Committed revision 3 from revision 2.',
+           'Committed revision 4 from revision 3.',
+           'Committed revision 5 from revision 4.',
+           'Committed revision 6 from revision 5.',
+           'Committed revision 7 from revision 7.']);
+
+
+is_ancestor($svk, "//mirror/proj/branches/branchA", '/mirror/proj/trunk', 5);
+is_ancestor($svk, "//mirror/proj/branches/branchB", '/mirror/proj/trunk', 5);
+


More information about the svk-commit mailing list