My team at Sun uses Subversion to host our “authoritative” source repository for Project Kenai. However, since most work is done on the trunk, many of us find it more convenient to work locally with Git, using the git svn subcommand heavily to keep ourselves up-to-date without interfering with others’ work.
When I first started using this combo, I had some early trouble keeping my local Git repository from getting horribly b0rked whenever there were edits made to the same files I had been working on locally. Having used CVS and Subversion for so long, I initially assumed such conflicts (and the manual merge steps they entailed) were simply part of the equation, even when working with a proper DVCS. However, by applying a little more discipline to my use of local branches, I’ve been able to basically eliminate manual merges, except in cases where the exact same line has literally been edited by multiple people.
My first, most critical discovery was to never use the fetch command. Instead, use rebase. Second, never pull the latest changes from Subversion into a working feature branch; instead, switch to your master branch, create a new branch for merging (I usually call mine “svn-merge”), and do your rebase there. After the rebase has finished, merge in your feature branch changes, and then use dcommit to push your changes upstream.
As an example, here are to commands I would use to check out a new local Git clone of the main Subverion repository, work on a single command, and then push it back into SVN:
viper:Work$ git svn clone https://example.com/svn/repo/trunk -r500:HEAD repo # ... lots of Git output here ... viper:Work$ cd repo viper:repo$ git checkout -b issue-123 # ... hack, hack, hack... viper:repo$ git commit -m "fix for issue #123" viper:repo$ git checkout master viper:repo$ git checkout -b svn-merge viper:repo$ git svn rebase # ... watch results for conflicts ... viper:repo$ git merge --squash issue-123 viper:repo$ git commit -m "ISSUE-123: fixed" viper:repo$ git svn dcommit -e # ... $EDITOR launches, allows you to write useful commit message for svn ... viper:repo$ git checkout master viper:repo$ git merge svn-merge
This may seem like a lot of extra branch switching, localized commits, etc., but the end result has been worth it (for me, at least). If you following this process, you can be relatively certain that your master branch will only ever mirror changes that have been made in Subversion.
Insuring that the master branch is always “clean” (i.e., has no conflicting commits) with regard to the shared svn tree makes it easy to switch temporarily to another feature branch if you have an urgent bugfix or simple change to make, while your bigger changes happily sit on another feature branch waiting to be pushed.
Updated Mar. 4, 2009: Changed merge to use --squash option, so that many local Git commits can be combined into a single upstream revision.
Thank you, thank you, thank you! I’m heading down this road myself in the very near future and seeing this written out like that is immensely helpful in my understanding of what it will take to make it work.
I can only FULL-ACK what Thomas said. Great help to see this scenario played out.
It looks like the example flow is not quite correct. The line that says:
“# … watch results for conflicts …”
is one line too early. Thanks for the useful info.
Why not use the fetch command? I want to take advantage of the –ignore-files option, which isn’t supported by my version’s git svn rebase.
The only other thing I lose is local svn logs.. but it’s subversion, I use svn log for that and I’m ok with it.
So what else am I missing that I’m missing? Thanks.