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.)
We’ll start with the state of the repository like this:
An excerpt from gitg –all gives us this nice diagram:
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:
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:
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.
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.
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.
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:
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 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.
+ 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 <firstname.lastname@example.org> Date: Fri Mar 25 13:26:10 2011 -0700 Merge branch 'test' Conflicts: README commit c40656482d0d2f0b9c9b37f74ea84a9c54929a56 Author: Tim Flagg <email@example.com> Date: Thu Mar 24 17:04:00 2011 -0700 README changed in the test branch commit 3330113bda8d3544ef6f05275970cd4dd705ef03 Author: Tim Flagg <firstname.lastname@example.org> Date: Thu Mar 24 16:48:19 2011 -0700 README changed in the master branch ...
gitg –all shows the merge:
End of conflict. Fade to commercial.