git-svn: prepare multi-init for wildcard support
authorEric Wong <normalperson@yhbt.net>
Sat, 3 Feb 2007 21:29:17 +0000 (13:29 -0800)
committerEric Wong <normalperson@yhbt.net>
Fri, 23 Feb 2007 08:57:11 +0000 (00:57 -0800)
Update the tests since we no longer write so many things to the
config.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
git-svn.perl
t/t9107-git-svn-migrate.sh

index 66b4c20fd9567a2b91dc54ebd95e7aaf4f76a8f3..6874eabffa2b577c4b0c707238e7e3c14c4e880c 100755 (executable)
@@ -367,9 +367,10 @@ sub cmd_multi_init {
 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);
+               }
        }
 }
 
@@ -479,9 +480,12 @@ sub complete_url_ls_init {
                # 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/*";
@@ -489,7 +493,7 @@ sub complete_url_ls_init {
                $remote_path =~ s#^/##g;
                my ($n) = ($switch =~ /^--(\w+)/);
                command_noisy('config', "svn-remote.$remote_id.$n",
-                                       $remote_path);
+                                       "$remote_path:refs/remotes/$pfx*");
        }
 }
 
@@ -660,9 +664,48 @@ BEGIN {
 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;
@@ -685,6 +728,16 @@ sub read_all_remotes {
                        $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;
@@ -3072,10 +3125,67 @@ sub DESTROY {
        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
index 0fbfd264ec2e326b647ffd5715586f17bcd8696c..8376429bcb764df44503359801e774dc4dabbc49 100755 (executable)
@@ -6,7 +6,7 @@ test_description='git-svn metadata migrations from previous versions'
 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 && \
@@ -43,11 +43,19 @@ test_expect_success 'initialize a multi-repository repo' "
        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: