summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgit-svn.perl196
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh6
2 files changed, 110 insertions, 92 deletions
diff --git a/git-svn.perl b/git-svn.perl
index 15d65e21e1..8d49959f37 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -790,6 +790,16 @@ sub ra {
$self->{ra} ||= Git::SVN::Ra->new($self->{url});
}
+sub rel_path {
+ my ($self) = @_;
+ my $repos_root = $self->ra->{repos_root};
+ return $self->{path} if ($self->{url} eq $repos_root);
+ my $url = $self->{url} .
+ (length $self->{path} ? "/$self->{path}" : $self->{path});
+ $url =~ s!^\Q$repos_root\E/*!!g;
+ $url;
+}
+
sub copy_remote_ref {
my ($self) = @_;
my $origin = $::_cp_remote ? $::_cp_remote : 'origin';
@@ -998,16 +1008,97 @@ sub do_git_commit {
return $commit;
}
+sub revisions_eq {
+ my ($self, $r0, $r1) = @_;
+ return 1 if $r0 == $r1;
+ my $nr = 0;
+ $self->ra->get_log([$self->{path}], $r0, $r1,
+ 0, 0, 1, sub { $nr++ });
+ return 0 if ($nr > 1);
+ return 1;
+}
+
+sub find_parent_branch {
+ my ($self, $paths, $rev) = @_;
+
+ # look for a parent from another branch:
+ my $i = $paths->{'/'.$self->rel_path} or return;
+ my $branch_from = $i->copyfrom_path or return;
+ my $r = $i->copyfrom_rev;
+ my $repos_root = $self->ra->{repos_root};
+ my $url = $self->ra->{url};
+ my $new_url = $repos_root . $branch_from;
+ 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 my $f (keys %$fetch) {
+ next if $f ne $branch_from;
+ $gs = Git::SVN->new($fetch->{$f}, $repo_id, $f);
+ last;
+ }
+ last if $gs;
+ }
+ unless ($gs) {
+ my $ref_id = $branch_from;
+ $ref_id .= "\@$r" if find_ref($ref_id);
+ # just grow a tail if we're not unique enough :x
+ $ref_id .= '-' while find_ref($ref_id);
+ $gs = Git::SVN->init($new_url, '', $ref_id, $ref_id);
+ }
+ my ($r0, $parent) = $gs->find_rev_before($r, 1);
+ if ($::_follow_parent && (!defined $r0 || !defined $parent)) {
+ foreach (0 .. $r) {
+ my $log_entry = eval { $gs->do_fetch(undef, $_) };
+ $gs->do_git_commit($log_entry) if $log_entry;
+ }
+ ($r0, $parent) = $gs->last_rev_commit;
+ }
+ if (defined $r0 && defined $parent && $gs->revisions_eq($r0, $r)) {
+ print STDERR "Found branch parent: ($self->{ref_id}) $parent\n";
+ command_noisy('read-tree', $parent);
+ my $ed;
+ if ($self->ra->can_do_switch) {
+ # do_switch works with svn/trunk >= r22312, but that
+ # is not included with SVN 1.4.2 (the latest version
+ # at the moment), so we can't rely on it
+ $self->{last_commit} = $parent;
+ $ed = SVN::Git::Fetcher->new($self);
+ $gs->ra->gs_do_switch($r0, $rev, $gs->{path}, 1,
+ $self->full_url, $ed)
+ or die "SVN connection failed somewhere...\n";
+ } else {
+ $ed = SVN::Git::Fetcher->new($self);
+ $self->ra->gs_do_update($rev, $rev, $self->{path},
+ 1, $ed)
+ or die "SVN connection failed somewhere...\n";
+ }
+ return $self->make_log_entry($rev, [$parent], $ed);
+ }
+ print STDERR "Branch parent not found...\n";
+ return undef;
+}
+
sub do_fetch {
my ($self, $paths, $rev) = @_;
- my $ed = SVN::Git::Fetcher->new($self);
+ my $ed;
my ($last_rev, @parents);
if ($self->{last_commit}) {
+ $ed = SVN::Git::Fetcher->new($self);
$last_rev = $self->{last_rev};
$ed->{c} = $self->{last_commit};
@parents = ($self->{last_commit});
} else {
$last_rev = $rev;
+ if (my $log_entry = $self->find_parent_branch($paths, $rev)) {
+ return $log_entry;
+ }
+ $ed = SVN::Git::Fetcher->new($self);
}
unless ($self->ra->gs_do_update($last_rev, $rev,
$self->{path}, 1, $ed)) {
@@ -1120,9 +1211,9 @@ sub fetch {
my @revs;
$self->ra->get_log([''], $min, $max, 0, 1, 1, sub {
my ($paths, $rev, $author, $date, $log) = @_;
- push @revs, $rev });
+ push @revs, [ $paths, $rev ] });
foreach (@revs) {
- my $log_entry = $self->do_fetch(undef, $_);
+ my $log_entry = $self->do_fetch(@$_);
$self->do_git_commit($log_entry, @parents);
}
last if $max >= $head;
@@ -1232,6 +1323,18 @@ sub rev_db_get {
$ret;
}
+sub find_rev_before {
+ my ($self, $rev, $eq_ok) = @_;
+ --$rev unless $eq_ok;
+ while ($rev > 0) {
+ if (my $c = $self->rev_db_get($rev)) {
+ return ($rev, $c);
+ }
+ --$rev;
+ }
+ return (undef, undef);
+}
+
sub _new {
my ($class, $repo_id, $ref_id, $path) = @_;
unless (defined $repo_id && length $repo_id) {
@@ -1398,93 +1501,6 @@ sub uri_decode {
$f
}
-sub revisions_eq {
- my ($path, $r0, $r1) = @_;
- return 1 if $r0 == $r1;
- my $nr = 0;
- # should be OK to use Pool here (r1 - r0) should be small
- $SVN->get_log([$path], $r0, $r1, 0, 0, 1, sub {$nr++});
- return 0 if ($nr > 1);
- return 1;
-}
-
-sub libsvn_find_parent_branch {
- my ($paths, $rev, $author, $date, $log) = @_;
- my $svn_path = '/'.$SVN->{svn_path};
-
- # look for a parent from another branch:
- my $i = $paths->{$svn_path} or return;
- my $branch_from = $i->copyfrom_path or return;
- my $r = $i->copyfrom_rev;
- print STDERR "Found possible branch point: ",
- "$branch_from => $svn_path, $r\n";
- $branch_from =~ s#^/##;
- my $l_map = {};
- read_url_paths_all($l_map, '', "$GIT_DIR/svn");
- my $url = $SVN->{repos_root};
- defined $l_map->{$url} or return;
- my $id = $l_map->{$url}->{$branch_from};
- if (!defined $id && $_follow_parent) {
- print STDERR "Following parent: $branch_from\@$r\n";
- # auto create a new branch and follow it
- $id = basename($branch_from);
- $id .= '@'.$r if -r "$GIT_DIR/svn/$id";
- while (-r "$GIT_DIR/svn/$id") {
- # just grow a tail if we're not unique enough :x
- $id .= '-';
- }
- }
- return unless defined $id;
-
- my ($r0, $parent) = find_rev_before($r,$id,1);
- if ($_follow_parent && (!defined $r0 || !defined $parent)) {
- defined(my $pid = fork) or croak $!;
- if (!$pid) {
- $GIT_SVN = $ENV{GIT_SVN_ID} = $id;
- init_vars();
- $SVN_URL = "$url/$branch_from";
- $SVN = undef;
- setup_git_svn();
- # we can't assume SVN_URL exists at r+1:
- $_revision = "0:$r";
- fetch_lib();
- exit 0;
- }
- waitpid $pid, 0;
- croak $? if $?;
- ($r0, $parent) = find_rev_before($r,$id,1);
- }
- return unless (defined $r0 && defined $parent);
- if (revisions_eq($branch_from, $r0, $r)) {
- unlink $GIT_SVN_INDEX;
- print STDERR "Found branch parent: ($GIT_SVN) $parent\n";
- command_noisy('read-tree', $parent);
- unless ($SVN->can_do_switch) {
- return _libsvn_new_tree($paths, $rev, $author, $date,
- $log, [$parent]);
- }
- # do_switch works with svn/trunk >= r22312, but that is not
- # included with SVN 1.4.2 (the latest version at the moment),
- # so we can't rely on it.
- my $ra = Git::SVN::Ra->new("$url/$branch_from");
- my $ed = SVN::Git::Fetcher->new({c => $parent, q => $_q });
- $ra->gs_do_switch($r0, $rev, '', 1, $SVN->{url}, $ed) or
- die "SVN connection failed somewhere...\n";
- return libsvn_log_entry($rev, $author, $date, $log, [$parent]);
- }
- print STDERR "Nope, branch point not imported or unknown\n";
- return undef;
-}
-
-sub _libsvn_new_tree {
- my ($paths, $rev, $author, $date, $log, $parents) = @_;
- my $ed = SVN::Git::Fetcher->new({q => $_q});
- unless ($SVN->gs_do_update($rev, $rev, '', 1, $ed)) {
- die "SVN connection failed somewhere...\n";
- }
- libsvn_log_entry($rev, $author, $date, $log, $parents, $ed);
-}
-
{
my $kill_stupid_warnings = $SVN::Node::none.$SVN::Node::file.
$SVN::Node::dir.$SVN::Node::unknown.
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 405b555368..91fdfe964e 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -30,8 +30,10 @@ test_expect_success 'initialize repo' "
test_expect_success 'init and fetch --follow-parent a moved directory' "
git-svn init -i thunk $svnrepo/thunk &&
git-svn fetch --follow-parent -i thunk &&
- git-rev-parse --verify refs/remotes/trunk &&
- test '$?' -eq '0'
+ test \"\`git-rev-parse --verify refs/remotes/trunk\`\" \
+ = \"\`git-rev-parse --verify refs/remotes/thunk~1\`\" &&
+ test \"\`git-cat-file blob refs/remotes/thunk:readme |\
+ sed -n -e '3p'\`\" = goodbye
"
test_debug 'gitk --all &'