How to use GIT to merge two SVN branches
TL;DR: Download the git-merge-svn script here
I’ve been using git for years now but had to start using SVN for some projects. I found that GIT is good enough Subversion client too, especially as I retain the ability to commit often and rebase my work on top of commits from other devs (on the SVN side).
The only question arose – can I merge two SVN branches so that GIT log will show the merge?
The git-svn manual states that one should avoid all
git clone|merge|pull|push activity when using
git log does show merge history that was created in Subversion – how does it do that?
Subversion does not support actual merge of branches (more like cherry-picking), but since version 1.5 Subversion supports the svn:mergeinfo property that is used to track what has been merged into this folder previously.
Digging some more into the matter, I found out that GIT supports setting
svn:mergeinfo property on the SVN branch when
git svn dcommit --mergeinfo "/branches/somebranch:1-3"
svn:mergeinfo is overwritten with whatever is given on the command-line, so be careful to list previous merges too.
While more recent git version added the config parameter to automatically set this property:
config key: svn.pushmergeinfo
I had some troubles with the automatic mergeinfo – for one reason or the other GIT calculated it wrong and I couldn’t get it to work.
To automate the process, I wrote a shell script
git merge-svn which can be used to merge two SVN branches with correct
svn:mergeinfo set on the dcommit.
The script handles both situations:
- the branch is not merged in git – will do
- the branches have been already merged in git (but not in SVN) – will traverse until previous ancestor for the merged commit revisions.
UPDATE: Thanks theantway and haraldreingruber for patches – the script now:
- always does full merge (no fast-forward) so that SVN can fully understand and
- does not die on first merge (no previous mergeinfo)
Download the git-merge-svn script here
With this script I was able to produce these merges solely on git-side and retain the merge info so that GIT graph shows the log nicely:
- Make some commits on devel6
dcommitdevel6 to SVN (required to get SVN revision numbers for the commits)
- check out testtunk6 – yes, I know I made a typo in the name 😉
git merge-svn devel6
The last commant outputs:
% git merge-svn devel6 About to do an SVN merge: devel6 -> testtunk6 * NEW MERGE COMMIT | | * devel6 [7b71187] (r102) * | testtunk6 [0682a45] (r101) | * [273d6d6] (r100) STEP 1: GIT merge Executing: git merge --no-ff devel6 Continue? (y/n) [n]: y Merge made by the 'recursive' strategy. testfile | 1 + 1 file changed, 1 insertion(+) STEP 2: SVN dcommit executing: git svn dcommit --mergeinfo /idp/branches/devel:9-32,35-41 /idp/branches/devel6:89 /idp/branches/devel6:94 /idp/branches/devel6:93 /idp/branches/devel6:96 /idp/branches/devel6:97 /idp/branches/devel6:99 /idp/branches/devel6:100 /idp/branches/devel6:102 Continue? (y/n) [n]: y Committing to https://my.svn.host/svn/auth/idp/branches/testtunk6 ... M testfile Committed r103 M testfile Found merge parent (svn:mergeinfo prop): 7b71187fc371d3f86658c5850009e63be88157ac r103 = 87759323cbadd38bac78087d57b6870a926287e7 (refs/remotes/svn/testtunk6) No changes between 3fb2168cfbbe605fbd810d76513443203a85a549 and refs/remotes/svn/testtunk6 Resetting to the latest refs/remotes/svn/testtunk6