50

When I encounter a merge conflict using git rebase, how can I identify the source of the conflict in terms of commits, rather than just file differences?

I already know how to make (basic) use of git mergetool or git add before git rebase --continue, but sometimes the differences between files just isn't enough: I want to see the commit log and diff of the commit that just failed to be applied to the working tree.

I've read in other questions that git log --merge would show the parent commits if I were using git merge. I tried it anyways when I encountered a conflict and got told fatal: --merge without MERGE_HEAD?.

How can I identify the problematic commit?

1
  • Don't forget the March 2018 Git 2.17+ command git am --show-current-patch. See my answer below.
    – VonC
    Commented Nov 4, 2019 at 17:12

8 Answers 8

41

Short Answer

If it says

Patch failed at 0001 commit message for F

Then run

$ head -1 .git/rebase-apply/0001
From ad1c7739c1152502229e3f2ab759ec5323988326 Mon Sep 17 00:00:00 2001

To get the SHA ad1c77 of the failing commit, and then use git show ad1c77 to have a look at it.

Long Answer

Let's start with this tree:

A---B---C---D
     \
      E---F---G

$ git checkout G
$ git rebase D

When a rebase conflict occurs, it is a conflict between

  • the upstream changes (C--D) from the the common ancestor (B) PLUS the already rebased changes and already resolved conflict (E') versus
  • the patch of the next commit (F)

Let's see what happens:

1) A---B---C---D---E'          <- E patched and committed successfully as E'
2) A---B---C---D---E'---       <- failed to patch F onto E'

Here's the error message:

First, rewinding head to replay your work on top of it...
Applying: commit message for F
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
Auto-merging 1.txt
CONFLICT (content): Merge conflict in 1.txt
Failed to merge in the changes.
Patch failed at 0001 commit message for F

First, you can see that it was F, because the commit message appears. However, if your commit messages all look like "foo", "documentation" or "some fixes", then this won't help, and you really want the SHA id ad1c77 or the contents of the patch.

Here's how to find out the real identity of F:

When it lists the rebase conflict, it will say something like:

Patch failed at 0001 commit message for F

Now look in .git/rebase-apply/, where you will find the patch file 0001:

$ ls .git/rebase-apply
0001          head-name     msg           orig-head     sign
0002          info          msg-clean     patch         threeway
apply-opt     keep          next          quiet         utf8
final-commit  last          onto          rebasing

The patch file includes the original commit-id

$ head -1 .git/rebase-apply/0001
From ad1c7739c1152502229e3f2ab759ec5323988326 Mon Sep 17 00:00:00 2001

You can then look at that.

There must be an easier way, but this works.

Note that the fact that the patch failed may be due to a different commit (if you are rebasing onto a common ancestor of HEAD and the rebase target). Finding that commit is rather more complicated, although you could try doing the rebase in reverse to find it:

$ git checkout D
$ git rebase G
6
  • Nice answer! For me, the commit subjects are enough to identify things (particularly since the ref hasn't been updated yet), but it's always good to know you can look in rebase-apply for a backup.
    – Cascabel
    Commented Jan 22, 2010 at 16:37
  • Excellent answer! What about if you've lost the "Patch failed at 0001 commit message for F" message (scrollback, SSH session died etc)?
    – RobM
    Commented Jan 22, 2010 at 17:11
  • Look in .git/rebase-apply-old/next to find the patch number 1 of the patch it's trying at the moment. Note it doesn't have the leading zeroes.
    – Alex Brown
    Commented Jan 22, 2010 at 17:18
  • 1
    Very helpful! You can also use head -1 .git/rebase-apply/0001 | awk '{ print $2 }' if you just want the SHA and git show $(head -1 .git/rebase-apply/0001 | awk '{ print $2 }') if you want to immediately show the commit
    – Dan
    Commented Dec 12, 2012 at 18:10
  • Thanks a lot! I've been beating my head on getting the complete commit message because the fragment of a line it shows me isn't enough to tell me what should be done to fix the conflict correctly.
    – DerfK
    Commented Jul 16, 2013 at 21:40
12

Show the current/failed commit

This may be a new feature, but REBASE_HEAD gives you the commit where you're currently stopped (e.g. if the commit failed to apply). If you want to see the commit in full you can use

git show REBASE_HEAD

As a more verbose alternative, you can use git rebase --show-commit-patch. The docs say they are equivalent.

Show what's changed since you started your work

If you want to see what's changed between where you're rebasing from and where you're rebasing to, you can get a diff between the two branches. For example, if you're rebasing from master onto origin/master you can use:

git diff master..origin/master

Or if you want to see the changes as individual commits:

git log -p master..origin/master

If you'd prefer to use the hash or maybe are coming back to a rebase after a while and can't remember which branches you're rebasing, you can use git status to see the two branches. For example:

You are currently rebasing branch 'master' on 'b5284275'

Then, to see what's changed you can use:

git diff master..b5284275
2
  • 1
    You may want to use diff with three dots (master...origin/master) and log with two (master..origin/master), see stackoverflow.com/a/7256391/502126 Commented May 22, 2019 at 16:18
  • it's --show-current-patch here
    – ricab
    Commented Jun 22, 2023 at 16:35
11

During a git rebase which stops to resolve conflicts, the following command will show the conflicting commit (all of it, not just the conflicting files), that is, your commit currently being replayed/rebased onto the new base, regardless of where you are up-to:

git show $(< .git/rebase-apply/original-commit)

If you want to see only the conflicts for a specific conflicting file (the one you are resolving), in-isolation:

git show $(< .git/rebase-apply/original-commit) -- /path/to/conflicting/file

No cats were abused in the construction of this answer :).

0
6

Since Git 2.17 (March 2018), you shouldn't need to use rebase-apply.

The new "--show-current-patch" option gives an end-user facing way to get the diff being applied when "git rebase" (and "git am") stops with a conflict.

See commit fbd7a23, commit 6633529, commit 984913a (11 Feb 2018) by Nguyễn Thái Ngọc Duy (pclouds).
(Merged by Junio C Hamano -- gitster -- in commit 9ca488c, 06 Mar 2018)

am: add --show-current-patch

Signed-off-by: Nguyễn Thái Ngọc Duy

Pointing the user to $GIT_DIR/rebase-apply may encourage them to mess around in there, which is not a good thing.

With this, the user does not have to keep the path around somewhere (because after a couple of commands, the path may be out of scrollback buffer) when they need to look at the patch.

See more at "Show current git interactive rebase operation"

Example:

C:\Users\VonC\repo\src>git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: change code
Using index info to reconstruct a base tree...
M       a/src/file
Falling back to patching base and 3-way merge...
Auto-merging a/src/file
CONFLICT (content): Merge conflict in a/src/file
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch' to see the failed patch                  <======
Patch failed at 0001 change code
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".

You would then get:

C:\Users\VonC\rep\src>git am --show-current-patch
  commit xxx (master)
  Author: VonC <[email protected]>
  Date:   Mon Nov 4 13:59:18 2019 +0100

      change code

  diff --git a/a/src/file b/a/src/file
  index yyy..zzz 100644
  --- a/a/src/file
  +++ b/a/src/file
  @@ -13,5 +13,5 @@ file: /a/src
   content line 1
   content line 2
   content line 3
   content line 4
  -content line 5
  -content line 6
  +content bis line 5
  +content bis line 6

"git am --short-current-patch" is a way to show the piece of e-mail for the stopped step, which is not suitable to directly feed "git apply" (it is designed to be a good "git am" input).

With Git 2.26 (Q1 2020), it learned a new option to show only the patch part.

See commit aa416b2, commit f3b4822, commit e8ef1e8, commit bc8620b, commit 62e7a6f (20 Feb 2020) by Paolo Bonzini (bonzini).
(Merged by Junio C Hamano -- gitster -- in commit 0e0d717, 09 Mar 2020)

am: support --show-current-patch=diff to retrieve .git/rebase-apply/patch

Reported-by: J. Bruce Fields
Signed-off-by: Paolo Bonzini

When "git am --show-current-patch" was added in commit 984913a210 ("am: add --show-current-patch", 2018-02-12, Git v2.17.0-rc0 -- merge listed in batch #7), "git am" started recommending it as a replacement for .git/rebase-merge/patch.

Unfortunately the suggestion is somewhat misguided; for example, the output of "git am --show-current-patch" cannot be passed to "git apply" if it is encoded as quoted-printable or base64.

Add a new mode to "git am --show-current-patch" in order to straighten the suggestion.

The new mode is diff:

--show-current-patch[=(diff|raw)]:

Show the message at which git am has stopped due to conflicts.
If raw is specified, show the raw contents of the e-mail message; if diff, show the diff portion only.
Defaults to raw.

And:

am: support --show-current-patch=raw as a synonym for --show-current-patch

Signed-off-by: Paolo Bonzini

To simplify worktree operations and to avoid that users poke into .git, it would be better if "git am" also provided a mode that copies .git/rebase-merge/patch to stdout.

One possibility could be to have completely separate options, introducing for example --show-current-message (for .git/rebase-apply/NNNN) and --show-current-diff (for .git/rebase-apply/patch), while possibly deprecating --show-current-patch.

That would even remove the need for the first two patches in the series. However, the long common prefix would have prevented using an abbreviated option such as "--show".

Therefore, I chose instead to add a string argument to --show-current-patch.

2
cat .git/rebase-apply/original-commit

Given this:

A---B---C---D
     \
      E---F---G

$ git checkout G
$ git rebase D

and given that there's a merge conflict trying to apply F:

A---B---C---D--E'--!
     \
      E---F---G

then the original-commit file will show the hash of F. This is "theirs" version.

Also, HEAD (.git/HEAD) will be E' in this case. This is "mine" version. HEAD^ will be "base" version.

This is true for at least git 1.7.9

2

Not sure why I don't have .git/rebase-apply in my situation. For those in the same situation, here's my variation.

git show $(cat .git/rebase-merge/stopped-sha)

Or as an alias...

git config --global alias.sp='!git show $(cat .git/rebase-merge/stopped-sha)'
1
  • 5
    The rebase-apply folder is created for non-interactive rebase, while rebase-merge is for interactive one.
    – LoKi
    Commented Dec 13, 2015 at 15:59
1

To see the commit that was being applied when the conflict occurred, use...

git am --show-current-patch
0

Many times you'll be in the middle of a rebase and want to skip those commits that aren't needed.

Unfortunately, while git status tells you that you're in the middle of commit and recommends using git rebase --continue git rebase --skip or git rebase --abort, it doesn't tell you what commit you're currently on.

So it can oftentimes be hard to know if you should git rebase --skip or not.

However, there is still a way to find out which commit you're on by running:

git log -1 $(< .git/rebase-apply/original-commit)

Not the answer you're looking for? Browse other questions tagged or ask your own question.