Skip to content

Commit 747fa12

Browse files
Eric WongJunio C Hamano
authored andcommitted
git-svn: correctly access repos when only given partial read permissions
Sometimes users are given only read access to a subtree inside a repository, and git-svn could not read log information (and thus fetch commits) when connecting a session to the root of the repository. We now start an SVN::Ra session with the full URL of what we're tracking, and not the repository root as before. This change was made much easier with a cleanup of repo_path_split() usage as well as improving the accounting of authentication batons. Signed-off-by: Eric Wong <normalperson@yhbt.net> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 67affd5 commit 747fa12

File tree

1 file changed

+65
-72
lines changed

1 file changed

+65
-72
lines changed

git-svn.perl

Lines changed: 65 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
memoize('cmt_metadata');
4040
memoize('get_commit_time');
4141

42-
my ($SVN_PATH, $SVN, $SVN_LOG, $_use_lib, $AUTH_BATON, $AUTH_CALLBACKS);
42+
my ($SVN, $_use_lib);
4343

4444
sub nag_lib {
4545
print STDERR <<EOF;
@@ -381,10 +381,7 @@ sub fetch_cmd {
381381
sub fetch_lib {
382382
my (@parents) = @_;
383383
$SVN_URL ||= file_to_s("$GIT_SVN_DIR/info/url");
384-
my $repo;
385-
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
386-
$SVN_LOG ||= libsvn_connect($repo);
387-
$SVN ||= libsvn_connect($repo);
384+
$SVN ||= libsvn_connect($SVN_URL);
388385
my ($last_rev, $last_commit) = svn_grab_base_rev();
389386
my ($base, $head) = libsvn_parse_revision($last_rev);
390387
if ($base > $head) {
@@ -426,7 +423,7 @@ sub fetch_lib {
426423
# performance sucks with it enabled, so it's much
427424
# faster to fetch revision ranges instead of relying
428425
# on the limiter.
429-
libsvn_get_log($SVN_LOG, '/'.$SVN_PATH,
426+
libsvn_get_log(libsvn_dup_ra($SVN), [''],
430427
$min, $max, 0, 1, 1,
431428
sub {
432429
my $log_msg;
@@ -528,7 +525,6 @@ sub commit_lib {
528525
my $commit_msg = "$GIT_SVN_DIR/.svn-commit.tmp.$$";
529526

530527
my $repo;
531-
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
532528
set_svn_commit_env();
533529
foreach my $c (@revs) {
534530
my $log_msg = get_commit_message($c, $commit_msg);
@@ -537,13 +533,11 @@ sub commit_lib {
537533
# can't track down... (it's probably in the SVN code)
538534
defined(my $pid = open my $fh, '-|') or croak $!;
539535
if (!$pid) {
540-
$SVN_LOG = libsvn_connect($repo);
541-
$SVN = libsvn_connect($repo);
542536
my $ed = SVN::Git::Editor->new(
543537
{ r => $r_last,
544-
ra => $SVN_LOG,
538+
ra => libsvn_dup_ra($SVN),
545539
c => $c,
546-
svn_path => $SVN_PATH
540+
svn_path => $SVN->{svn_path},
547541
},
548542
$SVN->get_commit_editor(
549543
$log_msg->{msg},
@@ -661,10 +655,9 @@ sub show_ignore_cmd {
661655

662656
sub show_ignore_lib {
663657
my $repo;
664-
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
665-
$SVN ||= libsvn_connect($repo);
658+
$SVN ||= libsvn_connect($SVN_URL);
666659
my $r = defined $_revision ? $_revision : $SVN->get_latest_revnum;
667-
libsvn_traverse_ignore(\*STDOUT, $SVN_PATH, $r);
660+
libsvn_traverse_ignore(\*STDOUT, $SVN->{svn_path}, $r);
668661
}
669662

670663
sub graft_branches {
@@ -790,7 +783,7 @@ sub show_log {
790783
} elsif (/^:\d{6} \d{6} $sha1_short/o) {
791784
push @{$c->{raw}}, $_;
792785
} elsif (/^[ACRMDT]\t/) {
793-
# we could add $SVN_PATH here, but that requires
786+
# we could add $SVN->{svn_path} here, but that requires
794787
# remote access at the moment (repo_path_split)...
795788
s#^([ACRMDT])\t# $1 #;
796789
push @{$c->{changed}}, $_;
@@ -856,10 +849,7 @@ sub commit_diff {
856849
$_message ||= get_commit_message($tb,
857850
"$GIT_DIR/.svn-commit.tmp.$$")->{msg};
858851
}
859-
my $repo;
860-
($repo, $SVN_PATH) = repo_path_split($SVN_URL);
861-
$SVN_LOG ||= libsvn_connect($repo);
862-
$SVN ||= libsvn_connect($repo);
852+
$SVN ||= libsvn_connect($SVN_URL);
863853
if ($r eq 'HEAD') {
864854
$r = $SVN->get_latest_revnum;
865855
} elsif ($r !~ /^\d+$/) {
@@ -868,8 +858,9 @@ sub commit_diff {
868858
my @lock = $SVN::Core::VERSION ge '1.2.0' ? (undef, 0) : ();
869859
my $rev_committed;
870860
my $ed = SVN::Git::Editor->new({ r => $r,
871-
ra => $SVN_LOG, c => $tb,
872-
svn_path => $SVN_PATH
861+
ra => libsvn_dup_ra($SVN),
862+
c => $tb,
863+
svn_path => $SVN->{svn_path}
873864
},
874865
$SVN->get_commit_editor($_message,
875866
sub {
@@ -1147,8 +1138,7 @@ sub graft_file_copy_lib {
11471138
my $tree_paths = $l_map->{$u};
11481139
my $pfx = common_prefix([keys %$tree_paths]);
11491140
my ($repo, $path) = repo_path_split($u.$pfx);
1150-
$SVN_LOG ||= libsvn_connect($repo);
1151-
$SVN ||= libsvn_connect($repo);
1141+
$SVN = libsvn_connect($repo);
11521142

11531143
my ($base, $head) = libsvn_parse_revision();
11541144
my $inc = 1000;
@@ -1157,7 +1147,8 @@ sub graft_file_copy_lib {
11571147
$SVN::Error::handler = \&libsvn_skip_unknown_revs;
11581148
while (1) {
11591149
my $pool = SVN::Pool->new;
1160-
libsvn_get_log($SVN_LOG, "/$path", $min, $max, 0, 1, 1,
1150+
libsvn_get_log(libsvn_dup_ra($SVN), [$path],
1151+
$min, $max, 0, 1, 1,
11611152
sub {
11621153
libsvn_graft_file_copies($grafts, $tree_paths,
11631154
$path, @_);
@@ -1267,13 +1258,9 @@ sub repo_path_split {
12671258
return ($u, $full_url);
12681259
}
12691260
}
1270-
12711261
if ($_use_lib) {
12721262
my $tmp = libsvn_connect($full_url);
1273-
my $url = $tmp->get_repos_root;
1274-
$full_url =~ s#^\Q$url\E/*##;
1275-
push @repo_path_split_cache, qr/^(\Q$url\E)/;
1276-
return ($url, $full_url);
1263+
return ($tmp->{repos_root}, $tmp->{svn_path});
12771264
} else {
12781265
my ($url, $path) = ($full_url =~ m!^([a-z\+]+://[^/]*)(.*)$!i);
12791266
$path =~ s#^/+##;
@@ -2815,34 +2802,41 @@ sub _read_password {
28152802

28162803
sub libsvn_connect {
28172804
my ($url) = @_;
2818-
if (!$AUTH_BATON || !$AUTH_CALLBACKS) {
2819-
SVN::_Core::svn_config_ensure($_config_dir, undef);
2820-
($AUTH_BATON, $AUTH_CALLBACKS) = SVN::Core::auth_open_helper([
2821-
SVN::Client::get_simple_provider(),
2822-
SVN::Client::get_ssl_server_trust_file_provider(),
2823-
SVN::Client::get_simple_prompt_provider(
2824-
\&_simple_prompt, 2),
2825-
SVN::Client::get_ssl_client_cert_prompt_provider(
2826-
\&_ssl_client_cert_prompt, 2),
2827-
SVN::Client::get_ssl_client_cert_pw_prompt_provider(
2828-
\&_ssl_client_cert_pw_prompt, 2),
2829-
SVN::Client::get_username_provider(),
2830-
SVN::Client::get_ssl_server_trust_prompt_provider(
2831-
\&_ssl_server_trust_prompt),
2832-
SVN::Client::get_username_prompt_provider(
2833-
\&_username_prompt, 2),
2834-
]);
2835-
}
2836-
SVN::Ra->new(url => $url, auth => $AUTH_BATON,
2837-
auth_provider_callbacks => $AUTH_CALLBACKS);
2805+
SVN::_Core::svn_config_ensure($_config_dir, undef);
2806+
my ($baton, $callbacks) = SVN::Core::auth_open_helper([
2807+
SVN::Client::get_simple_provider(),
2808+
SVN::Client::get_ssl_server_trust_file_provider(),
2809+
SVN::Client::get_simple_prompt_provider(
2810+
\&_simple_prompt, 2),
2811+
SVN::Client::get_ssl_client_cert_prompt_provider(
2812+
\&_ssl_client_cert_prompt, 2),
2813+
SVN::Client::get_ssl_client_cert_pw_prompt_provider(
2814+
\&_ssl_client_cert_pw_prompt, 2),
2815+
SVN::Client::get_username_provider(),
2816+
SVN::Client::get_ssl_server_trust_prompt_provider(
2817+
\&_ssl_server_trust_prompt),
2818+
SVN::Client::get_username_prompt_provider(
2819+
\&_username_prompt, 2),
2820+
]);
2821+
my $ra = SVN::Ra->new(url => $url, auth => $baton,
2822+
pool => SVN::Pool->new,
2823+
auth_provider_callbacks => $callbacks);
2824+
$ra->{svn_path} = $url;
2825+
$ra->{repos_root} = $ra->get_repos_root;
2826+
$ra->{svn_path} =~ s#^\Q$ra->{repos_root}\E/*##;
2827+
push @repo_path_split_cache, qr/^(\Q$ra->{repos_root}\E)/;
2828+
return $ra;
2829+
}
2830+
2831+
sub libsvn_dup_ra {
2832+
my ($ra) = @_;
2833+
SVN::Ra->new(map { $_ => $ra->{$_} }
2834+
qw/url auth auth_provider_callbacks repos_root svn_path/);
28382835
}
28392836

28402837
sub libsvn_get_file {
28412838
my ($gui, $f, $rev, $chg) = @_;
2842-
my $p = $f;
2843-
if (length $SVN_PATH > 0) {
2844-
return unless ($p =~ s#^\Q$SVN_PATH\E/##);
2845-
}
2839+
$f =~ s#^/##;
28462840
print "\t$chg\t$f\n" unless $_q;
28472841

28482842
my ($hash, $pid, $in, $out);
@@ -2879,7 +2873,7 @@ sub libsvn_get_file {
28792873
waitpid $pid, 0;
28802874
$hash =~ /^$sha1$/o or die "not a sha1: $hash\n";
28812875
}
2882-
print $gui $mode,' ',$hash,"\t",$p,"\0" or croak $!;
2876+
print $gui $mode,' ',$hash,"\t",$f,"\0" or croak $!;
28832877
}
28842878

28852879
sub libsvn_log_entry {
@@ -2897,7 +2891,6 @@ sub libsvn_log_entry {
28972891

28982892
sub process_rm {
28992893
my ($gui, $last_commit, $f) = @_;
2900-
$f =~ s#^\Q$SVN_PATH\E/?## or return;
29012894
# remove entire directories.
29022895
if (safe_qx('git-ls-tree',$last_commit,'--',$f) =~ /^040000 tree/) {
29032896
defined(my $pid = open my $ls, '-|') or croak $!;
@@ -2919,9 +2912,11 @@ sub libsvn_fetch {
29192912
my ($last_commit, $paths, $rev, $author, $date, $msg) = @_;
29202913
open my $gui, '| git-update-index -z --index-info' or croak $!;
29212914
my @amr;
2915+
my $p = $SVN->{svn_path};
29222916
foreach my $f (keys %$paths) {
29232917
my $m = $paths->{$f}->action();
2924-
$f =~ s#^/+##;
2918+
$f =~ s#^/\Q$p\E/##;
2919+
next if $f =~ m#^/#;
29252920
if ($m =~ /^[DR]$/) {
29262921
print "\t$m\t$f\n" unless $_q;
29272922
process_rm($gui, $last_commit, $f);
@@ -3011,9 +3006,9 @@ sub libsvn_parse_revision {
30113006

30123007
sub libsvn_traverse {
30133008
my ($gui, $pfx, $path, $rev, $files) = @_;
3014-
my $cwd = "$pfx/$path";
3009+
my $cwd = length $pfx ? "$pfx/$path" : $path;
30153010
my $pool = SVN::Pool->new;
3016-
$cwd =~ s#^/+##g;
3011+
$cwd =~ s#^\Q$SVN->{svn_path}\E##;
30173012
my ($dirent, $r, $props) = $SVN->get_dir($cwd, $rev, $pool);
30183013
foreach my $d (keys %$dirent) {
30193014
my $t = $dirent->{$d}->kind;
@@ -3037,7 +3032,7 @@ sub libsvn_traverse_ignore {
30373032
my $pool = SVN::Pool->new;
30383033
my ($dirent, undef, $props) = $SVN->get_dir($path, $r, $pool);
30393034
my $p = $path;
3040-
$p =~ s#^\Q$SVN_PATH\E/?##;
3035+
$p =~ s#^\Q$SVN->{svn_path}\E/##;
30413036
print $fh length $p ? "\n# $p\n" : "\n# /\n";
30423037
if (my $s = $props->{'svn:ignore'}) {
30433038
$s =~ s/[\r\n]+/\n/g;
@@ -3064,7 +3059,7 @@ sub revisions_eq {
30643059
if ($_use_lib) {
30653060
# should be OK to use Pool here (r1 - r0) should be small
30663061
my $pool = SVN::Pool->new;
3067-
libsvn_get_log($SVN, "/$path", $r0, $r1,
3062+
libsvn_get_log($SVN, [$path], $r0, $r1,
30683063
0, 1, 1, sub {$nr++}, $pool);
30693064
$pool->clear;
30703065
} else {
@@ -3079,7 +3074,7 @@ sub revisions_eq {
30793074

30803075
sub libsvn_find_parent_branch {
30813076
my ($paths, $rev, $author, $date, $msg) = @_;
3082-
my $svn_path = '/'.$SVN_PATH;
3077+
my $svn_path = '/'.$SVN->{svn_path};
30833078

30843079
# look for a parent from another branch:
30853080
my $i = $paths->{$svn_path} or return;
@@ -3090,7 +3085,7 @@ sub libsvn_find_parent_branch {
30903085
$branch_from =~ s#^/##;
30913086
my $l_map = {};
30923087
read_url_paths_all($l_map, '', "$GIT_DIR/svn");
3093-
my $url = $SVN->{url};
3088+
my $url = $SVN->{repos_root};
30943089
defined $l_map->{$url} or return;
30953090
my $id = $l_map->{$url}->{$branch_from};
30963091
if (!defined $id && $_follow_parent) {
@@ -3112,7 +3107,7 @@ sub libsvn_find_parent_branch {
31123107
$GIT_SVN = $ENV{GIT_SVN_ID} = $id;
31133108
init_vars();
31143109
$SVN_URL = "$url/$branch_from";
3115-
$SVN_LOG = $SVN = undef;
3110+
$SVN = undef;
31163111
setup_git_svn();
31173112
# we can't assume SVN_URL exists at r+1:
31183113
$_revision = "0:$r";
@@ -3149,7 +3144,7 @@ sub libsvn_new_tree {
31493144
}
31503145
my ($paths, $rev, $author, $date, $msg) = @_;
31513146
open my $gui, '| git-update-index -z --index-info' or croak $!;
3152-
libsvn_traverse($gui, '', $SVN_PATH, $rev);
3147+
libsvn_traverse($gui, '', $SVN->{svn_path}, $rev);
31533148
close $gui or croak $?;
31543149
return libsvn_log_entry($rev, $author, $date, $msg);
31553150
}
@@ -3234,11 +3229,10 @@ sub libsvn_commit_cb {
32343229

32353230
sub libsvn_ls_fullurl {
32363231
my $fullurl = shift;
3237-
my ($repo, $path) = repo_path_split($fullurl);
3238-
$SVN ||= libsvn_connect($repo);
3232+
$SVN ||= libsvn_connect($fullurl);
32393233
my @ret;
32403234
my $pool = SVN::Pool->new;
3241-
my ($dirent, undef, undef) = $SVN->get_dir($path,
3235+
my ($dirent, undef, undef) = $SVN->get_dir($SVN->{svn_path},
32423236
$SVN->get_latest_revnum, $pool);
32433237
foreach my $d (keys %$dirent) {
32443238
if ($dirent->{$d}->kind == $SVN::Node::dir) {
@@ -3260,8 +3254,9 @@ sub libsvn_skip_unknown_revs {
32603254
# Wonderfully consistent library, eh?
32613255
# 160013 - svn:// and file://
32623256
# 175002 - http(s)://
3257+
# 175007 - http(s):// (this repo required authorization, too...)
32633258
# More codes may be discovered later...
3264-
if ($errno == 175002 || $errno == 160013) {
3259+
if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
32653260
return;
32663261
}
32673262
croak "Error from SVN, ($errno): ", $err->expanded_message,"\n";
@@ -3349,8 +3344,7 @@ sub split_path {
33493344
}
33503345

33513346
sub repo_path {
3352-
(defined $_[1] && length $_[1]) ? "$_[0]->{svn_path}/$_[1]"
3353-
: $_[0]->{svn_path}
3347+
(defined $_[1] && length $_[1]) ? $_[1] : ''
33543348
}
33553349

33563350
sub url_path {
@@ -3382,10 +3376,9 @@ sub rmdirs {
33823376
exec qw/git-ls-tree --name-only -r -z/, $self->{c} or croak $!;
33833377
}
33843378
local $/ = "\0";
3385-
my @svn_path = split m#/#, $self->{svn_path};
33863379
while (<$fh>) {
33873380
chomp;
3388-
my @dn = (@svn_path, (split m#/#, $_));
3381+
my @dn = split m#/#, $_;
33893382
while (pop @dn) {
33903383
delete $rm->{join '/', @dn};
33913384
}

0 commit comments

Comments
 (0)