Wednesday, September 16, 2009

Merging a branch with git-svn and still retaining the association with the previous svn path

git-svn uses commit messages to tie the commits in a particular local branch to its corresponding SVN path, which works great most of the time.

It causes a problem though when you want to merge branches in git and have the merged results be put back into svn.  I wanted to do this because the git merge functionality is much easier to use, IMHO, than subversion's merge command.

The problem is that when you do a git merge of two remote svn folders in a local git branch the commit message path changes from the previous path to the path of the source of the merge.

So, for instance, if you want your trunk in svn to match a branch (let's say branch 1.1.1), you could do the following in a git-svn checked out repo (Assuming you have already done git svn clone -s...)

git checkout trunk
git checkout -b localtrunk
You are now in the local git branch 'localtrunk' which is a copy of the trunk path of your svn repository.
Now, let's say you want to merge changes from branch 1.1.1 into trunk, you could do this...
git merge 1.1.1
If the merge is successful there is still a problem. The problem is that now if you do a git svn dcommit to push the merged changes into trunk they will not go into trunk. They will go into the 1.1.1 branch! Bummer. If you read the man page for git-merge you might say, "Hey, there is a --no-commit option to git-merge! That could fix the problem." In theory you would be right and I thought the same thing, but after many attempts (on Ubuntu 9.04) it would always commit after merging. Bummer! --no-log didn't work either. So, after a lot of searching, I found a way to do this, but it only works if you don't have a lot of commits to replicate.
Instead of running git merge as above do this. For each commit do...
git cherry-pick -e (SHA1 hash of the commit to merge from branch 1.1.1)
You can get those hashes from git log, after doing git checkout 1.1.1 in another terminal.

Each time you do it delete the line in the commit message that starts with git-svn-id: (and the line after it, but I don't think deleting that 2nd line is technically necessary)

Now do...
git svn dcommit (to send the merged changes to svn while keeping your local git branch pointing to trunk!)
git svn fetch
git svn rebase

Check that your localbranch branch and your 1.1.1 branch are the same with...
git diff 1.1.1

If there is no output they are identical.

Check that your localbranch branch is still pointing to trunk by doing...
git log

and verifying the git-svn-id: lines for the last commits (the ones at the top) still point to trunk@(svn commit number)

All done!