sub cmd_multi_fetch {
my $remotes = Git::SVN::read_all_remotes();
foreach my $repo_id (sort keys %$remotes) {
- my $url = $remotes->{$repo_id}->{url} or next;
- my $fetch = $remotes->{$repo_id}->{fetch} or next;
- Git::SVN::fetch_all($repo_id, $url, $fetch);
+ if ($remotes->{$repo_id}->{url} &&
+ $remotes->{$repo_id}->{fetch}) {
+ Git::SVN::fetch_all($repo_id, $remotes);
+ }
}
}
# don't try to init already existing refs
unless ($gs) {
print "init $url/$path => $ref\n";
- $gs = Git::SVN->init($url, $path, undef, $ref);
+ $gs = Git::SVN->init($url, $path, undef, $ref, 1);
+ }
+ if ($gs) {
+ $remote_id = $gs->{repo_id};
+ last;
}
- $remote_id ||= $gs->{repo_id} if $gs;
}
if (defined $remote_id) {
$remote_path = "$ra->{svn_path}/$repo_path/*";
$remote_path =~ s#^/##g;
my ($n) = ($switch =~ /^--(\w+)/);
command_noisy('config', "svn-remote.$remote_id.$n",
- $remote_path);
+ "$remote_path:refs/remotes/$pfx*");
}
}
my %LOCKFILES;
END { unlink keys %LOCKFILES if %LOCKFILES }
+sub resolve_local_globs {
+ my ($url, $fetch, $glob_spec) = @_;
+ 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}$#;
+ my $p = $1;
+ my $pathname = $path->full_path($p);
+ my $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";
+ }
+ my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
+ $u =~ s!^\Q$url\E/?!! or die
+ "refs/remotes/$refname: '$url' not found in '$u'\n";
+ if ($pathname ne $u) {
+ warn "W: Refspec glob conflict ",
+ "(ref: refs/remotes/$refname):\n",
+ "expected path: $pathname\n",
+ " real path: $u\n",
+ "Continuing ahead with $u\n";
+ next;
+ }
+ } else {
+ warn "Globbed ($path->{glob}:$ref->{glob}): ",
+ "$pathname == $refname\n";
+ $fetch->{$pathname} = $refname;
+ }
+ }
+}
+
sub fetch_all {
- my ($repo_id, $url, $fetch) = @_;
+ my ($repo_id, $remotes) = @_;
+ my $fetch = $remotes->{$repo_id}->{fetch};
+ my $url = $remotes->{$repo_id}->{url};
my @gs;
+ resolve_local_globs($url, $fetch, $remotes->{$repo_id}->{branches});
+ resolve_local_globs($url, $fetch, $remotes->{$repo_id}->{tags});
my $ra = Git::SVN::Ra->new($url);
my $head = $ra->get_latest_revnum;
my $base = $head;
$r->{$1}->{fetch}->{$2} = $3;
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r->{$1}->{url} = $2;
+ } elsif (m!^(.+)\.(branches|tags)=
+ (.*):refs/remotes/(.+)\s*$/!x) {
+ my ($p, $g) = ($3, $4);
+ my $rs = $r->{$1}->{$2} = {
+ path => Git::SVN::GlobSpec->new($p),
+ ref => Git::SVN::GlobSpec->new($g) };
+ if (length($rs->{ref}->{right}) != 0) {
+ die "The '*' glob character must be the last ",
+ "character of '$g'\n";
+ }
}
}
$r;
command_close_pipe($self->{gui}, $self->{ctx});
}
+package Git::SVN::GlobSpec;
+use strict;
+use warnings;
+
+sub new {
+ my ($class, $glob) = @_;
+ warn "glob: $glob\n";
+ my $re = $glob;
+ $re =~ s!/+$!!g; # no need for trailing slashes
+ my $nr = ($re =~ s!^(.*/?)\*(/?.*)$!\(\[^/\]+\)!g);
+ my ($left, $right) = ($1, $2);
+ if ($nr > 1) {
+ warn "Only one '*' wildcard expansion ",
+ "is supported (got $nr): '$glob'\n";
+ } elsif ($nr == 0) {
+ warn "One '*' is needed for glob: '$glob'\n";
+ }
+ $re = quotemeta($left) . $re . quotemeta($right);
+ $left =~ s!/+$!!g;
+ $right =~ s!^/+!!g;
+ bless { left => $left, right => $right,
+ regex => qr/$re/, glob => $glob }, $class;
+}
+
+sub full_path {
+ my ($self, $path) = @_;
+ return (length $self->{left} ? "$self->{left}/" : '') .
+ $path . (length $self->{right} ? "/$self->{right}" : '');
+}
+
__END__
Data structures:
+
+$remotes = { # returned by read_all_remotes()
+ 'svn' => {
+ # svn-remote.svn.url=https://svn.musicpd.org
+ url => 'https://svn.musicpd.org',
+ # svn-remote.svn.fetch=mpd/trunk:trunk
+ fetch => {
+ 'mpd/trunk' => 'trunk',
+ },
+ # svn-remote.svn.tags=mpd/tags/*:tags/*
+ tags => {
+ path => {
+ left => 'mpd/tags',
+ right => '',
+ regex => qr!mpd/tags/([^/]+)$!,
+ glob => 'tags/*',
+ },
+ ref => {
+ left => 'tags',
+ right => '',
+ regex => qr!tags/([^/]+)$!,
+ glob => 'tags/*',
+ },
+ }
+ }
+};
+
$log_entry hashref as returned by libsvn_log_entry()
{
log => 'whitespace-formatted log entry
test_expect_success 'setup old-looking metadata' "
cp $GIT_DIR/config $GIT_DIR/config-old-git-svn &&
mkdir import &&
- cd import
+ cd import &&
for i in trunk branches/a branches/b \
tags/0.1 tags/0.2 tags/0.3; do
mkdir -p \$i && \
git-svn multi-init $svnrepo -T trunk -t tags -b branches &&
git-repo-config --get-all svn-remote.svn.fetch > fetch.out &&
grep '^trunk:refs/remotes/trunk$' fetch.out &&
- grep '^branches/a:refs/remotes/a$' fetch.out &&
- grep '^branches/b:refs/remotes/b$' fetch.out &&
- grep '^tags/0\.1:refs/remotes/tags/0\.1$' fetch.out &&
- grep '^tags/0\.2:refs/remotes/tags/0\.2$' fetch.out &&
- grep '^tags/0\.3:refs/remotes/tags/0\.3$' fetch.out
+ test -n \"\`git-config --get svn-remote.svn.branches \
+ '^branches/\*:refs/remotes/\*$'\`\" &&
+ test -n \"\`git-config --get svn-remote.svn.tags \
+ '^tags/\*:refs/remotes/tags/\*$'\`\" &&
+ git config --unset svn-remote.svn.branches \
+ '^branches/\*:refs/remotes/\*$' &&
+ git config --unset svn-remote.svn.tags \
+ '^tags/\*:refs/remotes/tags/\*$' &&
+ git-config --add svn-remote.svn.fetch 'branches/a:refs/remotes/a' &&
+ git-config --add svn-remote.svn.fetch 'branches/b:refs/remotes/b' &&
+ for i in tags/0.1 tags/0.2 tags/0.3; do
+ git-config --add svn-remote.svn.fetch \
+ \$i:refs/remotes/\$i || exit 1; done
"
# refs should all be different, but the trees should all be the same: