Conflicts resulting from merges

The merge command does the following (from chapter 7):

The resulting history looks like this:

  (N)   - new revision - the position of the current branch when the merge is done
  / \
 |   |
(L)  |  - left side (ORIG_HEAD) - the position of the current branch when the merge started
 |   |
 |  (R) - right side (MERGE_HEAD) - the branch being merged
 |   |
  \ /
  (O)   - merge base (FETCH_HEAD) - the most recent commit that ORIG_HEAD and MERGE_HEAD share

During a merge conflict, the names ORIG_HEAD, MERGE_HEAD, and FETCH_HEAD are valid and point to the labelled parts of the above diagram. The new revision doesn't exist during the conflict, as that is what the merge will create.

To show how this is useful, I've created a conflict in the helloworld repository.

In my master branch, I have a python.py that looks like this:

#!/usr/bin/env python
print "Hello World I am the master branch"

In another branch, python.py looks like this:

#!/usr/bin/env python
print "Hello World I am another branch"

Now, here's what happens when I try to merge another into master:

$ git checkout master
Switched to branch "master"
$ git merge another
Auto-merged python.py
CONFLICT (content): Merge conflict in python.py
Automatic merge failed; fix conflicts and then commit the result.

So I edit python.py and see this:

#!/usr/bin/env python
<<<<<<< HEAD:python.py
print "Hello World I am the master branch"
=======
print "Hello World I am another branch"
>>>>>>> another:python.py

Git is trying to show two versions of the file at once.

The area between

<<<<<<<
and
=======
is from the "left side", the current branch. Right now, this happens to be the master branch.

The area between

=======
and
>>>>>>>
is from the "right side", the branch being merged. This happens to be another branch.

The parts of the file that are not enclosed in these tags were merged automatically. They do not necessarily match what is on either side.

If I ask git to show me the changes, it will give me a three-way diff:

$ git diff
diff --cc python.py
index 4419867,43946f0..0000000
--- a/python.py
+++ b/python.py
@@@ -1,2 -1,2 +1,6 @@@
  #!/usr/bin/env python
++<<<<<<< HEAD:python.py
 +print "Hello World I am the master branch"
++=======
+ print "Hello World I am another branch"
++>>>>>>> another:python.py

Again, Git is trying to show two things at once: a diff from the left side and a diff from the right side.

To see how the current revision differs from the left side, read the first column and ignore the second. The lines marked with + have been added, lines marked with - have been removed, and lines marked with spaces are unchanged.

To see how the current revision differs from the right side, read the second column and ignore the first.

Because the new revision will be on the master branch, I've edited python.py to look like this:

#!/usr/bin/env python
print "Hello World I am the master branch"

I've added a blank line at the end because Git won't give me a diff otherwise. This seems to be an anomaly caused by my working with such tiny files, but it indicates that sometimes git diff will fail during a merge conflict.

Before committing, I check the diff again:

$ git diff
diff --cc python.py
index 4419867,43946f0..0000000
--- a/python.py
+++ b/python.py
@@@ -1,2 -1,2 +1,3 @@@
  #!/usr/bin/env python
 -print "Hello World I am another branch"
 +print "Hello World I am the master branch"
++

This shows that "unchanged" can have a special meaning in three-way diffs. If you simply read the first column and ignored the second column, you might think that master originally looked like this:

#!/usr/bin/env python
print "Hello World I am another branch"
print "Hello World I am the master branch"
But we know that the line "Hello World I am another branch" was not in master. In this case, "unchanged" means that it was NOT in master and also is NOT in the current revision. Git has to show this line because it was removed relative to the right side.

You can tell from looking at the diff that it is not in the new revision because there is a minus. It was removed from the right side. Since the line is missing and is "unchanged" from the master revision, that means it's also not in master. So if you see a space and a minus in a three-way diff, you know the side with the space doesn't have that line.

Now, the final step is to add the file and commit:

$ git add python.py
$ git commit

I do not recommend using git commit -a when resolving conflicts. If you do, and you forgot to update a file, Git won't be able to remind you.

Vincent Povirk