GIT merge-svn

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-svn.

But git log does show merge history that was created in Subversion – how does it do that?

svn:mergeinfo

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 dcommit‘ing:

git svn dcommit --mergeinfo "/branches/somebranch:1-3"

NB! the 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.

SOLUTION: git-merge-svn

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 git merge beforehand
  • 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

Example usage

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:

git-merge-svn result

  1. Make some commits on devel6
  2. dcommit devel6 to SVN (required to get SVN revision numbers for the commits)
  3. check out testtunk6 – yes, I know I made a typo in the name 😉
  4. 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

SVN vs Orwell

Not long ago I had to change history in GIT. Now I happened to commit to Subversion a revision, but forgot to limit the files to only relevant to the log entry and thus committed all changed files. Nothing really bad, but the entry was minor, compared to all the changes and I did not want those to go unnoticed in the log. I did not think one can take back files from an already committed revision, so I searched out for a solution to change the log entry. It turns out this is relatively simple, as the Subversion FAQ points out.Read More »