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:
svn/trunk
, git: master
) - the stable branchsvn/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).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?
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.
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.
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:
git merge
beforehandWith 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:
Example usage:
dcommit
devel6 to SVN (required to get SVN revision numbers for the commits)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
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 dcommit
ing, git creates a "twin" commit "hardly" linked to a svn commit, so it rewrites original commit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With