Merging Branches With a Conflict: Conflicts And Resolutions

Commands discussed in this section:

  • git merge
  • git diff
  • git status
  • git show
  • git ls-files

Merging Branches With a Conflict

What could be more fun than creating a conflict! Our conflict with consist of the same file in two branches, but with conflicting changes in the same location of the file.

We’ll explore what approaches there are to resolve the conflict, and then we’ll resolve the conflict. (Sounds almost as dramatic as a made-for-TV movie.)

For this example, let’s back up in time a bit, before the “git merge test” command we did in the Merging Branches: Without a Conflict page.

We’ll start with the state of the repository like this:

annotated

An excerpt from gitg –all gives us this nice diagram:

annotated

Then while on the master branch, let’s say we:

  • Inserted a line into the README file between the first and second lines:
        This line was inserted from the 'master' branch.
  • Then we committed the change to the master branch

The session is shown below:

$ cat README
This is the README file.
    This line was inserted from the 'master' branch.
One more line.
$ git add README
$ git commit -m 'README changed in the master branch'
[master 3330113] README changed in the master branch
 1 files changed, 1 insertions(+), 0 deletions(-)

The commit history now looks like this:

README change on master branch

The Plot Thickens: The Conflicted Line

Then we switched to the test branch and inserted a different and conflicting line in the test branch’s version of the README file (the scarey music starts in the background as the plot thickens…):

  • Inserted the following line into the README file between the first and second lines:
       The 'test' branch version is here.
  • And we commited the change to the test branch.

The session is shown below:

$ git checkout test
Switched to branch 'test'
$ [some-unnamed-editor] README
$ cat README
This is the README file.
   The 'test' branch version is here.
One more line.
$ git commit -a -m 'README changed in the test branch'
[test c406564] README changed in the test branch
 1 files changed, 1 insertions(+), 0 deletions(-)

Can’t you really feel the conflict now?! (We’d have some conflicts with you, the git reader, if we named the editor we used above. However, there are no tools for resolving which editor is best. We know which is best except for the fact we gitguys don’t agree…)

gitg –all shows us the current commit history:

README change on test branch

The two branches are clearly diverging.

Git Agrees There Is A Conflict

We’ll switch back to the master branch and then start to merge the test branch onto the master branch:

$ git checkout master
Switched to branch 'master'
$ git merge test
Auto-merging README
CONFLICT (content): Merge conflict in README
Automatic merge failed; fix conflicts and then commit the result.

The git merge test command resulted in the conflict we created:
CONFLICT (content): Merge conflict in README “.

Show Merge Status: git status

Let’s dig into ways to resolve the conflict and end all the drama. git status always helps and tells us where are:

$ git status
# On branch master
# Changes to be committed:
#
#   new file:   plan
#
# Unmerged paths:
#   (use "git add/rm ..." as appropriate to mark resolution)
#
#   both modified:      README
#

The Changes to be committed section shows us that the new file plan is ready to be committed and had no conflicts.

The Unmerged paths section shows us that both branches modified the README file, so we’ll need to do something to resolve the conflict.

Just Try To Ignore Git’s Warning

If we try to ignore git’s warning about the conflict and try to git commit, we get this:

$ git commit
U   README
fatal: 'commit' is not possible because you have unmerged files.
Please, fix them up in the work tree, and then use 'git add/rm ' as
appropriate to mark resolution and make a commit, or use 'git commit -a'.

Git reflects life: Trying to ignore conflicts doesn’t work in git either.

Let’s Investigate

Clearly, git cannot live with conflict and is forcing us to resolve it.

Let’s look at the contents of the README file:

$ cat README
This is the README file.
<<<<<<< HEAD
    This line was added from the 'master' branch.
=======
   The 'test' branch version is here.
>>>>>>> test
One more line.

Git inserted:

<<<<<<< HEAD

to mark the beginning of the conflicting section from the master branch (where the HEAD reference is pointing).

Git also inserted:

=======

to mark the ending of the conflicting section from the master branch, and inserted:

>>>>>>> test

to mark the end of the conflicting section from the test branch.

Telling Git The Conflict Is Resolved

You inform git that a conflict is resolved by adding the conflicting file to the index.

So at this point, we could resolve the whole thing by using an editor, changing the file, adding the file to the index and committing it:

$ [some-unnamed-editor] README
 Choose what we want to have in the conflicting section and
 save our updated version.
$ git commit -a -m'README conflict resolved'

But there are too many fun git commands to play with first.

git diff

git diff gives a quick way of showing the differences between the two conflicting versions:

$ git diff
diff --cc README
index 56df44d,9585db7..0000000
--- a/README
+++ b/README
@@@ -1,3 -1,3 +1,7 @@@
  This is the README file.
++<<<<<<< HEAD
 +    This line was added from the 'master' branch.
++=======
+    The 'test' branch version is here.
++>>>>>>> test
  One more line.

The output above looks a bit odd at first, but is displaying the differences in the conflicts. It is sometimes easier to see differences when Merging With a GUI.

The first 2 columns are used to report whether or not the line is in either of the two versions of the README file:

  • The first column is for the current branch (the master branch in this case).
  • The second column is for the test branch.

The column can contain any of these 3 chracters:

  • +” (a “plus sign”) means the line was missing from the previous version.
  • “-” (a “minus sign”) means the line was added to the previous version.
  • “ ” (a space) means the line was not changed.

Let’s start with the following output from git diff:

  This is the README file.
^^

The two leading spaces mean both the master and test versions of the file had this line.

Let’s look at the next line:

++<<<<<<< HEAD
^^

The “++” at the beginning of the line means both the master and test did not have this line (or the line would need to be added to the master and test version to make it the same as the current version).

It makes sense neither version would have included that line because the git merge command added the line to the current version.

Next:

 +    This line was added from the 'master' branch.
^^

The space in the first column means the master branch included the line and the + in the second column means the test branch did not include this line.

Display Another Branch’s Version Of A File

We can see the complete contents of the README file from the test branch. Simply prefix the branch name and colon (“test:“) to the file:

$ git show test:README
This is the README file.
   The 'test' branch version is here.
One more line.

Which Files Need Merging?

In addition to using git status, to see which files need merging, we can also use git ls-files -u:

$ git ls-files -u
100644 b0ed415d15862ac5582b51e4de65528e86934cd2 1   README
100644 56df44dd62daa9a16ab69f49e7f25df54054dae3 2   README
100644 9585db7d9c2d9ca05075f67a878f2554886d7b1a 3   README

Every line, above, lists the filename README, reporting it clearly needs to be merged. But why is it listed 3 times? Because git has 3 versions of the file, labeled 1, 2, and 3 immediately before the README filename. The versions are:

  • 1: The “common ancestor” of the file (the version of the file that both the current and other branch originated from).
  • 2: The version from the current branch (the master branch in this case).
  • 3: The version from the other branch (the test branch)

We can see the different versions of the files like this by prepending :stage#: where “stage#” is the number shown above.

$ git show :1:README
This is the README file.
One more line.
$ git show :2:README
This is the README file.
    This line was added from the 'master' branch.
One more line.
$ git show :3:README
This is the README file.
   The 'test' branch version is here.
One more line.
$ git show :4:README
fatal: Path '4:README' does not exist (neither on disk nor in the index).

Resolve the conflict

After we have resolved the conflict (by changing the README file the way we want for this git merge), we have to tell git that the conflict is resolved by adding the conflicted file to the index.

In this case, we need to add README to the git index. git status will then no longer complain about the README file having a conflict:

$ git add README
$ git status
# On branch master
# Changes to be committed:
#
#   modified:   README
#   new file:   plan
#

We’re ready to commit the merge:

$ git commit
[master 368a14a] Merge branch 'test'

The git log command shows the resolved conflict:

$ git log
commit 368a14a034eda95ee401bb56b3bb8df04b84ab0c
Merge: 3330113 c406564
Author: Tim Flagg <me@gitguys.com>
Date:   Fri Mar 25 13:26:10 2011 -0700

    Merge branch 'test'

    Conflicts:
        README

commit c40656482d0d2f0b9c9b37f74ea84a9c54929a56
Author: Tim Flagg <me@gitguys.com>
Date:   Thu Mar 24 17:04:00 2011 -0700

    README changed in the test branch

commit 3330113bda8d3544ef6f05275970cd4dd705ef03
Author: Tim Flagg <me@gitguys.com>
Date:   Thu Mar 24 16:48:19 2011 -0700

    README changed in the master branch
...

gitg –all shows the merge:

merged

End of conflict. Fade to commercial.

Next: Merging With A GUI
Previous: Merging Branches: Without A Conflict

Related: