git-svn: fix --follow-parent to work with Git::SVN
While we're at it, beef up the test because I was getting false-passes during development. Signed-off-by: Eric Wong <normalperson@yhbt.net>
This commit is contained in:
parent
47e39c55c9
commit
15710b6f34
196
git-svn.perl
196
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.
|
||||
|
@ -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 &'
|
||||
|
Loading…
Reference in New Issue
Block a user