Git Conflict Resolution
By Ted Felix
Updated May 2, 2020
The conflict resolution process in git is slightly different for each git command. This tutorial will walk you through the major variations.
The man page for git-merge(1) gives the most information about the conflict resolution process. See the two sections: "HOW CONFLICTS ARE PRESENTED" and "HOW TO RESOLVE CONFLICTS". However, rebase, am, and apply also have a conflict resolution process that is different, but not as well documented. Note, however, that the documentation for git improves continuously, so it is a good idea to read through it to see what's there.
Which commands might give us conflicts? The "mergy" ones, of course:
Git Version
Git is constantly improving. For reference, the output of the commands in this document was generated using the following git version:
$ git --version
git version 2.17.1
Setup: merge.conflictstyle diff3
Before diving into conflict resolution, we need to take a look at the merge.conflictstyle setting. It is documented in git-config(1) along with several other "merge.*" settings that might be interesting to investigate. merge.conflictstyle has two possible values. "merge" is the default and this gives two-way conflict information in a conflicting file. "diff3" is the other possible value, and this gives three-way conflict information which can be much more helpful. Here's an example. First, let's go with the default value of "merge":
$ git config --global merge.conflictstyle merge
This gives us two-way conflict resolution markers that only show the current state of each branch:
<<<<<<< HEAD
Hello, master change.
=======
Hello, branch b1 change.
>>>>>>> b1
Switching to diff3:
$ git config --global merge.conflictstyle diff3
And we get three-way conflict resolution markers that include the original state along with the current state of each branch:
<<<<<<< HEAD
Hello, master change.
||||||| merged common ancestors
Hello, Original.
=======
Hello, branch b1 change.
>>>>>>> b1
The original file before changes had "Hello, Original.". The master (HEAD) branch has "Hello, master change.", and the "b1" branch has "Hello, branch b1 change.". This three-way diff can be very helpful in determining what really changed, so I recommend working with merge.conflictstyle set to diff3.
Even more helpful is a GUI merge tool like kdiff3. See "git mergetool" below for details.
Some Conflicts
Here's the script I use to generate a set of conflicts to play with.
#!/bin/sh
# This script will set up a git repo with interesting conflicts in
# two branches.
# hello.txt and goodbye.txt have simple conflicts for illustrating the
# features of git merge, rebase, and am.
# mergetool.txt has a set of conflicts designed to test the capability
# of external merge tools.
# Run only in an empty directory.
# Need to check for hello.txt, goodbye.txt, mergetool.txt, and a .git dir
# and abort if found.
# TODO
# - Might be nice to do more than just one commit in each branch. This
# will illustrate how the git commands behave when dealing with
# sequential commits.
# Be careful not to destroy an existing repo
if [ -d .git ]; then
echo ".git directory found. aborting..."
exit 1
fi
git init
# Create the files
cat >mergetool.txt <<EOF
LINE 1
LINE 2
LINE 3
LINE 4: This will not be changed by anyone
EOF
echo "Hello, Original." >hello.txt
echo "Goodbye, Original." >goodbye.txt
# Add them to the index (stage them for commit)
git add mergetool.txt
git add hello.txt
git add goodbye.txt
# Commit them
git commit -m "Initial Commit"
# In a new branch "b1"
git checkout -b b1
# Make some changes...
sed -i "s/LINE 2/LINE 2: Branch b1 changed this./" mergetool.txt
sed -i "s/LINE 3/LINE 3: Branch b1 changed this./" mergetool.txt
# An additional commit to make things a little more interesting
git commit -a -m "Change branch b1, mergetool"
sed -i "s/Original/branch b1 change/" hello.txt
sed -i "s/Original/branch b1 change/" goodbye.txt
# Commit the changes
git commit -a -m "Change branch b1, hi/bye"
# Back in master...
git checkout master
# Make some conflicting changes...
sed -i 's/LINE 1/LINE 1: Branch master changed this./' mergetool.txt
sed -i 's/LINE 3/LINE 3: Branch master changed this./' mergetool.txt
sed -i "s/Original/master change/" hello.txt
sed -i "s/Original/master change/" goodbye.txt
# Commit the changes
git commit -a -m "Change master"
git merge
"git merge" is the "mergiest" of all the mergy git commands. Even if you mainly use rebase or "am", it's a good idea to spend some time getting familiar with how to resolve conflicts with git merge. Much of what you learn here will carry over to the other mergy commands. Just keep in mind that there are subtle differences that can be quite annoying.
Merge
Given the setup described in "Some Conflicts" above, let's try to merge b1 into master. It's important to note that merging results in a new "merge commit" that has two parents. So a conflict in this process halts the creation of that commit. Our goal is to resolve the conflicts, stage (add) the files, and complete the commit.
$ git merge b1
Auto-merging mergetool.txt
CONFLICT (content): Merge conflict in mergetool.txt
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Auto-merging goodbye.txt
CONFLICT (content): Merge conflict in goodbye.txt
Automatic merge failed; fix conflicts and then commit the result.
Merge lets us know we've got some conflicts. "git status" can be used at any time to see what "unmerged paths" need conflict resolution:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: goodbye.txt
both modified: hello.txt
both modified: mergetool.txt
no changes added to commit (use "git add" and/or "git commit -a")
Note that git also gives us some basic instructions for dealing with the situation: fix conflicts and run "git commit".
Use "git diff" to examine the conflicts in a file:
$ git diff hello.txt
diff --cc hello.txt
index 5eb9649,379bd44..0000000
--- a/hello.txt
+++ b/hello.txt
@@@ -1,1 -1,1 +1,7 @@@
++<<<<<<< HEAD
+Hello, master change.
++||||||| merged common ancestors
++Hello, Original.
++=======
+ Hello, branch b1 change.
++>>>>>>> b1
Resolve The Conflicts
At this point, we can edit the conflicting files and resolve the conflicts manually. If there are a large number of conflicts "git mergetool" and a GUI merge tool like kdiff3 make things easier. See the git mergetool section for details.
Git offers a few simple conflict resolution functions. We can take all of the changes from the branch we are merging into master by using "checkout --theirs":
$ git checkout --theirs hello.txt
$ cat hello.txt
Hello, branch b1 change.
We can also go with all the changes from master by using "checkout --ours":
$ git checkout --ours hello.txt
$ cat hello.txt
Hello, master change.
If conflict resolution gets out of hand in a file and we want to start over, "checkout -m" will undo our changes:
$ git checkout -m hello.txt
$ cat hello.txt
<<<<<<< ours
Hello, master change.
||||||| base
Hello, Original.
=======
Hello, branch b1 change.
>>>>>>> theirs
There is no redo, so if you've spent considerable time working on the conflicts in a file, think carefully before doing this.
Let's go with theirs for hello.txt.
$ git checkout --theirs hello.txt
$ git diff hello.txt
diff --cc hello.txt
index 5eb9649,379bd44..0000000
--- a/hello.txt
+++ b/hello.txt
git diff lets us know there are no more conflicts to resolve. So, let's add hello.txt to the index and check status to see what files are left.
$ git add hello.txt
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Changes to be committed:
modified: hello.txt
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: goodbye.txt
both modified: mergetool.txt
Let's go ahead and resolve the rest of the conflicts by going with either ours or theirs.
$ git checkout --ours goodbye.txt
$ git add goodbye.txt
$ git checkout --theirs mergetool.txt
$ git add mergetool.txt
$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: hello.txt
modified: mergetool.txt
Complete The Commit
git status shows us that there are no more "unmerged paths" (unresolved conflicts), so we can finish the merge by committing.
$ git commit -m "Resolve Merge Conflicts"
[master f0190c7] Resolve Merge Conflicts
Aborting a Merge
Use --abort:
$ git merge --abort
This takes you back to where you were before you tried the merge. Be very careful with this as it loses any work you may have done in your working copy and anything you might have staged in the index. If you are part-way through a merge, this might be a little extreme unless you really just want to lose it all.
Summary
When dealing with a conflict in git merge:
- Use "git status" and "git diff" to find out what went wrong.
- Resolve the conflicts by any of the following methods:
- Edit each conflicting file with your favorite editor.
- "git mergetool" and an appropriate merge GUI tool like kdiff3.
- "git checkout --theirs" or "git checkout --ours"
- "git checkout -m" to undo conflict resolution on specific files. (BE CAREFUL!)
- "git add" the resolved files.
- If things get hosed, use "git merge --abort" to start over before the merge. (BE CAREFUL!)
- "git commit" to finish and get out of merge mode.
"git mergetool" combined with kdiff3 provides a visual way to deal with difficult merge conflicts. As long as you have kdiff3 installed, git mergetool should find it. If not, you can specify the tool you want as an option:
$ git mergetool --tool=kdiff3
Or you can make the setting persistent:
$ git config --global merge.tool kdiff3
Note: kdiff3 isn't the only choice you've got. But it is one of the best. Try it first, then try the others and you'll see what I mean. I tested meld for this as that is what I usually use for diffs. Meld was incapable of dealing with the conflicts in my test scenario. It also did not do any merging for me at all. kdiff3 turned out to be far superior to meld.
The file mergetool.txt that is produced by the "Some Conflicts" script above is specifically designed to test the capabilities of "git mergetool". After running that script, try a merge and then try git mergetool.
$ git merge b1
... conflict messages ...
$ git mergetool
Merging:
mergetool.txt
Normal merge conflict for 'mergetool.txt':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (kdiff3):
Note that it will prompt us to resolve conflicts for each file. Hit enter and kdiff3 will be launched.
Click the screenshot above for a full-size version. Across the top are three versions of the file. The original (A), the master branch (B), and the b1 branch (C). At the bottom is the merged version with conflicts marked. You can right-click on conflicts in the bottom pane and select which version you want. With a large number of conflicts, this can be a lot faster than removing conflict resolution markers with an editor.
"git mergetool" is probably best for complicated conflicts. If all you want to do is take one or the other version wholesale, you can do this in kdiff3, but "git checkout --ours" (or --theirs) is probably faster. Also note that kdiff3 probably can't handle binary files.
To complete the merge, make any modifications you want, hit Save in kdiff3 and exit kdiff3. This will make the changes and stage (add) them for commit.
"git mergetool" tends to leave ".orig" files around. Use "git status" to find them. You can safely delete them. ("git clean" can be used for this, but be careful with it.)
Windows? To help Git find kdiff3 in Windows, you need to add "c:\Program Files\KDiff3" to your PATH variable. I've had mixed results with kdiff3 and Windows. For simple test projects, it seemed to work fine. For more complex projects with long directory names and substituted drive letters, it didn't work. Be prepared for problems.
git rebase
Rebase is similar to merge, but instead of commit, we use rebase --continue (and sometimes --skip) to finish the rebase.
Rebase
Given the setup described in "Some Conflicts" above, let's try to rebase the b1 branch on master.
$ git checkout b1
Switched to branch 'b1'
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Change branch b1, mergetool
Using index info to reconstruct a base tree...
M mergetool.txt
Falling back to patching base and 3-way merge...
Auto-merging mergetool.txt
CONFLICT (content): Merge conflict in mergetool.txt
error: Failed to merge in the changes.
Patch failed at 0001 Change branch b1, mergetool
Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Rebase lets us know we've got some conflicts. "git status" can be used at any time to see what "unmerged paths" need conflict resolution:
$ git status
rebase in progress; onto 8b47a50
You are currently rebasing branch 'b1' on '8b47a50'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: mergetool.txt
no changes added to commit (use "git add" and/or "git commit -a")
Note that git gives us some basic instructions for dealing with the situation: fix conflicts and then run "git rebase --continue". (These instructions are missing the stage (add) step in git 1.8.1.)
Use "git diff" to examine the conflicts in a file:
$ git diff mergetool.txt
diff --cc mergetool.txt
index 2b1c43c,3f83dbd..0000000
--- a/mergetool.txt
+++ b/mergetool.txt
@@@ -1,4 -1,4 +1,14 @@@
++<<<<<<< HEAD
+LINE 1: Branch master changed this.
+LINE 2
+LINE 3: Branch master changed this.
++||||||| merged common ancestors
++LINE 1
++LINE 2
++LINE 3
++=======
+ LINE 1
+ LINE 2: Branch b1 changed this.
+ LINE 3: Branch b1 changed this.
++>>>>>>> Change branch b1, mergetool
LINE 4: This will not be changed by anyone
Resolve The Conflicts
As with merge, we can edit the conflicting files and resolve the conflicts manually. If there are a large number of conflicts "git mergetool" and a GUI merge tool like kdiff3 make things easier.
While rebasing, Git offers a few simple conflict resolution functions. We can take all of the changes from the branch we are rebasing (b1) by using "checkout --theirs". Note that this is backwards from what you might expect, "theirs" is the one we are currently on:
$ git checkout --theirs mergetool.txt
$ cat mergetool.txt
LINE 1
LINE 2: Branch b1 changed this.
LINE 3: Branch b1 changed this.
LINE 4: This will not be changed by anyone
We can also go with all the changes from the destination (upstream) of the rebase (master) by using "checkout --ours":
$ git checkout --ours mergetool.txt
$ cat mergetool.txt
LINE 1: Branch master changed this.
LINE 2
LINE 3: Branch master changed this.
LINE 4: This will not be changed by anyone
The meaning of theirs and ours is backwards when rebasing. If we have b1 checked out and we rebase onto master, "theirs" is b1, and "ours" is master. (The man page for git-checkout mentions that "ours" refers to "stage #2" and "theirs" refers to "stage #3". I'm not sure what that means, but it might explain why these seem backwards. Try "git ls-files -u" to see the stage #'s.)
If conflict resolution gets out of hand in a file and we want to start over, "checkout -m" will undo our changes:
$ git checkout -m mergetool.txt
$ cat mergetool.txt
<<<<<<< ours
LINE 1: Branch master changed this.
LINE 2
LINE 3: Branch master changed this.
||||||| base
LINE 1
LINE 2
LINE 3
=======
LINE 1
LINE 2: Branch b1 changed this.
LINE 3: Branch b1 changed this.
>>>>>>> theirs
LINE 4: This will not be changed by anyone
There is no redo, so if you've spent considerable time working on the conflicts in a file, think carefully before doing this.
Let's go with "theirs" (from b1) for mergetool.txt.
$ git checkout --theirs mergetool.txt
$ git diff mergetool.txt
diff --cc mergetool.txt
index 2b1c43c,3f83dbd..0000000
--- a/mergetool.txt
+++ b/mergetool.txt
git diff lets us know there are no more conflicts to resolve. So, we can stage (add) the file and continue.
$ git add mergetool.txt
$ git rebase --continue
Applying: Change branch b1, mergetool
Applying: Change branch b1, hi/bye
Using index info to reconstruct a base tree...
M goodbye.txt
M hello.txt
Falling back to patching base and 3-way merge...
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Auto-merging goodbye.txt
CONFLICT (content): Merge conflict in goodbye.txt
error: Failed to merge in the changes.
Patch failed at 0002 Change branch b1, hi/bye
Use 'git am --show-current-patch' to see the failed patch
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
There were two commits on the b1 branch. We fixed the first one and continued, and now git is trying to apply the second commit to master and there are more conflicts to resolve. In this case, let's go with "ours" (master's) which will throw away the changes in b1.
$ git checkout --ours hello.txt
$ git add hello.txt
$ git checkout --ours goodbye.txt
$ git add goodbye.txt
$ git rebase --continue
Applying: Change branch b1, hi/bye
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
This time, continue doesn't work. The reason is because we have thrown away all the changes in this particular commit from the branch (b1) that is being rebased. So we need to let git know that we really do want to throw away the entire commit. Just use "--skip":
$ git rebase --skip
And the rebase is complete.
Aborting a Rebase
Use "rebase --abort":
$ git rebase --abort
This takes you back to where you were before you tried the rebase. Be very careful with this as it loses any work you may have done in your working copy and anything you might have staged in the index. If you are part-way through a rebase, this might be a little extreme unless you really just want to lose it all.
Summary
When dealing with a conflict in git rebase:
- Use "git status" and "git diff" to find out what went wrong.
- Resolve the conflicts by any of the following methods:
- Edit each conflicting file with your favorite editor.
- "git checkout --theirs" or "git checkout --ours". Be careful of the backwards meaning of --theirs and --ours when rebasing. Double-check the file to make sure you got what you wanted.
- "git checkout -m" to undo conflict resolution on specific files. (BE CAREFUL!)
- "git mergetool" and an appropriate merge GUI tool like kdiff3.
- "git add" the resolved files.
- "git rebase --continue" to continue the rebase.
- "git rebase --skip" in rare situations when you decide to throw away all the changes for a single commit from the branch being rebased. Be very careful with skipping to make sure changes aren't lost.
- "git rebase --abort" to go back to where you were before the rebase. (BE CAREFUL!)
git am -3
"git am -3" is very similar to rebase.
Given the setup described in "Some Conflicts" above, let's generate patch files from the b1 branch.
$ git checkout b1
Switched to branch 'b1'
$ git format-patch -M master
0001-Change-branch-b1-mergetool.patch
0002-Change-branch-b1-hi-bye.patch
Now let's apply the patches onto master with "git am -3". The "-3" is important as it requests a 3-way merge. Without this option, the conflicts will not appear in the appropriate files. "git diff" will show nothing.
$ git checkout master
Switched to branch 'master'
$ git am -3 *.patch
Applying: Change branch b1, mergetool
Using index info to reconstruct a base tree...
M mergetool.txt
Falling back to patching base and 3-way merge...
Auto-merging mergetool.txt
CONFLICT (content): Merge conflict in mergetool.txt
error: Failed to merge in the changes.
Patch failed at 0001 Change branch b1, mergetool
Use 'git am --show-current-patch' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
$ git diff mergetool.txt
diff --cc mergetool.txt
index 2b1c43c,3f83dbd..0000000
--- a/mergetool.txt
+++ b/mergetool.txt
@@@ -1,4 -1,4 +1,14 @@@
++<<<<<<< HEAD
+LINE 1: Branch master changed this.
+LINE 2
+LINE 3: Branch master changed this.
++||||||| merged common ancestors
++LINE 1
++LINE 2
++LINE 3
++=======
+ LINE 1
+ LINE 2: Branch b1 changed this.
+ LINE 3: Branch b1 changed this.
++>>>>>>> Change branch b1, mergetool
LINE 4: This will not be changed by anyone
At this point you can go ahead and edit the conflicting files and resolve the conflicts manually. If you prefer a merge GUI, try git mergetool.
As with merge and rebase, you can also use checkout --theirs and --ours to take one version or the other in its entirety. For "git am", the meaning of theirs and ours is logical. "theirs" is the version from the patch:
$ git checkout --theirs mergetool.txt
$ cat mergetool.txt
LINE 1
LINE 2: Branch b1 changed this.
LINE 3: Branch b1 changed this.
LINE 4: This will not be changed by anyone
While "ours" is the one in master:
$ git checkout --ours mergetool.txt
$ cat mergetool.txt
LINE 1: Branch master changed this.
LINE 2
LINE 3: Branch master changed this.
LINE 4: This will not be changed by anyone
And you can use checkout -m to undo any conflict resolution you've done in a file. Be careful as there is no "redo".
$ git checkout -m mergetool.txt
$ cat mergetool.txt
<<<<<<< ours
LINE 1: Branch master changed this.
LINE 2
LINE 3: Branch master changed this.
||||||| base
LINE 1
LINE 2
LINE 3
=======
LINE 1
LINE 2: Branch b1 changed this.
LINE 3: Branch b1 changed this.
>>>>>>> theirs
LINE 4: This will not be changed by anyone
Let's go with the version from the patch for this commit and continue.
$ git checkout --theirs mergetool.txt
$ git diff
diff --cc mergetool.txt
index 2b1c43c,3f83dbd..0000000
--- a/mergetool.txt
+++ b/mergetool.txt
The diff shows no conflicts left to resolve, so we add and let git know we are done resolving conflicts for this patch:
$ git add mergetool.txt
$ git am --continue
Applying: Change branch b1, mergetool
Applying: Change branch b1, hi/bye
Using index info to reconstruct a base tree...
M goodbye.txt
M hello.txt
Falling back to patching base and 3-way merge...
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Auto-merging goodbye.txt
CONFLICT (content): Merge conflict in goodbye.txt
error: Failed to merge in the changes.
Patch failed at 0002 Change branch b1, hi/bye
Use 'git am --show-current-patch' to see the failed patch
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Git moves on to the next patch and finds some more conflicts. Let's go ahead and reject all the changes from this patch and go with the changes in master (ours).
$ git checkout --ours hello.txt
$ git add hello.txt
$ git checkout --ours goodbye.txt
$ git add goodbye.txt
$ git am --continue
Applying: Change branch b1, hi/bye
No changes - did you forget to use 'git add'?
If there is nothing left to stage, chances are that something else
already introduced the same changes; you might want to skip this patch.
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
Like with rebase, we've effectively asked to discard an entire patch, so we must tell git we are serious about skipping this...
$ git am --skip
And that's it. As with rebase, be careful of the skip situation.
Aborting a "git am -3"
Just use am --abort:
$ git am --abort
Summary
- Always use "-3" with "git am" to make sure you get conflict markers.
- Use "git status" and "git diff" to find out what went wrong.
- Resolve the conflicts by any of the following methods:
- Edit each conflicting file with your favorite editor.
- "git checkout --theirs" or "git checkout --ours".
- "git checkout -m" to undo conflict resolution on specific files. (BE CAREFUL!)
- "git mergetool" and an appropriate merge GUI tool like kdiff3.
- "git add" the resolved files.
- "git am --continue" to continue the am.
- "git am --skip" in rare situations when you decide to throw away all the changes from a single patch. Be very careful with skipping to make sure changes aren't lost.
- "git am --abort" to go back to where you were before the merge. (BE CAREFUL!)
git apply
Essentially, you want to avoid git apply if you can. Its conflict resolution abilities are very limited. "git am -3" is significantly better and should be used whenever possible. But, if you've got a patch that isn't git-compatible, this is probably your only choice.
Given the setup described in "Some Conflicts" above, let's generate a patch file from b1 against master.
$ git checkout b1
Switched to branch 'b1'
$ git format-patch -M master
0001-Change-branch-b1-mergetool.patch
0002-Change-branch-b1-hi-bye.patch
And try to apply it to master. Don't forget to use "--reject" so we'll get .rej files:
$ git checkout master
Switched to branch 'master'
$ git apply --reject 0001*.patch
Checking patch mergetool.txt...
error: while searching for:
LINE 1
LINE 2
LINE 3
LINE 4: This will not be changed by anyone
error: patch failed: mergetool.txt:1
Applying patch mergetool.txt with 1 reject...
Rejected hunk #1.
Let's try git status to find out what happened.
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
0001-Change-branch-b1-mergetool.patch
0002-Change-branch-b1-hi-bye.patch
mergetool.txt.rej
nothing added to commit but untracked files present (use "git add" to track)
git status shows us the .rej file that was created. And it contains just the hunk that failed:
$ cat mergetool.txt.rej
diff a/mergetool.txt b/mergetool.txt (rejected hunks)
@@ -1,4 +1,4 @@
LINE 1
-LINE 2
-LINE 3
+LINE 2: Branch b1 changed this.
+LINE 3: Branch b1 changed this.
LINE 4: This will not be changed by anyone
To recover from this, the only option is to somehow manually apply the rejected hunk. All the nice "checkout -m", "checkout --theirs", etc... features are not available when using apply. Once finished, you can just add and commit if you want to keep the changes. apply doesn't have a "mode" like merge and rebase.
Aborting "git apply"
Use git reset --hard. Be very careful as this loses anything you've done since the last commit to your repo (HEAD).
$ git reset --hard
Summary
- Prefer "git am -3" to "git apply".
- Always use "--reject" with "git apply" to make sure you get .rej files that show you what parts (hunks) of the patch couldn't be applied.
- Use "git status" to find the .rej files.
- Resolve the conflicts by examining the .rej files and the target file and making changes with an editor.
- "git reset --hard" to abort the apply. (BE CAREFUL!)
- add and commit the changes as appropriate.
git stash pop
It's probably best to avoid conflicts with git stash pop by working in branches. But in case you do run into conflicts with stash pop, here's how to handle it.
When we pop the stash with conflicting changes in master and the stash, we get this:
$ git stash pop
Auto-merging stash.txt
CONFLICT (content): Merge conflict in stash.txt
$ git status
On branch master
Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: stash.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git diff stash.txt
diff --cc stash.txt
index 07d1fc9,e7e37f2..0000000
--- a/stash.txt
+++ b/stash.txt
@@@ -1,4 -1,4 +1,14 @@@
++<<<<<<< Updated upstream
+LINE 1: master changed this.
+LINE 2
+LINE 3: master changed this.
++||||||| merged common ancestors
++LINE 1
++LINE 2
++LINE 3
++=======
+ LINE 1
+ LINE 2: Stash changed this.
+ LINE 3: Stash changed this.
++>>>>>>> Stashed changes
LINE 4: This will not be changed by anyone
Resolve the Conflicts
As with other conflict situations, we can edit the conflicting files and resolve the conflicts manually. git mergetool also works fine with stash pop conflicts.
Although git checkout "--ours" and "--theirs" doesn't work with stash pop, we can still use checkout and specify which version we want. If we want to go with all the changes from master:
$ git checkout master -- stash.txt
$ cat stash.txt
LINE 1: master changed this.
LINE 2
LINE 3: master changed this.
LINE 4: This will not be changed by anyone
We can also go with all the changes in the stash for a file:
$ git checkout stash@{0} -- stash.txt
$ cat stash.txt
LINE 1
LINE 2: Stash changed this.
LINE 3: Stash changed this.
LINE 4: This will not be changed by anyone
And, if you want to go back to the version with conflicts, checkout -m works fine.
$ git checkout -m stash.txt
$ cat stash.txt
<<<<<<< ours
LINE 1: master changed this.
LINE 2
LINE 3: master changed this.
||||||| base
LINE 1
LINE 2
LINE 3
=======
LINE 1
LINE 2: Stash changed this.
LINE 3: Stash changed this.
>>>>>>> theirs
LINE 4: This will not be changed by anyone
Stash Drop
Once you've got the conflicts resolved, you can manually remove the stashed changes from the stash with "git stash drop".
$ git stash drop
Dropped refs/stash@{0} (70d16711ae12d341b53f6f3faca9afa1950be8cd)
Continue Working
Work popped from the stash is typically work in progress, so at this point you can continue working, however git status will look a bit strange with "unmerged paths". If this bothers you, you can "git add".
$ git add stash.txt
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: stash.txt
Just make sure you "git add" the file again if you make further changes.
Aborting a Stash Pop
To abort a conflicting stash pop and throw away all work you've done since the last commit to master (this can be exceedingly dangerous if you tend to make a lot of changes without committing, so be very careful):
$ git reset --hard
This takes you back to the last commit in master. This can be quite dangerous. Especially if you've done "git stash drop". Then the stashed changes are lost. (Not really, they are just orphaned and can be recovered so long as you don't do a "gc".) If you were in the middle of conflict resolution, this throws away all of your work.
Summary
When dealing with a conflict in git stash pop:
- Use "git status" and "git diff" to find out what went wrong.
- Resolve the conflicts by any of the following methods:
- Edit each conflicting file with your favorite editor.
- "git mergetool" and an appropriate merge GUI tool like kdiff3.
- "git checkout master -- filename" to get the master version.
- "git checkout stash@{0} -- filename" to get the stash version.
- "git checkout -m" to undo conflict resolution on specific files. (BE CAREFUL!)
- "git add" the resolved files (optional).
- "git stash drop" when finished resolving conflicts to remove the changes from the stash.
- If things get hosed, use "git reset --hard" to start over before the stash pop. (BE CAREFUL!)
- Continue working as usual.
Links
Conflicts and Resolutions - Great basic article from the gitguys.
Conflict Resolution in Git - Movie showing how to use "git mergetool" and the opendiff GUI to resolve conflicts.
Painless Merge Conflict Resolution - Shows the joys of "merge.conflictstyle diff3".
git checkout -m file - Learn about undoing conflict resolution with "git checkout -m file". This is a great post by a git dev. The way he talks about conflict resolution gives you more of a feel for what it's all about.
TODO
Careful examination of the merge section shows some ambiquity WRT whether or not "git add" is actually required. I think we need to track this down. It might mean that we would need to hand-edit one of the files to see a situation where "add" is required. Once this is sorted, determine whether this also affects the other sections.
Examples for revert and cherry-pick. Or at least a quick summary of which of the big three they are most similar to, and any gotchas to watch out for.
Work in "git log --merge -p file". It shows the changes from the common point to where you are now. Might be useful in more complex situations.
License
Copyright (C) 2013-2014, Ted Felix
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. See the Free Documentation License for the full text of this license.
<- Back to my software page.