I'm having trouble merging a subtree of a development branch back into the integration branch.
I have two branches, one was used for development and one is used for integration. Extensive development has been done in the development branch and I want to merge a portion of it back in. The specific portion that I want to merge is all contained in one subtree of the development branch.
My directory structure is like this:
[Branch A] [Branch B]
| |
+--Dir1 +--Dir1
+--Dir2 +--Dir2
| | |
+--DirA | +--DirA
| | |
+--File1 | +--File1
+--File2 | +--File2
| +--File3
| +--File4
+--Dir3
I want to merge Branch B/Dir2/DirA into Branch A/Dir2/DirA. I want File1 and File2 to be merged and File3 and File4 should be created in Branch A. I don't want to pick up Dir3, or any changes in Dir1.
I've tried the steps outlined by kernel.org for merging subtrees, but they fail when I do the git read-tree with:
error: Entry 'Dir1/DirA/File1' overlaps with 'Dir1/DirA/File1'. Cannot bind.
I've tried using the subtree script which is hosted on github, but I am not having much luck with it. When I do:
git checkout Branch_A
git subtree merge -P Dir2/DirA Branch_B
I see evidence that Dir3 has been merged and the merge fails with conflicts.
I could cherry-pick the files to merge, but that seems unnecessarily convoluted for what should be a common and straight-forward problem.
Merging subtrees really refers to merging part of an external project into your project. I'd refer to this blog post to accomplish what you want to do: http://jasonrudolph.com/blog/2009/02/25/git-tip-how-to-merge-specific-files-from-another-branch/
The problem you have is that git isn't designed to do such a thing. Consider the following history that might result in your two branches
A
andB
from the question:with branch
A
pointing at commitI
and branchB
pointing at commitH
. Now you want to take over some changes inB
intoA
.For example commits
D
andF
modifiedDir2
,E
,G
andH
did something toDir1
andDir3
. So actually you only want to have commitsD
andF
added to your integration branchA
.That means that branch
B
wasn't created carefully enough and you actually want to redo it into (at least) two clean topic branches. Something like:If there are commits that are touching both,
Dir1
andDir2
then these commits are "bad", as they don't address a single topic, but does several things at once. In this case you might want not only reshuffle the commits, but also change the commits to be good ones usinggit-rebase -i
.Of course you could just merge branch
A
and fix-up the resulting tree. But that would make it hardly possible to continue working on the "non-merged" changes inA
, because when you merge a descendant ofA
you would have to fix-up the resulting tree once more, because the changes ofE
,G
andH
would not be included in the tree that git prepares for you, as these commits are already merged.What you want may be simply:
Details
This recipe copies a subtree from Branch_B to Branch_A, and (OPTIONALLY) also creates a merge-commit in git's project history:
Switch to Branch_A in case you aren't there already.
git checkout Branch_A
(OPTIONAL) Establish a merge commit, without changing any files in your workspace (merge strategy "ours"). This "empty" commit just adds the other branch as a second parent, creating a history link. If that's unimportant to you, you may skip this step; it's not required for the subsequent step.
git merge Branch_B -s ours
NOTE: only do this in a clean workspace; this command may fail if you have any files modified or staged.
Copy the contents of subtree Dir2/DirA from Branch_B into your workspace, without changing your active branch.
git checkout Branch_B Dir2/DirA
Commit the files from Branch_B into Branch_A. If you omit
--amend
then a new commit is created for this step; using it will make the new files part of the earlier merge commit instead.git commit --amend
The form of checkout in step 3 may seem unfamiliar, but the official documentation states that the customary branch-switching behavior is actually just a convenience when this form is not used:
So if you do supply an explicit path, it will not switch branches, just copy the files from that path.
I would simply make dirA a submodule and introduce a 3rd repo for it.