Using git to interface with svn repositories is awesome. You get all the advantages of git: local commits, squashing/amending commits, cheap branching, local stash, smart merging, wicked fast operation, etc. However, when you try to have git work properly with a non-standard svn repository layout, things tend to get wonky.
git-svn tries to be smart, it does so by doing what it thinks you want it to do and not exactly what you tell it to do.
First of all, a standard svn layout looks something like this:
- awesome_project
- trunk
- branches
- 1.0.x
- feature_a
- feature_b
- tags
- 1.0.0
- 1.0.1
Getting git to talk to such a repository is easy as pie:
$ git svn clone -s url://to/awesome_project
The -s option tells git to use the standard layout and automatically map your trunk/branches/tags as… master, branches, and tags. You probably want to tell it to fetch a specific revision to speed things up if you have a large repository.
But like everything else in life, things are not always that easy. You may run into times where you need to have git talk to a non-standard repository layout. Maybe something like this:
- awesome_project
- trunk
- branches
- fork_a
- trunk
- 1.0.0
- 1.0.1
- fork_b
- trunk
- 1.0.0
In my case, I happen to work on fork_a almost exclusively, so the most logical thing to do was:
$ git svn clone url://to/awesome_project/branches/fork_a
If you did something like that before, you may find that git svn thinks you meant to use awesome_project as the project root. The reason why it does that is git svn starts from the beginning of the history, and moves up until it finds /branches/fork_a. Unfortunately, when git sees standard svn directories (trunk, branches, and tags) in the project root, it incorrectly corrects your error for you.
Don’t you hate it when the computer does what it thinks you want it to do instead of what you tell it to do?
The solution to this problem is first initialize an empty git repository:
$ git svn init /path/to/awesome_project
Open up .git/config and change your svn-remote section to look something like this:
[svn-remote "svn"]
url = http://awesome_host/awesome_project
trunk = branches/fork_a/trunk:refs/remotes/svn/trunk
branches = branches/fork_a/*:refs/remotes/svn/branches/*
Save the changes and do a fetch on a more current revision.
This will treat each folder under branches/fork_a, including trunk, as git remote branches.
The key here is to not use the full url to the specific fork/branch you want to clone; instead, use the svn project root as the url and specify the path relative to the project root in the trunk/branches/fetch options in .git/config. This configuration setting is identical to using git svn clone/init with the –prefix option.
After doing a fetch (specifying a decently recent revision). Your git branch -r looks something like this:
$ git branch -r
svn/branches/1.0.0
svn/branches/1.0.1
svn/branches/trunk
And you’re done! You can now create feature/working branches from these remote branches and work with git and git svn as you normally would.
Hopefully in a future release, we will be able use regexes in place of globs for matching paths in .git/config. It will no doubt make interfacing with nonstandard svn repository layouts using git much easier.
Your repository layout may not be identical to the example I provided above, but this technique should be applicable to any reasonably organized repository layout. Right now my solution does not map the tags from svn (1.0.0, 1.0.1) as git tags. If you happen have a better solution, please share!