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:

  1. Use "git status" and "git diff" to find out what went wrong.
  2. Resolve the conflicts by any of the following methods:
  3. "git add" the resolved files.
  4. If things get hosed, use "git merge --abort" to start over before the merge. (BE CAREFUL!)
  5. "git commit" to finish and get out of merge mode.

git mergetool

"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.

Screenshot of kdiff3

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:

  1. Use "git status" and "git diff" to find out what went wrong.
  2. Resolve the conflicts by any of the following methods:
  3. "git add" the resolved files.
  4. "git rebase --continue" to continue the rebase.
  5. "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.
  6. "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

  1. Always use "-3" with "git am" to make sure you get conflict markers.
  2. Use "git status" and "git diff" to find out what went wrong.
  3. Resolve the conflicts by any of the following methods:
  4. "git add" the resolved files.
  5. "git am --continue" to continue the am.
  6. "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.
  7. "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

  1. Prefer "git am -3" to "git apply".
  2. 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.
  3. Use "git status" to find the .rej files.
  4. Resolve the conflicts by examining the .rej files and the target file and making changes with an editor.
  5. "git reset --hard" to abort the apply. (BE CAREFUL!)
  6. 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:

  1. Use "git status" and "git diff" to find out what went wrong.
  2. Resolve the conflicts by any of the following methods:
  3. "git stash drop" when finished resolving conflicts to remove the changes from the stash.
  4. If things get hosed, use "git reset --hard" to start over before the stash pop. (BE CAREFUL!)
  5. 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.