diff options
-rwxr-xr-x | git-svn.perl | 78 | ||||
-rwxr-xr-x | t/t9138-git-svn-multiple-branches.sh | 114 |
2 files changed, 170 insertions, 22 deletions
diff --git a/git-svn.perl b/git-svn.perl index b1245cbab..48e8aad00 100755 --- a/git-svn.perl +++ b/git-svn.perl @@ -63,7 +63,7 @@ my ($SVN); $sha1 = qr/[a-f\d]{40}/; $sha1_short = qr/[a-f\d]{4,40}/; my ($_stdin, $_help, $_edit, - $_message, $_file, + $_message, $_file, $_branch_dest, $_template, $_shared, $_version, $_fetch_all, $_no_rebase, $_fetch_parent, $_merge, $_strategy, $_dry_run, $_local, @@ -92,11 +92,11 @@ my %fc_opts = ( 'follow-parent|follow!' => \$Git::SVN::_follow_parent, 'localtime' => \$Git::SVN::_localtime, %remote_opts ); -my ($_trunk, $_tags, $_branches, $_stdlayout); +my ($_trunk, @_tags, @_branches, $_stdlayout); my %icv; my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared, - 'trunk|T=s' => \$_trunk, 'tags|t=s' => \$_tags, - 'branches|b=s' => \$_branches, 'prefix=s' => \$_prefix, + 'trunk|T=s' => \$_trunk, 'tags|t=s@' => \@_tags, + 'branches|b=s@' => \@_branches, 'prefix=s' => \$_prefix, 'stdlayout|s' => \$_stdlayout, 'minimize-url|m' => \$Git::SVN::_minimize_url, 'no-metadata' => sub { $icv{noMetadata} = 1 }, @@ -141,11 +141,13 @@ my %cmd = ( branch => [ \&cmd_branch, 'Create a branch in the SVN repository', { 'message|m=s' => \$_message, + 'destination|d=s' => \$_branch_dest, 'dry-run|n' => \$_dry_run, 'tag|t' => \$_tag } ], tag => [ sub { $_tag = 1; cmd_branch(@_) }, 'Create a tag in the SVN repository', { 'message|m=s' => \$_message, + 'destination|d=s' => \$_branch_dest, 'dry-run|n' => \$_dry_run } ], 'set-tree' => [ \&cmd_set_tree, "Set an SVN repository to a git tree-ish", @@ -365,7 +367,7 @@ sub init_subdir { sub cmd_clone { my ($url, $path) = @_; if (!defined $path && - (defined $_trunk || defined $_branches || defined $_tags || + (defined $_trunk || @_branches || @_tags || defined $_stdlayout) && $url !~ m#^[a-z\+]+://#) { $path = $url; @@ -379,10 +381,10 @@ sub cmd_clone { sub cmd_init { if (defined $_stdlayout) { $_trunk = 'trunk' if (!defined $_trunk); - $_tags = 'tags' if (!defined $_tags); - $_branches = 'branches' if (!defined $_branches); + @_tags = 'tags' if (! @_tags); + @_branches = 'branches' if (! @_branches); } - if (defined $_trunk || defined $_branches || defined $_tags) { + if (defined $_trunk || @_branches || @_tags) { return cmd_multi_init(@_); } my $url = shift or die "SVN repository location required ", @@ -630,7 +632,31 @@ sub cmd_branch { my ($src, $rev, undef, $gs) = working_head_info($head); my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}}; - my $glob = $remote->{ $_tag ? 'tags' : 'branches' }; + my $allglobs = $remote->{ $_tag ? 'tags' : 'branches' }; + my $glob; + if ($#{$allglobs} == 0) { + $glob = $allglobs->[0]; + } else { + unless(defined $_branch_dest) { + die "Multiple ", + $_tag ? "tag" : "branch", + " paths defined for Subversion repository.\n", + "You must specify where you want to create the ", + $_tag ? "tag" : "branch", + " with the --destination argument.\n"; + } + foreach my $g (@{$allglobs}) { + if ($_branch_dest eq $g->{path}->{left}) { + $glob = $g; + last; + } + } + unless (defined $glob) { + die "Unknown ", + $_tag ? "tag" : "branch", + " destination $_branch_dest\n"; + } + } my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/}; my $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ()); @@ -837,7 +863,7 @@ sub cmd_proplist { sub cmd_multi_init { my $url = shift; - unless (defined $_trunk || defined $_branches || defined $_tags) { + unless (defined $_trunk || @_branches || @_tags) { usage(1); } @@ -862,10 +888,14 @@ sub cmd_multi_init { undef, $trunk_ref); } } - return unless defined $_branches || defined $_tags; + return unless @_branches || @_tags; my $ra = $url ? Git::SVN::Ra->new($url) : undef; - complete_url_ls_init($ra, $_branches, '--branches/-b', $_prefix); - complete_url_ls_init($ra, $_tags, '--tags/-t', $_prefix . 'tags/'); + foreach my $path (@_branches) { + complete_url_ls_init($ra, $path, '--branches/-b', $_prefix); + } + foreach my $path (@_tags) { + complete_url_ls_init($ra, $path, '--tags/-t', $_prefix.'tags/'); + } } sub cmd_multi_fetch { @@ -1150,6 +1180,7 @@ sub complete_url_ls_init { die "--prefix='$pfx' must have a trailing slash '/'\n"; } command_noisy('config', + '--add', "svn-remote.$gs->{repo_id}.$n", "$remote_path:refs/remotes/$pfx*" . ('/*' x (($remote_path =~ tr/*/*/) - 1)) ); @@ -1616,7 +1647,8 @@ sub fetch_all { # read the max revs for wildcard expansion (branches/*, tags/*) foreach my $t (qw/branches tags/) { defined $remote->{$t} or next; - push @globs, $remote->{$t}; + push @globs, @{$remote->{$t}}; + my $max_rev = eval { tmp_config(qw/--int --get/, "svn-remote.$repo_id.${t}-maxRev") }; if (defined $max_rev && ($max_rev < $base)) { @@ -1663,15 +1695,16 @@ sub read_all_remotes { } elsif (m!^(.+)\.(branches|tags)= (.*):refs/remotes/(.+)\s*$/!x) { my ($p, $g) = ($3, $4); - my $rs = $r->{$1}->{$2} = { - t => $2, - remote => $1, - path => Git::SVN::GlobSpec->new($p), - ref => Git::SVN::GlobSpec->new($g) }; + my $rs = { + t => $2, + remote => $1, + 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"; } + push @{ $r->{$1}->{$2} }, $rs; } } @@ -1811,9 +1844,10 @@ sub find_by_url { # repos_root and, path are optional next if defined $repos_root && $repos_root ne $u; my $fetch = $remotes->{$repo_id}->{fetch} || {}; - foreach (qw/branches tags/) { - resolve_local_globs($u, $fetch, - $remotes->{$repo_id}->{$_}); + foreach my $t (qw/branches tags/) { + foreach my $globspec (@{$remotes->{$repo_id}->{$t}}) { + resolve_local_globs($u, $fetch, $globspec); + } } my $p = $path; my $rwr = rewrite_root({repo_id => $repo_id}); diff --git a/t/t9138-git-svn-multiple-branches.sh b/t/t9138-git-svn-multiple-branches.sh new file mode 100755 index 000000000..9725ccf9d --- /dev/null +++ b/t/t9138-git-svn-multiple-branches.sh @@ -0,0 +1,114 @@ +#!/bin/sh +# +# Copyright (c) 2009 Marc Branchaud +# + +test_description='git svn multiple branch and tag paths in the svn repo' +. ./lib-git-svn.sh + +test_expect_success 'setup svnrepo' ' + mkdir project \ + project/trunk \ + project/b_one \ + project/b_two \ + project/tags_A \ + project/tags_B && + echo 1 > project/trunk/a.file && + svn import -m "$test_description" project "$svnrepo/project" && + rm -rf project && + svn cp -m "Branch 1" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_one/first" && + svn cp -m "Tag 1" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/1.0" && + svn co "$svnrepo/project" svn_project && + cd svn_project && + . + echo 2 > trunk/a.file && + svn ci -m "Change 1" trunk/a.file && + svn cp -m "Branch 2" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_one/second" && + svn cp -m "Tag 2" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/2.0" && + echo 3 > trunk/a.file && + svn ci -m "Change 2" trunk/a.file && + svn cp -m "Branch 3" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_two/1" && + svn cp -m "Tag 3" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/3.0" && + echo 4 > trunk/a.file && + svn ci -m "Change 3" trunk/a.file && + svn cp -m "Branch 4" "$svnrepo/project/trunk" \ + "$svnrepo/project/b_two/2" && + svn cp -m "Tag 4" "$svnrepo/project/trunk" \ + "$svnrepo/project/tags_A/4.0" && + svn up && + echo 5 > b_one/first/a.file && + svn ci -m "Change 4" b_one/first/a.file && + svn cp -m "Tag 5" "$svnrepo/project/b_one/first" \ + "$svnrepo/project/tags_B/v5" && + echo 6 > b_one/second/a.file && + svn ci -m "Change 5" b_one/second/a.file && + svn cp -m "Tag 6" "$svnrepo/project/b_one/second" \ + "$svnrepo/project/tags_B/v6" && + echo 7 > b_two/1/a.file && + svn ci -m "Change 6" b_two/1/a.file && + svn cp -m "Tag 7" "$svnrepo/project/b_two/1" \ + "$svnrepo/project/tags_B/v7" && + echo 8 > b_two/2/a.file && + svn ci -m "Change 7" b_two/2/a.file && + svn cp -m "Tag 8" "$svnrepo/project/b_two/2" \ + "$svnrepo/project/tags_B/v8" && + cd .. + ' + +test_expect_success 'clone multiple branch and tag paths' ' + git svn clone -T trunk \ + -b b_one/* --branches b_two/* \ + -t tags_A/* --tags tags_B \ + "$svnrepo/project" git_project && + cd git_project && + git rev-parse refs/remotes/first && + git rev-parse refs/remotes/second && + git rev-parse refs/remotes/1 && + git rev-parse refs/remotes/2 && + git rev-parse refs/remotes/tags/1.0 && + git rev-parse refs/remotes/tags/2.0 && + git rev-parse refs/remotes/tags/3.0 && + git rev-parse refs/remotes/tags/4.0 && + git rev-parse refs/remotes/tags/v5 && + git rev-parse refs/remotes/tags/v6 && + git rev-parse refs/remotes/tags/v7 && + git rev-parse refs/remotes/tags/v8 && + cd .. + ' + +test_expect_success 'Multiple branch or tag paths require -d' ' + cd git_project && + test_must_fail git svn branch -m "No new branch" Nope && + test_must_fail git svn tag -m "No new tag" Tagless && + test_must_fail git rev-parse refs/remotes/Nope && + test_must_fail git rev-parse refs/remotes/tags/Tagless && + cd ../svn_project && + svn up && + test_must_fail test -d b_one/Nope && + test_must_fail test -d b_two/Nope && + test_must_fail test -d tags_A/Tagless && + test_must_fail test -d tags_B/Tagless && + cd .. + ' + +test_expect_success 'create new branches and tags' ' + ( cd git_project && git svn branch -m "New branch 1" -d project/b_one New1 ) && + ( cd svn_project && svn up && test -e b_one/New1/a.file ) && + + ( cd git_project && git svn branch -m "New branch 2" -d project/b_two New2 ) && + ( cd svn_project && svn up && test -e b_two/New2/a.file ) && + + ( cd git_project && git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) && + ( cd svn_project && svn up && test -e tags_A/Tag1/a.file ) + + ( cd git_project && git svn tag -m "New tag 2" -d project/tags_B Tag2 ) && + ( cd svn_project && svn up && test -e tags_B/Tag2/a.file ) + ' + +test_done |