If you’re a fan of science (or science fiction), you may be familiar with the concept of the Multiverse, the idea that there are multiple universes. In some science fiction stories, one universe can split, branching into multiple universes and then later collapse into a single universe, this is a surprisingly good analogy for Git and branching.
Only Progress in One Universe
Imagine our universe (main
) branches off into a second universe (feature-a
). Time works differently in these two universes, so while in feature-a
I place flowers on the coffee table, in main
no time passes.
(main) (feature-a)
Add flowers to coffee table
Add kitchen.md Add kitchen.md
When feature-a
collapses back into main
(git merge
), everything goes smoothly since main
now moves forward to include the events of feature-a
. (In Git terminology, this is a “fast-forward merge”).
Technical Details of Fast-Forward Merge
The following is the output of git log --oneline --graph
before the merge. You can see feature-a
is one commit ahead of main
.
* 2971e6c (feature-a) Add flowers to coffee table
* ea72b65 (main) Add kitchen.md
After merging main
and feature-a
are identical.
* 2971e6c (main, feature-a) Add flowers to coffee table
* ea72b65 Add kitchen.md
Git repo (before the merge) available at salcode/git-merge-multiverse-scenario-1
Progress in Both Universes
Imagine a similar scenario, where the main
universe branches off into a second universe (feature-a
). Once again in feature-a
, I place flowers on the coffee table. However in this scenario, main
also moves forward and in that timeline I place a tablecloth on the kitchen table.
(main) (feature-a)
Add tablecloth Add flowers to coffee table
Add kitchen.md Add kitchen.md
This time, when feature-a
collapses back into main
things aren’t quite as simple. Time has moved forward in both universes. Fortunately, the changes in each universe are separate so after feature-a
merges into main
, now there are flowers on the coffee table and a tablecloth on the kitchen table.
This merge requires stitching these two separate timelines together. This stitching of timelines together creates a merge commit. A merge commit is a moment in time that has two different histories. In one history, I put flowers on the coffee table. In the other, I place a tablecloth on the kitchen table. In our merge commit, both of these timelines co-exist happily and collapse into a single timeline where both events occurred.
Technical Details of Merge Commit without Conflict
The following is the output of git log --oneline --graph
after the merge. You can see the merge commit (b0c00e3
) has two parent commits (2971e6c
and 032193f
). Notice the asterisks in the output noting where the commits occur.
* b0c00e3 (main) Merge branch 'feature-a'
|\
| * 2971e6c (feature-a) Add flowers to coffee table
* | 032193f Add tablecloth
|/
* ea72b65 Add kitchen.md
Git repo (before the merge) available at salcode/git-merge-multiverse-scenario-2
Progress in Both Universes that Overlap
In our final scenario, once again our main
universe branches off into another universe (feature-b
) and time moves forward in both universes. This time instead of placing flowers on the coffee table, I place them on the kitchen table in feature-b
. Once again I place a tablecloth on the kitchen table in main
. (Have you spotted the problem?)
When these timelines collapse together they have a conflict. In main
, I added tablecloth to the kitchen table (which had no flowers). In feature-b
, I add flowers to the kitchen table that had no tablecloth.
When these timelines merge together they are incompatible, in Git this is a merge conflict.
When we open the file with the conflict (kitchen.md
), we see the line from each timeline.
<<<<<<< HEAD
## Kitchen table with tablecloth on top
=======
## Kitchen table withh flowers on top
>>>>>>> feature-b
We can combine these lines and remove the marker lines
## Kitchen table with tablecloth on top with flowers on top
Technical Details of Merge Commit with Merge Conflict
$ git merge feature-b
Auto-merging kitchen.md
CONFLICT (content): Merge conflict in kitchen.md
Recorded preimage for 'kitchen.md'
Automatic merge failed; fix conflicts and then commit the result.
After resolving our conflict and merging, our Git history looks the same as our previous scenario where we merged without any conflicts, however if you look at the merge commit (90f5db3
in this case) you’ll see the changes you made to resolve the merge commit.
* 90f5db3 (main) Merge branch 'feature-b'
|\
| * 9d59323 (feature-b) Add flowers to kitchen table
* | 032193f Add tablecloth to kitchen table
|/
* ea72b65 Add kitchen.md
Git repo (before the merge) available at salcode/git-merge-multiverse-scenario-3
Why Can’t Git Do This?
I often hear the question, can’t Git figure out the merge conflict for us? After all, it does seem rather trivial.
While combining these lines is rather trivial for us, that is only because of our knowledge of tables, tablecloths, and flowers on tables. Git manages the timelines but doesn’t have any understanding of what is going on in them.
Combining these lines without understanding could result in an invalid state (like the flowers under the tablecloth).
Git identifies that both timelines modified the same section of our code and asks that we use our knowledge to resolve this merge conflict.
Leave a Reply