Suppose I've got the following history, where the top line is the master branch, the lower one is a feature branch that's merged with master at one point, and D
just reverts C
(which means that the working directory is the same in B
and D
).
A---B---C---D master
\ \
E---F---G feature
I want to add C
and its reversion D
to the history before the merge in F
, like this:
A---B---C---D master
\ \
E-----------F'--G' feature
I don't want to change E
(which is actually a long series of commits).
git rebase --onto D B
(as suggested here) results in merge conflicts (with or without --preserve-merges
).
Is there a way to accomplish what I want?
Most methods will be somewhat painful. There's a moderately painless version using
git filter-branch
, except that filter-branch itself is painful. :-) (You'd filter commitsF
andG
and write a commit-filter that substitutes in the new parents you want forF'
, and let the filter-branch operation replace the parentage forG
.)I think the simplest method that does not resort to low level commands is just to make a new merge, then rebase
G
onto the new merge. The new merge may have conflicts but we don't care, we just want to take the old merge's tree, which we can do like this:The first
checkout
gets you on a detached HEAD with one SHA-1, themerge --no-commit
starts the merge process with the other SHA-1, thegit rm -rf .
throws away the merged tree and any conflicts, and thegit checkout <id> -- .
fills in the index and work-tree from the previous merge. The finalgit commit
creates mergeF'
with the same tree as mergeF
, but with different parents.At this point (still with a detached HEAD) you can rebase (or cherry-pick) commit
G
(or many commitsG
), then force your branch to point to the tip of the new graph. I'd suggest usinggit rebase ... --onto HEAD
but I have not tested this with a detached HEAD and there's at least one way it might go wrong (resolvingHEAD
to an ID too late).The low level
git commit-tree
command may actually be even simpler. Andrew C wrote the correct command in a comment, although you have to spell out the branch name withgit update-ref
. [Edit: maybe not quite correct, the two parents you want areD
andE
, notD
andB
. Again, put the one you want as first-parent first.]The advantage (?) of using the more familiar commands is that they're, well, more familiar.