Let’s look at how to resolve two Git branches that have diverged by creating a Git merge commit (your other option in this situation is to perform a git rebase).
As discussed in Visualizing Git Branching with Blocks, we can’t do a fast-forward merge when the most recent commit on the receiving branch (e.g. commit
main) does not appear in the branch we are merging in (e.g. branch
(main*) (feat/d) C D B B A A
A Commit with Two Parents
As discussed in What is a Git Commit?, a commit includes a bunch of information including the commit hash that came directly before it (the
Typically, a commit has exactly one parent. In the case of a merge commit, there are multiple parents.
git merge feat/d to merge
main, Git will create commit (
MERGE) which has both C and D as parents).
(main*) (feat/d) MERGE C D D B B A A
This allows us to merge these branches that have diverged without rewriting either of their history.
Git Log Display Merge Commits
To get a clearer picture of the situation we add
--graph to our
git log --oneline command
git log --oneline --graph * 154382e (HEAD -> main) Merge branch 'feat/d' into main |\ | * 163fe29 (feat/d) D * | 5d408f9 (feat/c) C |/ * 2b3a38b B * ec6a2c7 A
Here we can see our merge commit (
154382e) has two parents
Tracing back through this graph we can see the branches diverged after commit
Spacers in the Git Log Graph
Even though commits
D are adjacent to each other in Git, they are not displayed that way with
git log --oneline --graph. Spacers (
|) are included in the output while the commit is noted with an asterisk (
*). While visualizing this with blocks, I like to think of transparent blocks for the spacers.
View the Parent Commits
We can see the under the hood details of our merge commit (
git cat-file -p 154382e tree 62feee95ffe3c1d6a738d35dac3da1d27a68abe9 parent 5d408f929e4c596d007532725abfd592b293dfb3 parent 163fe29d91b2ff43d4c724934836617ca619fa25 author Sal Ferrarello <email@example.com> 1613264290 -0500 committer Sal Ferrarello <firstname.lastname@example.org> 1613264290 -0500 Merge branch 'feat/d' into main
In the above output, we can see there are two
parent commits. The first is
5d408f9 and the second
163fe29. These values match those that we see when we run
git log --oneline --graph.
Since our branches have diverged, it is possible we will have a merge conflict when we create our merge commit. To resolve our merge conflict we find the locations in our code with merge conflict markers (
>>>>>>>) and correct this code.
After the code is corrected, we stage our changes with
git add and then complete the merge with
git merge --continue
History is not Rewritten
The biggest advantage of using a merge commit (over a rebase) is it does not rewrite your history. You can now push your changes to a server without overwriting history.