svn: allow branches outside of refs/remotes

It may be convenient for some users to store svn remote tracking
branches outside of the refs/remotes/ heirarchy.

To accomplish this feat, this patch includes the entire path to
the ref in $r->{'refname'} in &read_all_remotes and tries to change
references to this entry so the new value makes sense.

[ew: fixed backwards compatibility, long lines]

Signed-off-by: Adam Brewster <adambrewster@gmail.com>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
This commit is contained in:
Adam Brewster 2009-08-11 23:14:27 -04:00 коммит произвёл Eric Wong
Родитель b186a261b1
Коммит 6f5748e14c
6 изменённых файлов: 96 добавлений и 54 удалений

Просмотреть файл

@ -909,7 +909,7 @@ sub cmd_multi_init {
}
do_git_init_db();
if (defined $_trunk) {
my $trunk_ref = $_prefix . 'trunk';
my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
# try both old-style and new-style lookups:
my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
unless ($gs_trunk) {
@ -1654,23 +1654,23 @@ sub resolve_local_globs {
return unless defined $glob_spec;
my $ref = $glob_spec->{ref};
my $path = $glob_spec->{path};
foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
next unless m#^refs/remotes/$ref->{regex}$#;
foreach (command(qw#for-each-ref --format=%(refname) refs/#)) {
next unless m#^$ref->{regex}$#;
my $p = $1;
my $pathname = desanitize_refname($path->full_path($p));
my $refname = desanitize_refname($ref->full_path($p));
if (my $existing = $fetch->{$pathname}) {
if ($existing ne $refname) {
die "Refspec conflict:\n",
"existing: refs/remotes/$existing\n",
" globbed: refs/remotes/$refname\n";
"existing: $existing\n",
" globbed: $refname\n";
}
my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
my $u = (::cmt_metadata("$refname"))[0];
$u =~ s!^\Q$url\E(/|$)!! or die
"refs/remotes/$refname: '$url' not found in '$u'\n";
"$refname: '$url' not found in '$u'\n";
if ($pathname ne $u) {
warn "W: Refspec glob conflict ",
"(ref: refs/remotes/$refname):\n",
"(ref: $refname):\n",
"expected path: $pathname\n",
" real path: $u\n",
"Continuing ahead with $u\n";
@ -1748,33 +1748,35 @@ sub read_all_remotes {
my $use_svm_props = eval { command_oneline(qw/config --bool
svn.useSvmProps/) };
$use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*};
foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) {
my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3);
die("svn-remote.$remote: remote ref '$_remote_ref' "
. "must start with 'refs/remotes/'\n")
unless $_remote_ref =~ m{^refs/remotes/(.+)};
my $remote_ref = $1;
$local_ref =~ s{^/}{};
if (m!^(.+)\.fetch=$svn_refspec$!) {
my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
die("svn-remote.$remote: remote ref '$remote_ref' "
. "must start with 'refs/'\n")
unless $remote_ref =~ m{^refs/};
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
$r->{$remote}->{svm} = {} if $use_svm_props;
} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
$r->{$1}->{svm} = {};
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r->{$1}->{url} = $2;
} elsif (m!^(.+)\.(branches|tags)=
(.*):refs/remotes/(.+)\s*$/!x) {
my ($p, $g) = ($3, $4);
} elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
my ($remote, $t, $local_ref, $remote_ref) =
($1, $2, $3, $4);
die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
. "must start with 'refs/'\n")
unless $remote_ref =~ m{^refs/};
my $rs = {
t => $2,
remote => $1,
path => Git::SVN::GlobSpec->new($p),
ref => Git::SVN::GlobSpec->new($g) };
t => $t,
remote => $remote,
path => Git::SVN::GlobSpec->new($local_ref),
ref => Git::SVN::GlobSpec->new($remote_ref) };
if (length($rs->{ref}->{right}) != 0) {
die "The '*' glob character must be the last ",
"character of '$g'\n";
"character of '$remote_ref'\n";
}
push @{ $r->{$1}->{$2} }, $rs;
push @{ $r->{$remote}->{$t} }, $rs;
}
}
@ -1882,9 +1884,9 @@ sub init_remote_config {
}
}
my ($xrepo_id, $xpath) = find_ref($self->refname);
if (defined $xpath) {
if (!$no_write && defined $xpath) {
die "svn-remote.$xrepo_id.fetch already set to track ",
"$xpath:refs/remotes/", $self->refname, "\n";
"$xpath:", $self->refname, "\n";
}
unless ($no_write) {
command_noisy('config',
@ -1959,7 +1961,7 @@ sub find_ref {
my ($ref_id) = @_;
foreach (command(qw/config -l/)) {
next unless m!^svn-remote\.(.+)\.fetch=
\s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x;
\s*/?(.*?)\s*:\s*(.+?)\s*$!x;
my ($repo_id, $path, $ref) = ($1, $2, $3);
if ($ref eq $ref_id) {
$path = '' if ($path =~ m#^\./?#);
@ -1976,16 +1978,16 @@ sub new {
if (!defined $repo_id) {
die "Could not find a \"svn-remote.*.fetch\" key ",
"in the repository configuration matching: ",
"refs/remotes/$ref_id\n";
"$ref_id\n";
}
}
my $self = _new($class, $repo_id, $ref_id, $path);
if (!defined $self->{path} || !length $self->{path}) {
my $fetch = command_oneline('config', '--get',
"svn-remote.$repo_id.fetch",
":refs/remotes/$ref_id\$") or
":$ref_id\$") or
die "Failed to read \"svn-remote.$repo_id.fetch\" ",
"\":refs/remotes/$ref_id\$\" in config\n";
"\":$ref_id\$\" in config\n";
($self->{path}, undef) = split(/\s*:\s*/, $fetch);
}
$self->{url} = command_oneline('config', '--get',
@ -1996,7 +1998,7 @@ sub new {
}
sub refname {
my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
my ($refname) = $_[0]->{ref_id} ;
# It cannot end with a slash /, we'll throw up on this because
# SVN can't have directories with a slash in their name, either:
@ -3331,12 +3333,23 @@ sub _new {
}
unless (defined $ref_id && length $ref_id) {
$_prefix = '' unless defined($_prefix);
$_[2] = $ref_id = $_prefix . $Git::SVN::default_ref_id;
$_[2] = $ref_id =
"refs/remotes/$_prefix$Git::SVN::default_ref_id";
}
$_[1] = $repo_id;
my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
# Older repos imported by us used $GIT_DIR/svn/foo instead of
# $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
if ($ref_id =~ m{^refs/remotes/(.*)}) {
my $old_dir = "$ENV{GIT_DIR}/svn/$1";
if (-d $old_dir && ! -d $dir) {
$dir = $old_dir;
}
}
$_[3] = $path = '' unless (defined $path);
mkpath(["$ENV{GIT_DIR}/svn"]);
mkpath([$dir]);
bless {
ref_id => $ref_id, dir => $dir, index => "$dir/index",
path => $path, config => "$ENV{GIT_DIR}/svn/config",
@ -5509,7 +5522,7 @@ sub minimize_connections {
my $pfx = "svn-remote.$x->{old_repo_id}";
my $old_fetch = quotemeta("$x->{old_path}:".
"refs/remotes/$x->{ref_id}");
"$x->{ref_id}");
command_noisy(qw/config --unset/,
"$pfx.fetch", '^'. $old_fetch . '$');
delete $r->{$x->{old_repo_id}}->
@ -5578,7 +5591,7 @@ sub new {
my ($class, $glob) = @_;
my $re = $glob;
$re =~ s!/+$!!g; # no need for trailing slashes
$re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!;
$re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!;
my $temp = $re;
my ($left, $right) = ($1, $3);
$re = $2;

Просмотреть файл

@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then
fi
GIT_DIR=$PWD/.git
GIT_SVN_DIR=$GIT_DIR/svn/git-svn
GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
svn >/dev/null 2>&1

Просмотреть файл

@ -172,11 +172,11 @@ test_expect_success "follow-parent is atomic" '
git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 &&
git update-ref -d refs/remotes/stunk &&
git config --unset svn-remote.svn.fetch stunk &&
mkdir -p "$GIT_DIR"/svn/flunk@18 &&
rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) &&
dd if="$GIT_DIR"/svn/stunk/$rev_map \
of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 &&
rm -rf "$GIT_DIR"/svn/stunk &&
mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 &&
rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) &&
dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \
of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 &&
rm -rf "$GIT_DIR"/svn/refs/remotes/stunk &&
git svn init --minimize-url -i flunk "$svnrepo"/flunk &&
git svn fetch -i flunk &&
git svn init --minimize-url -i stunk "$svnrepo"/stunk &&

Просмотреть файл

@ -16,9 +16,7 @@ test_expect_success 'setup old-looking metadata' '
cd .. &&
git svn init "$svnrepo" &&
git svn fetch &&
mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
rmdir "$GIT_DIR"/svn &&
rm -rf "$GIT_DIR"/svn &&
git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
@ -87,7 +85,7 @@ test_expect_success 'migrate --minimize on old inited layout' '
rm -rf "$GIT_DIR"/svn &&
for i in `cat fetch.out`; do
path=`expr $i : "\([^:]*\):.*$"`
ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"`
if test -z "$ref"; then continue; fi
if test -n "$path"; then path="/$path"; fi
( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
@ -107,16 +105,16 @@ test_expect_success 'migrate --minimize on old inited layout' '
test_expect_success ".rev_db auto-converted to .rev_map.UUID" '
git svn fetch -i trunk &&
test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" &&
test -n "$expect" &&
rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
convert_to_rev_db "$expect" "$rev_db" &&
rm -f "$expect" &&
test -f "$rev_db" &&
git svn fetch -i trunk &&
test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db &&
test -f "$expect"
'

Просмотреть файл

@ -28,26 +28,26 @@ test_expect_success 'Setup repo' 'git svn init "$svnrepo"'
test_expect_success 'Fetch repo' 'git svn fetch'
test_expect_success 'make backup copy of unhandled.log' '
cp .git/svn/git-svn/unhandled.log tmp
cp .git/svn/refs/remotes/git-svn/unhandled.log tmp
'
test_expect_success 'create leftover index' '> .git/svn/git-svn/index'
test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index'
test_expect_success 'git svn gc runs' 'git svn gc'
test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index'
test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index'
if perl -MCompress::Zlib -e 0 2>/dev/null
then
test_expect_success 'git svn gc produces a valid gzip file' '
gunzip .git/svn/git-svn/unhandled.log.gz
gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
'
else
say "Perl Compress::Zlib unavailable, skipping gunzip test"
fi
test_expect_success 'git svn gc does not change unhandled.log files' '
test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log
test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log
'
test_done

31
t/t9144-git-svn-old-rev_map.sh Executable file
Просмотреть файл

@ -0,0 +1,31 @@
#!/bin/sh
#
# Copyright (c) 2009 Eric Wong
test_description='git svn old rev_map preservd'
. ./lib-git-svn.sh
test_expect_success 'setup test repository with old layout' '
mkdir i &&
(cd i && > a) &&
svn_cmd import -m- i "$svnrepo" &&
git svn init "$svnrepo" &&
git svn fetch &&
test -d .git/svn/refs/remotes/git-svn/ &&
! test -e .git/svn/git-svn/ &&
mv .git/svn/refs/remotes/git-svn .git/svn/ &&
rm -r .git/svn/refs
'
test_expect_success 'old layout continues to work' '
svn_cmd import -m- i "$svnrepo/b" &&
git svn rebase &&
echo a >> b/a &&
git add b/a &&
git commit -m- -a &&
git svn dcommit &&
! test -d .git/svn/refs/ &&
test -e .git/svn/git-svn/
'
test_done