Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git-svn merge two SVN branches

Tags:

git

git-svn

I have read numerous SO questions and blogs on git-svn and merging. Some of them (notably git-svn man page) warns against using merge anywhere near the branch I plan to dcommit from.

Others, like this SO answer, state that merging is fine as long as I use one-off feature branches that I delete before dcommitting.

I have a setup with two long-living SVN (and git) branches:

  • trunk (git-svn: svn/trunk, git: master) - the stable branch
  • branches/devel (git-svn: svn/devel, git: devel) - the main development branch from which I fork off feature branches (and I can live with rebasing them back into devel instead of merging).

Snapshot of current state in Source Tree

In the picture above, (1) shows past SVN history, where branches/devel has been merged into trunk.

(2) shows that I have successfully dcommitted my current development back into SVN.

The question: can I use git-svn to merge two SVN branches so that history shows the merge point (as SVN itself can do)? In other words: what happens if I were to dcommit from master as shown in (3)?

Will this mess up my SVN (or git) history? Or will this just plain forget that devel was merged into master at all?

EDIT: If git just forgets that devel was merged into master, is there a difference between dcommiting from point (3) and applying all the commits as a single patch?

like image 407
Laas Avatar asked Apr 18 '13 08:04

Laas


People also ask

How do I merge 2 Branches in Git?

Merging Branches in a Local Repository To merge branches locally, use git checkout to switch to the branch you want to merge into. This branch is typically the main branch. Next, use git merge and specify the name of the other branch to bring into this branch.

Can I merge two feature Branches?

Merge branchesMerging your branch into master is the most common way to do this. Git creates a new commit (M) that is referred to as a merge commit that results from combining the changes from your feature branch and master from the point where the two branches diverged.


2 Answers

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.

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

Example usage:

  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 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
like image 199
Laas Avatar answered Sep 23 '22 22:09

Laas


Git and Svn have different data structure to keep a history.

Svn uses a simple tree. I.e. each commit has only one parent, but one commit could have several childs (branches). So, Svn doesn't support merges. The thing they call as "merge" is actually changeset porting, the closest analogy in git is rebase or maybe cherry-pick. From version 1.5 Svn supports a metainfo property svn:mergeinfo which helps somehow to track what was "merged", however it looks mostly as a workaround, that explains why branches in Svn are so hard to use. Merge point is a commit which squashes all merged changesets and conflict resolution into one changeset, probably annotated with the svn:mergeinfo property.

Git uses Directed acyclic graph which is very natural way to describe history merging and branching. A merge point is just another commit with two (or more!) parents. So, your (3) on the picture is a merge commit. So, all history looks natural, it has all commits reachable on a history graph from the current point.

Git-Svn bridge tries do the best to handle Svn design drawback, before doing dcommit it usually rebases all merged commits on the top of the current Svn branch. Asof ~2 years ago, git-svn was not able to supply svn:mergeinfo, so the thing you are asking is impossible. Moreover, when you are dcommiting, git creates a "twin" commit "hardly" linked to a svn commit, so it rewrites original commit.

like image 44
kan Avatar answered Sep 22 '22 22:09

kan