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

Tags:

Git