Merging two very divergent branches using git?
This is why the git merge
functionality exists.
$> git checkout master
$> git merge verydifferentbranch
Odds are the first time you do this there will be a lot of conflicts (if they diverged as much as you claim). But if you keep on top of things, after the initial merge it won't be too bad.
It sounds like your history looks like this:
...---o---A---o---...---o---o---B master
\
o---o---...---o---C verydifferentbranch
You say that you are worried about losing history if you force push verydifferentbranch
to master
. Such an operation would effectively be throwing away everything after A
and up to B
.
You can preserve the history by merging it, or just by dropping a tag at the abandoned branch tip and leaving it unmerged.
Use a Merge
A merge would allow you to keep both sides of the history:
...---o---A---o---...---o---o---B
\ \
o---o---...---o---C---M master
The kind of merge you do will determine the content created for commit M. A normal merge (using the recursive
merge strategy) sounds like it would produce a very large number of conflicts in your case. If you actually need to incorporate the changes from the A..B
commits, there is nothing else to do but work through the conflicts presented by either a merge or a rebase. (In the future you will probably have fewer problems if you can merge or rebase more often to deal with the conflicts as they arise.) But, if you just want M
to have the same content as C
(i.e. you want to ignore the changes represented by the A..B
commits), then you can use the ours
merge strategy.
git-merge(1) describes the ours
merge strategy:
This resolves any number of heads, but the resulting tree of the merge is always that of the current branch head, effectively ignoring all changes from all other branches. It is meant to be used to supersede old development history of side branches. Note that this is different from the -Xours option to the recursive merge strategy.
You can produce M with a message of Merge commit 'abandoned/foo-features'
like this:
git tag -m 'describe reason for abandonment here...' \
abandoned/foo-features master # tag abandoned branch tip
git checkout verydifferentbranch # checkout dominant branch
git branch -M verydifferentbranch master # replace master
git merge -s ours abandoned/foo-features # merge only other's history
git tag -d abandoned/foo-features # optional: delete the tag
git push wherever master tag abandoned/foo-features # publish them
Variations on these commands will give you slightly different automatic commit messages for M
(you can always supply your own with git merge -m
or amend it with git commit --amend
afterwards).
The main point is that the resulting master
will have both pieces of history, but none of the changes from the original master
side (those changes are still in the history, they are just not represented in the tree referenced by commit M
).
Leave It Hanging
If it is acceptable to “rewrite” master
(i.e. there is no other work based on any of the commits A..B
, or the users involved do not mind the work it will take to recover from the rewrite), then you could just leave a tag at the current tip of master
and replace master
with verydifferentbranch
.
...---o---A---o---...---o---o---B (tag: abandoned/foo-features)
\
o---o---...---o---C master
Arrange for this like so:
git tag -m 'describe reason for abandonment here...' \
abandoned/foo-features master # tag abandoned branch tip
git branch -M verydifferentbranch master # replace master
git push wherever +master tag abandoned/foo-features # publish them