How to identify conflicting commits by hash during git rebase?

放肆的年华 提交于 2019-11-27 11:46:57

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

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 :).

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

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)'

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)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!