threads / discuss / 19792

git svn: Supporting multiple branch subdirs?

Subject: git svn: Supporting multiple branch subdirs?

## tl;dr

23 messages between Jun 12, 2009 and Jun 27, 2009.

replies: 22people: 5as markdown or json

Marc Branchaud· Jun 12, 2009, 21:46 UTC · lore
Hi,
I believe I need git-svn to support multiple branch subdirectories.
The motivation for this is the (partial) gitification of the FreeBSD subversion repository.  FreeBSD doesn't follow the usual branches/tags/trunk pattern.  Instead:
- FreeBSD's /trunk is called /head.
- /head is branched into /stable/X subdirs.
- Each /stable/X branch is sub-branched into /releng/X.Y subirs.
- /releng/X.Y.Z branches are tagged under /release/foo.
I'm only looking to get an updatable mirror of their repo -- I don't (yet) need to push changes back to FreeBSD.
I'd like to modify git-svn to support multiple (at least two) -b parameters, so I could import the FreeBSD repo with something like
	git svn init --trunk=head \
		--branches=stable \
		--branches=releng \
		--tags=release \
		file:///local/mirror/of/svn.freebsd.org/base/
	git svn fetch
Is this approach wise or even feasible?  A couple of possible show-stoppers I see are:
- The releng/X.Y branches are created from the stable/X  tree, where X itself doesn't exist from the start.
- FreeBSD likes to make single commits that spans multiple branches.  This is usually done as a security patch: One commit can, for example, touch /head, /stable/X, /stable/W, and several branches under /releng.
If this isn't completely off the wall, I'd appreciate some pointers on the git-svn.perl code.  I've tried searching through the code to see how the -b parameter is handled, but I got lost pretty quickly.  How does the fetch command deal with branches?
Thanks,
		M.
Michael J Gruber· Jun 13, 2009, 11:46 UTC · re: Marc Branchaud · lore

Re: git svn: Supporting multiple branch subdirs?

Marc Branchaud venit, vidit, dixit 12.06.2009 23:46:
Show 25 quoted lines
> Hi,
> 
> I believe I need git-svn to support multiple branch subdirectories.
> 
> The motivation for this is the (partial) gitification of the FreeBSD subversion repository.  FreeBSD doesn't follow the usual branches/tags/trunk pattern.  Instead:
> 
> - FreeBSD's /trunk is called /head.
> 
> - /head is branched into /stable/X subdirs.
> 
> - Each /stable/X branch is sub-branched into /releng/X.Y subirs.
> 
> - /releng/X.Y.Z branches are tagged under /release/foo.
> 
> I'm only looking to get an updatable mirror of their repo -- I don't (yet) need to push changes back to FreeBSD.
> 
> I'd like to modify git-svn to support multiple (at least two) -b parameters, so I could import the FreeBSD repo with something like
> 	git svn init --trunk=head \
> 		--branches=stable \
> 		--branches=releng \
> 		--tags=release \
> 		file:///local/mirror/of/svn.freebsd.org/base/
> 	git svn fetch
> 
> Is this approach wise or even feasible?  A couple of possible show-stoppers I see are:

After doing the init with "--branches=stable" (without releng), do git config --add svn-remote.svn.branches 'releng/*:refs/remotes/*'

This gives you two branches refspecs in .git/config (feel free to put them in different dirs under remotes if there may be name clashes between stable and releng branches).

> 
> - The releng/X.Y branches are created from the stable/X  tree, where X itself doesn't exist from the start.

Well, I assume X exists at the time of creation of X.Y. That's all that is needed.

> 
> - FreeBSD likes to make single commits that spans multiple branches.  This is usually done as a security patch: One commit can, for example, touch /head, /stable/X, /stable/W, and several branches under /releng.

I assume that git-svn converts such an svn commit into several git commits, one for each affected branch. Since you have a local mirror it's easy to try out.

> 
> If this isn't completely off the wall, I'd appreciate some pointers on the git-svn.perl code.  I've tried searching through the code to see how the -b parameter is handled, but I got lost pretty quickly.  How does the fetch command deal with branches?

I think there's no need to hack git-svn. Let us know how it goes with the two branches configs.

Michael
Marc Branchaud· Jun 15, 2009, 17:29 UTC · re: Michael J Gruber · lore

Re: git svn: Supporting multiple branch subdirs?

Michael J Gruber wrote:
Show 7 quoted lines
> 
> After doing the init with "--branches=stable" (without releng), do
> git config --add svn-remote.svn.branches 'releng/*:refs/remotes/*'
> 
> This gives you two branches refspecs in .git/config (feel free to put
> them in different dirs under remotes if there may be name clashes
> between stable and releng branches).
That doesn't seem to work...
Here's my .git/config file before the first fetch:
[svn-remote "svn"]
        useSvnsyncProps = 1
        ignore-paths = ^[^/]+/(?:cvs2svn|projects|svnadmin|user|vendor.*)
        url = file:///home/marcnarc/Code/FreeBSD-all-mirror/base
        fetch = head:refs/remotes/trunk
        branches = stable/*:refs/remotes/stable/*
        branches = releng/*:refs/remotes/releng/*
        tags = release/*:refs/remotes/tags/*
(Shouldn't that fetch line be head:refs/remots/head, since there's nothing called "trunk" in the svn repo?  I used git-svn init --trunk=head ...)
With this, git-svn seems to be (mostly) ignoring the branches in stable/.  I've run the first fetch past the point where the first stable/ branch is created ("stable/2.0.5"), but in the repository (while the fetch command is still going, BTW, currently somewhere around commit 15,000), I get:
	$ git branch -a
	  remotes/releng/2.0.5
	  remotes/releng/ALPHA_2_0
	  remotes/releng/BETA_2_0
	  remotes/tags/2.0
	  remotes/tags/2.0.5
	  remotes/tags/2.1.0
	  remotes/tags/2.1.0@12369
	  remotes/trunk
At this point, git-svn has passed the commit that created the stable/2.0.5 branch (8869).  Mind you, nothing was ever committed to that branch, so it's basically a tag.  However, remotes/tags/2.0.5 is at svn commit 9201, which is the correct for the releases/2.0.5 tag.  remotes/tags/2.1.0 is also correct for the tagging of the 2.1.0 release.
git-svn has also passed the commit that created the stable/2.1 branch (9211), as well as later commits that only apply to that branch.  AFAICT, remotes/tags/2.1.0@12369 (which is not an actual tag or branch in the svn repo) is the stable/2.1 branch up to commit 12369, but there have been later commits to stable/2.1 since then that git-svn has seen.  Those later commits don't seem to be in the git repository at all.
The releng/* branches look fine, though.
Show 5 quoted lines
>> - The releng/X.Y branches are created from the stable/X  tree, where X
>> itself doesn't exist from the start.
> 
> Well, I assume X exists at the time of creation of X.Y. That's all that
> is needed.
Yes, that assumption is correct (it seems the 2.x releng branches were created off the trunk, not from a stable/ branch).
Show 7 quoted lines
>> - FreeBSD likes to make single commits that spans multiple branches.
>> This is usually done as a security patch: One commit can, for example,
>> touch /head, /stable/X, /stable/W, and several branches under /releng.
> 
> I assume that git-svn converts such an svn commit into several git
> commits, one for each affected branch. Since you have a local mirror
> it's easy to try out.
Yep, git-svn does just that.  Nice!
		M.
Michael J Gruber· Jun 17, 2009, 14:25 UTC · re: Marc Branchaud · lore

Re: git svn: Supporting multiple branch subdirs?

Marc Branchaud venit, vidit, dixit 15.06.2009 19:29:
Show 10 quoted lines
> Michael J Gruber wrote:
>>
>> After doing the init with "--branches=stable" (without releng), do
>> git config --add svn-remote.svn.branches 'releng/*:refs/remotes/*'
>>
>> This gives you two branches refspecs in .git/config (feel free to put
>> them in different dirs under remotes if there may be name clashes
>> between stable and releng branches).
> 
> That doesn't seem to work...

No, it doesn't I'm sorry. I knew that git-svn lumps multiple command line arguments into one, but it does so also for config lines. I didn't know that but confirmed from a test (and the source).

Now, one last resort would be a more complicated regexp. Unfortunately, something like

branches = (releng|stable)/*:refs/remotes/*/*
does not work (regexps don't work, only globs)
and something like
branches = */*:refs/remotes/*/*
works in principle but gets everything under head wrong.
Show 13 quoted lines
> 
> Here's my .git/config file before the first fetch:
> 
> [svn-remote "svn"]
>         useSvnsyncProps = 1
>         ignore-paths = ^[^/]+/(?:cvs2svn|projects|svnadmin|user|vendor.*)
>         url = file:///home/marcnarc/Code/FreeBSD-all-mirror/base
>         fetch = head:refs/remotes/trunk
>         branches = stable/*:refs/remotes/stable/*
>         branches = releng/*:refs/remotes/releng/*
>         tags = release/*:refs/remotes/tags/*
> 
> (Shouldn't that fetch line be head:refs/remots/head, since there's nothing called "trunk" in the svn repo?  I used git-svn init --trunk=head ...)

The left hand side of the refspec refers to the svn repo, the right hand side only names your local ref. Feel free to put "head" there, although this can be confused very easily with "HEAD" which has special meaning in git.

Show 7 quoted lines
>> I assume that git-svn converts such an svn commit into several git
>> commits, one for each affected branch. Since you have a local mirror
>> it's easy to try out.
> 
> Yep, git-svn does just that.  Nice!
> 
> 		M.
At least I got one thing right :)

I've used multiple svn-remote sections before (in cases where the repo structure changed) but I'm afraid this is of no use here. Looking at the source of git-svn, one sees that "tags" and "branches" are basically treated the same way, they are simply members of a "remote" struct/object/whatever it's called in perl and only differ in their standard refspec. Noone keeps us from adding "branches2" for a quick hack and checking whether everything stays sane... (git-svn perl often loops over "branches tags", that needs to be replaced).

In fact, I think the way to go would be replacing the two by an array. As a next step, the command line parsing and config reading needs to be changed so that multiple branches or tags entries end up generating multiple members of that array. Now this only needs to be implemented ;)

Michael

P.S.: Let me know if you give it a shot, so that we don't duplicate our waste of time...

Marc Branchaud· Jun 17, 2009, 15:25 UTC · re: Michael J Gruber · lore

Re: git svn: Supporting multiple branch subdirs?

Michael J Gruber wrote:
Show 8 quoted lines
> 
>> (Shouldn't that fetch line be head:refs/remots/head, since there's nothing called
>> "trunk" in the svn repo?  I used git-svn init --trunk=head ...)
> 
> The left hand side of the refspec refers to the svn repo, the right hand
> side only names your local ref. Feel free to put "head" there, although
> this can be confused very easily with "HEAD" which has special meaning
> in git.
Sure.  I was mostly wondering why --trunk=head didn't just reuse the name in my local ref.  A minor bug, perhaps?
(And the FreeBSD community commonly refers to that branch as "current", so that's a name I can use locally.)
> P.S.: Let me know if you give it a shot, so that we don't duplicate our
> waste of time...
I have some cycles to work on this, but I'd need some guidance around git-svn's internals.  I'm not even sure where to start implementing the "branches2" hack you described...
		M.
Michael J Gruber· Jun 18, 2009, 14:03 UTC · re: Marc Branchaud · lore

Re: git svn: Supporting multiple branch subdirs?

Marc Branchaud venit, vidit, dixit 17.06.2009 17:25:
Show 18 quoted lines
> Michael J Gruber wrote:
>>
>>> (Shouldn't that fetch line be head:refs/remots/head, since there's nothing called
>>> "trunk" in the svn repo?  I used git-svn init --trunk=head ...)
>>
>> The left hand side of the refspec refers to the svn repo, the right hand
>> side only names your local ref. Feel free to put "head" there, although
>> this can be confused very easily with "HEAD" which has special meaning
>> in git.
> 
> Sure.  I was mostly wondering why --trunk=head didn't just reuse the name in my local ref.  A minor bug, perhaps?
> 
> (And the FreeBSD community commonly refers to that branch as "current", so that's a name I can use locally.)
> 
>> P.S.: Let me know if you give it a shot, so that we don't duplicate our
>> waste of time...
> 
> I have some cycles to work on this, but I'd need some guidance around git-svn's internals.  I'm not even sure where to start implementing the "branches2" hack you described...

Heck it's perl, so don't even try to understand the code - after all, perl only barely missed the final round in the last competition for the next cryptographic algorithm!

That being said, I did some clueless hacking in git-svn.perl and let it run against the freebsd repo. Now, how's that:

~/src/git/git-svn fetch -r1:10000
git branch -r
  releng/2.0.5
  releng/ALPHA_2_0
  releng/BETA_2_0
  stable/2.0.5
  stable/2.1
  tags/2.0
  tags/2.0.5
  trunk
The revision graph looks OK as well. The git-svn config which I used is:
[svn-remote "svn"]
        url = svn://svn.freebsd.org/base
        fetch = head:refs/remotes/trunk
        branches = releng/*:refs/remotes/releng/*
        branchse = stable/*:refs/remotes/stable/*
        tags = release/*:refs/remotes/tags/*
No typo there, my git svn knows about "branches" and "branchse" now ;)
BTW: In fact there is overlap between releng and stable branches in the
feebsd repo, see 2.0.5.
I'll send a monkey patch in a minute.
Michael
Marc Branchaud· Jun 18, 2009, 14:28 UTC · re: Michael J Gruber · lore

Re: git svn: Supporting multiple branch subdirs?

Michael J Gruber wrote:
> 
> Heck it's perl, so don't even try to understand the code - after all,
> perl only barely missed the final round in the last competition for the
> next cryptographic algorithm!
:)
Hey, I'm not gonna dis something with such great job-security potential!
Show 16 quoted lines
> That being said, I did some clueless hacking in git-svn.perl and let it
> run against the freebsd repo. Now, how's that:
> 
> ~/src/git/git-svn fetch -r1:10000
> 
> git branch -r
>   releng/2.0.5
>   releng/ALPHA_2_0
>   releng/BETA_2_0
>   stable/2.0.5
>   stable/2.1
>   tags/2.0
>   tags/2.0.5
>   trunk
> 
> The revision graph looks OK as well. The git-svn config which I used is:
Nice!
Show 10 quoted lines
> [svn-remote "svn"]
>         url = svn://svn.freebsd.org/base
>         fetch = head:refs/remotes/trunk
>         branches = releng/*:refs/remotes/releng/*
>         branchse = stable/*:refs/remotes/stable/*
>         tags = release/*:refs/remotes/tags/*
> 
> No typo there, my git svn knows about "branches" and "branchse" now ;)
> BTW: In fact there is overlap between releng and stable branches in the
> feebsd repo, see 2.0.5.
By "overlap" do you mean single commits that touch both branches?  FreeBSD does that a lot.
But for 2.0.5, it looks to me like the stable/2.0.5 branch was abandoned after the 2.0.5_ALPHA release.
> I'll send a monkey patch in a minute.
Fantastic!  Thanks so much for this.
		M.
Michael J Gruber· Jun 18, 2009, 16:00 UTC · re: Marc Branchaud · lore

Re: git svn: Supporting multiple branch subdirs?

Marc Branchaud venit, vidit, dixit 18.06.2009 16:28:
Show 41 quoted lines
> Michael J Gruber wrote:
>>
>> Heck it's perl, so don't even try to understand the code - after all,
>> perl only barely missed the final round in the last competition for the
>> next cryptographic algorithm!
> 
> :)
> 
> Hey, I'm not gonna dis something with such great job-security potential!
> 
>> That being said, I did some clueless hacking in git-svn.perl and let it
>> run against the freebsd repo. Now, how's that:
>>
>> ~/src/git/git-svn fetch -r1:10000
>>
>> git branch -r
>>   releng/2.0.5
>>   releng/ALPHA_2_0
>>   releng/BETA_2_0
>>   stable/2.0.5
>>   stable/2.1
>>   tags/2.0
>>   tags/2.0.5
>>   trunk
>>
>> The revision graph looks OK as well. The git-svn config which I used is:
> 
> Nice!
> 
>> [svn-remote "svn"]
>>         url = svn://svn.freebsd.org/base
>>         fetch = head:refs/remotes/trunk
>>         branches = releng/*:refs/remotes/releng/*
>>         branchse = stable/*:refs/remotes/stable/*
>>         tags = release/*:refs/remotes/tags/*
>>
>> No typo there, my git svn knows about "branches" and "branchse" now ;)
>> BTW: In fact there is overlap between releng and stable branches in the
>> feebsd repo, see 2.0.5.
> 
> By "overlap" do you mean single commits that touch both branches?  FreeBSD does that a lot.

No, I just meant that there are stable and releng branches with the same name, so that they can't both be mapped to refs/remotes/* but need different "namespaces" (subdirs) as above.

Michael
Michael J Gruber· Jun 18, 2009, 14:31 UTC · re: Michael J Gruber · lore

[MONKEY PATCH] git-svn: allow two branch configurations

This is a MONKEY PATCH which introduces a long option and config variable "branchse" analogous to "branches", with a short option "-B". It has the same meaning and can be used to accomadate svn repos with two different branches subdirectories.

Signed-off-by: Michael J Gruber <git@drmicha.warpmail.net>
---
 git-svn.perl |   18 ++++++++++--------
 1 files changed, 10 insertions(+), 8 deletions(-)

Well, this is not a serious patch per se, but it shows that git-svn can very well handle multiple branches configs once it is told to read and use them. I don't think it's possible to do this with globs, and extending globs to regexps looks like a more complicated approach (although they are used internally).

I see two viable options for a real patch now:
- Extend $remote->branches to be an array and use "config --get-all" to
  read the config; do the same for tags.
- Use one single array for branches as well as tags.

Eric, which way do you prefer? The first one is simpler and may be even doable for me. The second looks more complicated mainly because of "git svn branch -t" (which element of the combined array is tags?), even though it's more natural if one thinks about the way svn works.

Michael
diff --git a/git-svn.perl b/git-svn.perl
index 3301797..e6a9422 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -92,11 +92,12 @@ 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, $_branchse, $_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,
+                  'branchse|B=s' => \$_branchse,
                   'stdlayout|s' => \$_stdlayout,
                   'minimize-url|m' => \$Git::SVN::_minimize_url,
 		  'no-metadata' => sub { $icv{noMetadata} = 1 },
@@ -358,7 +359,7 @@ sub init_subdir {
 sub cmd_clone {
 	my ($url, $path) = @_;
 	if (!defined $path &&
-	    (defined $_trunk || defined $_branches || defined $_tags ||
+	    (defined $_trunk || defined $_branches || defined $_branchse || defined $_tags ||
 	     defined $_stdlayout) &&
 	    $url !~ m#^[a-z\+]+://#) {
 		$path = $url;
@@ -375,7 +376,7 @@ sub cmd_init {
 		$_tags = 'tags' if (!defined $_tags);
 		$_branches = 'branches' if (!defined $_branches);
 	}
-	if (defined $_trunk || defined $_branches || defined $_tags) {
+	if (defined $_trunk || defined $_branches || defined $_branchse || defined $_tags) {
 		return cmd_multi_init(@_);
 	}
 	my $url = shift or die "SVN repository location required ",
@@ -800,7 +801,7 @@ sub cmd_proplist {
 
 sub cmd_multi_init {
 	my $url = shift;
-	unless (defined $_trunk || defined $_branches || defined $_tags) {
+	unless (defined $_trunk || defined $_branches || defined $_branchse || defined $_tags) {
 		usage(1);
 	}
 
@@ -825,9 +826,10 @@ sub cmd_multi_init {
 						   undef, $trunk_ref);
 		}
 	}
-	return unless defined $_branches || defined $_tags;
+	return unless defined $_branches || defined $_branchse || defined $_tags;
 	my $ra = $url ? Git::SVN::Ra->new($url) : undef;
 	complete_url_ls_init($ra, $_branches, '--branches/-b', $_prefix);
+	complete_url_ls_init($ra, $_branchse, '--branchse/-B', $_prefix);
 	complete_url_ls_init($ra, $_tags, '--tags/-t', $_prefix . 'tags/');
 }
 
@@ -1563,7 +1565,7 @@ sub fetch_all {
 	my $base = defined $fetch ? $head : 0;
 
 	# read the max revs for wildcard expansion (branches/*, tags/*)
-	foreach my $t (qw/branches tags/) {
+	foreach my $t (qw/branches branchse tags/) {
 		defined $remote->{$t} or next;
 		push @globs, $remote->{$t};
 		my $max_rev = eval { tmp_config(qw/--int --get/,
@@ -1609,7 +1611,7 @@ sub read_all_remotes {
 			$r->{$1}->{svm} = {};
 		} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
 			$r->{$1}->{url} = $2;
-		} elsif (m!^(.+)\.(branches|tags)=
+		} elsif (m!^(.+)\.(branches|branchse|tags)=
 		           (.*):refs/remotes/(.+)\s*$/!x) {
 			my ($p, $g) = ($3, $4);
 			my $rs = $r->{$1}->{$2} = {
@@ -1760,7 +1762,7 @@ 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/) {
+		foreach (qw/branches branchse tags/) {
 			resolve_local_globs($u, $fetch,
 			                    $remotes->{$repo_id}->{$_});
 		}
-- 
1.6.3.2.406.gd6a466
Marc Branchaud· Jun 22, 2009, 14:50 UTC · re: Michael J Gruber · lore

Re: [MONKEY PATCH] git-svn: allow two branch configurations

I tried this patch over the weekend, and it worked perfectly.
I've imported the head/, releng/, and stable/ FreeBSD branches, plus the release/ tags -- some 190,000 svn revisions.  The git repository is 720MB, compared to 4.6GB for the synsync'd mirror of the svn repo.
Michael J Gruber wrote:
Show 10 quoted lines
> 
> I see two viable options for a real patch now:
> - Extend $remote->branches to be an array and use "config --get-all" to
>   read the config; do the same for tags.
> - Use one single array for branches as well as tags.
> 
> Eric, which way do you prefer? The first one is simpler and may be even
> doable for me. The second looks more complicated mainly because of "git
> svn branch -t" (which element of the combined array is tags?), even
> though it's more natural if one thinks about the way svn works.
I favor the first option, mainly because of its simplicity and because it seems natural to me to keep branches and tags separate, despite svn's mechanics.
With this capability, the 'git svn branch' command (and 'git svn tag') would need to be extended to allow the user to specify the location of the new branch (or tag).  I suggest a -d (for "destination") argument.  -d would be required if there's more than one branches (or tags, if -t is also specified) config option for the svn-remote.  The -d value must successfully glob against the LHS of one of the branches' (or tags') refspecs.
Thanks, Michael!  I'm going to start putting together some unit tests for this while the implementation details get sorted out.
		M.
Marc Branchaud· Jun 23, 2009, 17:02 UTC · re: Marc Branchaud · lore

[PATCH] git svn: Support multiple branch and tag paths in the svn repository.

This enables git-svn.perl to read multiple 'branches' and 'tags' entries in svn-remote config sections. The init and clone subcommands also support multiple --branches and --tags arguments.

The branch (and tag) subcommand gets a new argument: --destination (or -d). This argument is required if there are multiple branches (or tags) entries configured for the remote Subversion repository. The argument's value specifies which branch (or tag) path to use to create the branch (or tag). The specified value must match the left side (without wildcards) of one of the branches (or tags) refspecs in the svn-remote's config.

Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>
---
I got carried away making unit tests and went and implemented most of this...

I'm fairly happy with this, except for the way the branch subcommand matches refspecs. The patch does a simple string comparison, but it'd be better to do an actual glob. I just couldn't track down the right function for that, so I left it as a strcmp and hope that a gitizen can tell me how to glob here.

(ps. I'm trying a new way to send patches -- apologies if it's mangled!)
 git-svn.perl                         |   79 +++++++++++++++++-------
 t/t9138-git-svn-multiple-branches.sh |  114 ++++++++++++++++++++++++++++++++++
 2 files changed, 171 insertions(+), 22 deletions(-)
 create mode 100755 t/t9138-git-svn-multiple-branches.sh
diff --git a/git-svn.perl b/git-svn.perl
index ef1d30d..0f2cb8d 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -62,7 +62,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,
@@ -90,11 +90,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 },
@@ -139,11 +139,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",
@@ -353,7 +355,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;
@@ -366,10 +368,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 ",
@@ -587,7 +589,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 || ());
 
@@ -794,7 +820,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);
 	}
 
@@ -819,10 +845,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 {
@@ -1093,6 +1123,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)) );
@@ -1548,7 +1579,9 @@ 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};
+		foreach my $b (@{$remote->{$t}}) {
+		    push @globs, $b;
+		}
 		my $max_rev = eval { tmp_config(qw/--int --get/,
 		                         "svn-remote.$repo_id.${t}-maxRev") };
 		if (defined $max_rev && ($max_rev < $base)) {
@@ -1595,15 +1628,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;
 		}
 	}
 
@@ -1743,9 +1777,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 0000000..9725ccf
--- /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
-- 
1.6.3.3
Eric Wong· Jun 25, 2009, 09:36 UTC · re: Marc Branchaud · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Marc Branchaud <marcnarc@xiplink.com> wrote:
Show 22 quoted lines
> This enables git-svn.perl to read multiple 'branches' and 'tags' entries in
> svn-remote config sections.  The init and clone subcommands also support
> multiple --branches and --tags arguments.
> 
> The branch (and tag) subcommand gets a new argument: --destination (or -d).
> This argument is required if there are multiple branches (or tags) entries
> configured for the remote Subversion repository.  The argument's value
> specifies which branch (or tag) path to use to create the branch (or tag).
> The specified value must match the left side (without wildcards) of one of
> the branches (or tags) refspecs in the svn-remote's config.
> 
> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>
> ---
> 
> I got carried away making unit tests and went and implemented most of this...
> 
> I'm fairly happy with this, except for the way the branch subcommand matches
> refspecs.  The patch does a simple string comparison, but it'd be better to do
> an actual glob.  I just couldn't track down the right function for that, so I
> left it as a strcmp and hope that a gitizen can tell me how to glob here.
> 
> (ps. I'm trying a new way to send patches -- apologies if it's mangled!)

Thanks Marc! Everything looks fine here; I don't think I'll have time to test it myself any time soon but your test case looks good and doesn't break any of the other tests :) Sorry for the delay, I haven't had access to my computer or email much in the past few weeks.

Acked and and pushed out to git://git.bogomips.org/git-svn along with a followup patch to convert the glob to a regexp for branching:

>From f7050599310c18bd67b35b8d59486116b30ff1f6 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Thu, 25 Jun 2009 02:28:15 -0700
Subject: [PATCH] git-svn: convert globs to regexps for branch destinations
Marc Branchaud wrote:
Show 5 quoted lines
> I'm fairly happy with this, except for the way the branch
> subcommand matches refspecs.  The patch does a simple string
> comparison, but it'd be better to do an actual glob.  I just
> couldn't track down the right function for that, so I left it as
> a strcmp and hope that a gitizen can tell me how to glob here.
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 I haven't actually tested it, and yes, it should probably be
 moved to Git.pm even though it's not necessarily git-only...
 git-svn.perl |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index 48e8aad..6c42e2a 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -646,7 +646,9 @@ sub cmd_branch {
 		            " with the --destination argument.\n";
 		}
 		foreach my $g (@{$allglobs}) {
-			if ($_branch_dest eq $g->{path}->{left}) {
+			# SVN::Git::Editor could probably be moved to Git.pm..
+			my $re = SVN::Git::Editor::glob2pat($g->{path}->{left});
+			if ($_branch_dest =~ /$re/) {
 				$glob = $g;
 				last;
 			}
-- 
Eric Wong
Junio C Hamano· Jun 25, 2009, 22:25 UTC · re: Eric Wong · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Eric Wong <normalperson@yhbt.net> writes:
> Acked and and pushed out to git://git.bogomips.org/git-svn along with a
> followup patch ...
Hmmm...
t9138-git-svn-multiple-branches.sh does not seem to pass for me.
    ...
    A    svn_project/tags_A/1.0
    A    svn_project/tags_A/1.0/a.file
    Checked out revision 3.
    ./test-lib.sh: line 329: .: filename argument required
    .: usage: . filename [arguments]
    FATAL: Unexpected exit with code 2
Eric Wong· Jun 26, 2009, 00:33 UTC · re: Junio C Hamano · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Junio C Hamano <gitster@pobox.com> wrote:
Show 16 quoted lines
> Eric Wong <normalperson@yhbt.net> writes:
> 
> > Acked and and pushed out to git://git.bogomips.org/git-svn along with a
> > followup patch ...
> 
> Hmmm...
> 
> t9138-git-svn-multiple-branches.sh does not seem to pass for me.
> 
>     ...
>     A    svn_project/tags_A/1.0
>     A    svn_project/tags_A/1.0/a.file
>     Checked out revision 3.
>     ./test-lib.sh: line 329: .: filename argument required
>     .: usage: . filename [arguments]
>     FATAL: Unexpected exit with code 2
Oops, I wasn't reading the test too closely[1], this should fix it:
>From 1ad8ff7b49f508ad37081a709d4230833564eca2 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalperson@yhbt.net>
Date: Thu, 25 Jun 2009 16:09:59 -0700
Subject: [PATCH] t9138: remove stray "." in tests

This breaks bash and probably some other shells, but worked fine with dash in my limited testing.

Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 t/t9138-git-svn-multiple-branches.sh |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/t/t9138-git-svn-multiple-branches.sh b/t/t9138-git-svn-multiple-branches.sh
index 9725ccf..37ecdb0 100755
--- a/t/t9138-git-svn-multiple-branches.sh
+++ b/t/t9138-git-svn-multiple-branches.sh
@@ -22,7 +22,6 @@ test_expect_success 'setup svnrepo' '
 			  "$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" \
-- 
Eric Wong

[1] - the test should use the new svn_cmd wrapper and avoid cd + cd ..
which is more error prone than (cd ).  I'll try to find time to fix
it later tonight.
Andreas Ericsson· Jun 26, 2009, 05:18 UTC · re: Eric Wong · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Eric Wong wrote:
Show 27 quoted lines
> Junio C Hamano <gitster@pobox.com> wrote:
>> Eric Wong <normalperson@yhbt.net> writes:
>>
>>> Acked and and pushed out to git://git.bogomips.org/git-svn along with a
>>> followup patch ...
>> Hmmm...
>>
>> t9138-git-svn-multiple-branches.sh does not seem to pass for me.
>>
>>     ...
>>     A    svn_project/tags_A/1.0
>>     A    svn_project/tags_A/1.0/a.file
>>     Checked out revision 3.
>>     ./test-lib.sh: line 329: .: filename argument required
>>     .: usage: . filename [arguments]
>>     FATAL: Unexpected exit with code 2
> 
> Oops, I wasn't reading the test too closely[1], this should fix it:
> 
> From 1ad8ff7b49f508ad37081a709d4230833564eca2 Mon Sep 17 00:00:00 2001
> From: Eric Wong <normalperson@yhbt.net>
> Date: Thu, 25 Jun 2009 16:09:59 -0700
> Subject: [PATCH] t9138: remove stray "." in tests
> 
> This breaks bash and probably some other shells, but worked
> fine with dash in my limited testing.
> 

Please reword to "The stray dots break bash and ..." Right now it reads as if the patch itself breaks bash et al.

-- 
Andreas Ericsson                   andreas.ericsson@op5.se
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

Considering the successes of the wars on alcohol, poverty, drugs and
terror, I think we should give some serious thought to declaring war
on peace.
Eric Wong· Jun 26, 2009, 18:11 UTC · re: Andreas Ericsson · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Andreas Ericsson <ae@op5.se> wrote:
Show 30 quoted lines
> Eric Wong wrote:
>> Junio C Hamano <gitster@pobox.com> wrote:
>>> Eric Wong <normalperson@yhbt.net> writes:
>>>
>>>> Acked and and pushed out to git://git.bogomips.org/git-svn along with a
>>>> followup patch ...
>>> Hmmm...
>>>
>>> t9138-git-svn-multiple-branches.sh does not seem to pass for me.
>>>
>>>     ...
>>>     A    svn_project/tags_A/1.0
>>>     A    svn_project/tags_A/1.0/a.file
>>>     Checked out revision 3.
>>>     ./test-lib.sh: line 329: .: filename argument required
>>>     .: usage: . filename [arguments]
>>>     FATAL: Unexpected exit with code 2
>>
>> Oops, I wasn't reading the test too closely[1], this should fix it:
>>
>> From 1ad8ff7b49f508ad37081a709d4230833564eca2 Mon Sep 17 00:00:00 2001
>> From: Eric Wong <normalperson@yhbt.net>
>> Date: Thu, 25 Jun 2009 16:09:59 -0700
>> Subject: [PATCH] t9138: remove stray "." in tests
>>
>> This breaks bash and probably some other shells, but worked
>> fine with dash in my limited testing.
>
> Please reword to "The stray dots break bash and ..."
> Right now it reads as if the patch itself breaks bash et al.
Oops, reworded to:
    t9138: remove stray dot in test which broke bash
    The stray dot broke bash and probably some other shells,
    but worked fine with dash in my limited testing.
And pushed out to git://git.bogomips.org/git-svn

There's also some other changes in there that have been sitting a while that may have been lost in the shuffle in the weeks I was offline:

Ben Jackson (3):
      Add 'git svn help [cmd]' which works outside a repo.
      git-svn: speed up find_rev_before
      Add 'git svn reset' to unwind 'git svn fetch'
Eric Wong (2):
      git-svn: convert globs to regexps for branch destinations
      t9138: remove stray dot in test which broke bash
Marc Branchaud (1):
      git svn: Support multiple branch and tag paths in the svn repository.
Thomas Rast (1):
      git-svn: let 'dcommit $rev' work on $rev instead of HEAD
-- 
Eric Wong
Marc Branchaud· Jun 26, 2009, 19:20 UTC · re: Eric Wong · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Thanks a bunch, Eric!
Can I assume that since you've taken the commit into your repo that the feature will find itself into Junio's git at some point?
I'll send out a documentation patch shortly.
Is the svn_cmd wrapper simply a drop-in replacement for svn?  I can make that change, too.
		M.
Eric Wong wrote:
Show 68 quoted lines
> Marc Branchaud <marcnarc@xiplink.com> wrote:
>> This enables git-svn.perl to read multiple 'branches' and 'tags' entries in
>> svn-remote config sections.  The init and clone subcommands also support
>> multiple --branches and --tags arguments.
>>
>> The branch (and tag) subcommand gets a new argument: --destination (or -d).
>> This argument is required if there are multiple branches (or tags) entries
>> configured for the remote Subversion repository.  The argument's value
>> specifies which branch (or tag) path to use to create the branch (or tag).
>> The specified value must match the left side (without wildcards) of one of
>> the branches (or tags) refspecs in the svn-remote's config.
>>
>> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>
>> ---
>>
>> I got carried away making unit tests and went and implemented most of this...
>>
>> I'm fairly happy with this, except for the way the branch subcommand matches
>> refspecs.  The patch does a simple string comparison, but it'd be better to do
>> an actual glob.  I just couldn't track down the right function for that, so I
>> left it as a strcmp and hope that a gitizen can tell me how to glob here.
>>
>> (ps. I'm trying a new way to send patches -- apologies if it's mangled!)
> 
> Thanks Marc!  Everything looks fine here; I don't think I'll have time
> to test it myself any time soon but your test case looks good and
> doesn't break any of the other tests :)  Sorry for the delay, I haven't
> had access to my computer or email much in the past few weeks.
> 
> Acked and and pushed out to git://git.bogomips.org/git-svn along with a
> followup patch to convert the glob to a regexp for branching:
> 
> From f7050599310c18bd67b35b8d59486116b30ff1f6 Mon Sep 17 00:00:00 2001
> From: Eric Wong <normalperson@yhbt.net>
> Date: Thu, 25 Jun 2009 02:28:15 -0700
> Subject: [PATCH] git-svn: convert globs to regexps for branch destinations
> 
> Marc Branchaud wrote:
>> I'm fairly happy with this, except for the way the branch
>> subcommand matches refspecs.  The patch does a simple string
>> comparison, but it'd be better to do an actual glob.  I just
>> couldn't track down the right function for that, so I left it as
>> a strcmp and hope that a gitizen can tell me how to glob here.
> 
> Signed-off-by: Eric Wong <normalperson@yhbt.net>
> ---
> 
>  I haven't actually tested it, and yes, it should probably be
>  moved to Git.pm even though it's not necessarily git-only...
> 
>  git-svn.perl |    4 +++-
>  1 files changed, 3 insertions(+), 1 deletions(-)
> 
> diff --git a/git-svn.perl b/git-svn.perl
> index 48e8aad..6c42e2a 100755
> --- a/git-svn.perl
> +++ b/git-svn.perl
> @@ -646,7 +646,9 @@ sub cmd_branch {
>  		            " with the --destination argument.\n";
>  		}
>  		foreach my $g (@{$allglobs}) {
> -			if ($_branch_dest eq $g->{path}->{left}) {
> +			# SVN::Git::Editor could probably be moved to Git.pm..
> +			my $re = SVN::Git::Editor::glob2pat($g->{path}->{left});
> +			if ($_branch_dest =~ /$re/) {
>  				$glob = $g;
>  				last;
>  			}
Marc Branchaud· Jun 26, 2009, 20:49 UTC · re: Marc Branchaud · lore

[PATCH] git svn: Doc update for multiple branch and tag paths

Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>
---
Includes changes to the description to be more accurate.
 Documentation/git-svn.txt |   44 +++++++++++++++++++++++++++++++++++---------
 1 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index bb22d8e..5cbcdf4 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -3,7 +3,7 @@ git-svn(1)
 
 NAME
 ----
-git-svn - Bidirectional operation between a single Subversion branch and git
+git-svn - Bidirectional operation between a Subversion repository and git
 
 SYNOPSIS
 --------
@@ -15,13 +15,12 @@ DESCRIPTION
 It provides a bidirectional flow of changes between a Subversion and a git
 repository.
 
-'git-svn' can track a single Subversion branch simply by using a
-URL to the branch, follow branches laid out in the Subversion recommended
-method (trunk, branches, tags directories) with the --stdlayout option, or
-follow branches in any layout with the -T/-t/-b options (see options to
-'init' below, and also the 'clone' command).
+'git-svn' can track a standard Subversion repository,
+following the common "trunk/branches/tags" layout, with the --stdlayout option.
+It can also follow branches and tags in any layout with the -T/-t/-b options
+(see options to 'init' below, and also the 'clone' command).
 
-Once tracking a Subversion branch (with any of the above methods), the git
+Once tracking a Subversion repository (with any of the above methods), the git
 repository can be updated from Subversion by the 'fetch' command and
 Subversion updated from git by the 'dcommit' command.
 
@@ -48,8 +47,11 @@ COMMANDS
 --stdlayout;;
 	These are optional command-line options for init.  Each of
 	these flags can point to a relative repository path
-	(--tags=project/tags') or a full url
-	(--tags=https://foo.org/project/tags). The option --stdlayout is
+	(--tags=project/tags) or a full url
+	(--tags=https://foo.org/project/tags).
+	You can specify more than one --tags and/or --branches options, in case
+	your Subversion repository places tags or branches under multiple paths.
+	The option --stdlayout is
 	a shorthand way of setting trunk,tags,branches as the relative paths,
 	which is the Subversion default. If any of the other options are given
 	as well, they take precedence.
@@ -204,6 +206,20 @@ config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
 	Create a tag by using the tags_subdir instead of the branches_subdir
 	specified during git svn init.
 
+-d;;
+--destination;;
+	If more than one --branches (or --tags) option was given to the 'init'
+	or 'clone' command, you must provide the location of the branch (or
+	tag) you wish to create in the SVN repository.  The value of this
+	option must match one of the paths specified by a --branches (or
+	--tags) option.  You can see these paths with the commands
++
+	git config --get-all svn-remote.<name>.branches
+	git config --get-all svn-remote.<name>.tags
++
+where <name> is the name of the SVN repository as specified by the -R option to
+'init' (or "svn" by default).
+
 'tag'::
 	Create a tag in the SVN repository. This is a shorthand for
 	'branch -t'.
@@ -669,6 +685,16 @@ already dcommitted.  It is considered bad practice to --amend commits
 you've already pushed to a remote repository for other users, and
 dcommit with SVN is analogous to that.
 
+When using multiple --branches or --tags, 'git-svn' does not automatically
+handle name collisions (for example, if two branches from different paths have
+the same name, or if a branch and a tag have the same name).  In these cases,
+use 'init' to set up your git repository then, before your first 'fetch', edit
+the .git/config file so that the branches and tags are associated with
+different name spaces.  For example:
+
+	branches = stable/*:refs/remotes/svn/stable/*
+	branches = debug/*:refs/remotes/svn/debug/*
+
 BUGS
 ----
 
-- 
1.6.3.3
Eric Wong· Jun 26, 2009, 20:57 UTC · re: Marc Branchaud · lore

Re: [PATCH] git svn: Support multiple branch and tag paths in the svn repository.

Marc Branchaud <marcnarc@xiplink.com> wrote:
> Thanks a bunch, Eric!

No problem, thanks for adding this feature since people have wanted it for a while now and I couldn't get a chance to deliver it.

> Can I assume that since you've taken the commit into your repo that
> the feature will find itself into Junio's git at some point?
Yes.
> I'll send out a documentation patch shortly.
>
> Is the svn_cmd wrapper simply a drop-in replacement for svn?  I can
> make that change, too.

Yes, it should be a simple substitution. Can you also start using "(cd $FOO && stuff && more_stuff)" to chdir in subshells to avoid having to keep track of "cd .."? Thanks again!

-- 
Eric Wong
Marc Branchaud· Jun 26, 2009, 21:08 UTC · re: Eric Wong · lore

[PATCH] git svn: Fix t9138-multiple-branches to use svn_cmd and (cd ...) syntax.

---
This is against the master branch of Eric's git://git.bogomips.org/git-svn repo.
Eric, if there's anything else I can do to help this along please let me know!
		M.
 t/t9138-git-svn-multiple-branches.sh |  123 ++++++++++++++++++----------------
 1 files changed, 66 insertions(+), 57 deletions(-)
diff --git a/t/t9138-git-svn-multiple-branches.sh b/t/t9138-git-svn-multiple-branches.sh
index 37ecdb0..cb9a6d2 100755
--- a/t/t9138-git-svn-multiple-branches.sh
+++ b/t/t9138-git-svn-multiple-branches.sh
@@ -14,58 +14,58 @@ test_expect_success 'setup svnrepo' '
 		project/tags_A \
 		project/tags_B &&
 	echo 1 > project/trunk/a.file &&
-	svn import -m "$test_description" project "$svnrepo/project" &&
+	svn_cmd 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 &&
+	svn_cmd cp -m "Branch 1" "$svnrepo/project/trunk" \
+				 "$svnrepo/project/b_one/first" &&
+	svn_cmd cp -m "Tag 1" "$svnrepo/project/trunk" \
+			      "$svnrepo/project/tags_A/1.0" &&
+	svn_cmd 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" &&
+		svn_cmd ci -m "Change 1" trunk/a.file &&
+		svn_cmd cp -m "Branch 2" "$svnrepo/project/trunk" \
+					 "$svnrepo/project/b_one/second" &&
+		svn_cmd 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" &&
+		svn_cmd ci -m "Change 2" trunk/a.file &&
+		svn_cmd cp -m "Branch 3" "$svnrepo/project/trunk" \
+					 "$svnrepo/project/b_two/1" &&
+		svn_cmd 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 &&
+		svn_cmd ci -m "Change 3" trunk/a.file &&
+		svn_cmd cp -m "Branch 4" "$svnrepo/project/trunk" \
+					 "$svnrepo/project/b_two/2" &&
+		svn_cmd cp -m "Tag 4" "$svnrepo/project/trunk" \
+				      "$svnrepo/project/tags_A/4.0" &&
+		svn_cmd 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" &&
+		svn_cmd ci -m "Change 4" b_one/first/a.file &&
+		svn_cmd 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" &&
+		svn_cmd ci -m "Change 5" b_one/second/a.file &&
+		svn_cmd 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" &&
+		svn_cmd ci -m "Change 6" b_two/1/a.file &&
+		svn_cmd 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 ..
-	'
+		svn_cmd ci -m "Change 7" b_two/2/a.file &&
+		svn_cmd cp -m "Tag 8" "$svnrepo/project/b_two/2" \
+				      "$svnrepo/project/tags_B/v8"
+	)
+'
 
 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 &&
+	( cd git_project &&
 		git rev-parse refs/remotes/first &&
 		git rev-parse refs/remotes/second &&
 		git rev-parse refs/remotes/1 &&
@@ -77,37 +77,46 @@ test_expect_success 'clone multiple branch and tag paths' '
 		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 ..
-	'
+		git rev-parse refs/remotes/tags/v8
+	)
+'
 
 test_expect_success 'Multiple branch or tag paths require -d' '
-	cd git_project &&
+	( 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 git rev-parse refs/remotes/tags/Tagless
+	) &&
+	( cd svn_project &&
+		svn_cmd 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_must_fail test -d tags_B/Tagless
+	)
+'
 
 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 1" -d project/b_one New1 ) &&
+	( cd svn_project &&
+		svn_cmd 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 -m "New branch 2" -d project/b_two New2 ) &&
+	( cd svn_project &&
+		svn_cmd 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 branch -t -m "New tag 1" -d project/tags_A Tag1 ) &&
+	( cd svn_project &&
+		svn_cmd 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 )
-	'
+	( cd git_project &&
+		git svn tag -m "New tag 2" -d project/tags_B Tag2 ) &&
+	( cd svn_project &&
+		svn_cmd up && test -e tags_B/Tag2/a.file )
+'
 
 test_done
-- 
1.6.3.3
Eric Wong· Jun 26, 2009, 21:54 UTC · re: Marc Branchaud · lore

Re: [PATCH] git svn: Fix t9138-multiple-branches to use svn_cmd and (cd ...) syntax.

Marc Branchaud <marcnarc@xiplink.com> wrote:
Show 5 quoted lines
> ---
> 
> This is against the master branch of Eric's git://git.bogomips.org/git-svn repo.
> 
> Eric, if there's anything else I can do to help this along please let me know!
Thanks Marc, sign-off?
-- 
Eric Wong
Marc Branchaud· Jun 27, 2009, 15:03 UTC · re: Eric Wong · lore

Re: [PATCH] git svn: Fix t9138-multiple-branches to use svn_cmd and (cd ...) syntax.

Eric Wong wrote:
> 
> Thanks Marc, sign-off?
Doh!
Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>
		M.
Eric Wong· Jun 27, 2009, 22:08 UTC · re: Marc Branchaud · lore

Re: [PATCH] git svn: Fix t9138-multiple-branches to use svn_cmd and (cd ...) syntax.

Marc Branchaud <marcnarc@xiplink.com> wrote:
Show 7 quoted lines
> Eric Wong wrote:
>>
>> Thanks Marc, sign-off?
>
> Doh!
>
> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>

Thanks Marc! This and the other patch pushed out to git://git.bogomips.org/git-svn

-- 
Eric Wong

← back to recent threads