如何在Git rebase过程中通过哈希值识别冲突提交?

48
当我使用git rebase时遇到合并冲突,如何确定冲突的来源(以提交为单位),而不仅仅是文件差异?
我已经知道如何在git rebase --continue之前使用git mergetool或git add(基础用法),但有时文件之间的差异并不足够:我想看到刚刚未能应用于工作树的提交日志和差异。
我阅读了其他问题中的答案,其中提到如果我正在使用git merge,则git log --merge将显示父提交。当我遇到冲突时还是尝试了它,但被告知“致命错误:--merge没有MERGE_HEAD?”。
如何确定有问题的提交?

不要忘记2018年3月的Git 2.17+命令git am --show-current-patch。请参见下面的答案 - VonC
8个回答

41

简短回答

如果显示为

Patch failed at 0001 commit message for F

然后运行

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

要获取失败提交的SHA ad1c77,然后使用 git show ad1c77 命令查看它。

长答案

让我们从这个树开始:

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

$ git checkout G
$ git rebase D

当发生变基冲突时,它是发生在

  • 上游更改(C--D)与公共祖先(B)之间的已经重新基于的更改和已经解决的冲突(E')相加形成的对比
  • 下一次提交的补丁 (F)

让我们看看会发生什么:

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'

以下是错误信息:

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

首先,你可以看到它是F,因为提交信息出现了。但是,如果你的提交信息都像“foo”、“documentation”或“一些修复”,那么这并没有帮助,你实际上需要查看SHA ID ad1c77或补丁内容。

以下是查找 F 的真实身份的方法:

当它列出变基冲突时,会显示类似于以下内容:

Patch failed at 0001 commit message for F

现在查看 .git/rebase-apply/,您将在那里找到补丁文件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

补丁文件包括原始的提交ID。

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

你可以查看那个。

肯定有更简单的方式,但这个方法也有效。

注意,补丁失败可能是由于不同的提交(如果你正在将其变基到HEAD和变基目标的公共祖先)。找到该提交要复杂得多,尽管你可以尝试反向进行变基以找到它:

$ git checkout D
$ git rebase G

请在.git/rebase-apply-old/next目录下查找当前正在尝试的补丁号“1”。请注意,它没有前导零。 - Alex Brown
1
非常有用!如果您只想要SHA,还可以使用head -1 .git/rebase-apply/0001 | awk '{ print $2 }'命令。如果您想立即显示提交,则可以使用git show $(head -1 .git/rebase-apply/0001 | awk '{ print $2 }')命令。 - Dan
非常感谢!我一直在努力获取完整的提交消息,因为它显示给我的那一行片段并不足以告诉我应该怎样正确解决冲突。 - DerfK
这似乎是一件非常明显需要的事情,我无法相信像 Azriel 的 'git show' 那样的杂技不被构建到工具中。谢谢! - zultron
这非常有用。感谢(y) - Dzung Nguyen
显示剩余2条评论

11

在进行 git rebase 时,如果遇到冲突需要停止解决时,以下命令将显示冲突的提交(所有内容,而不仅是冲突的文件),也就是您当前正在重播/重新基于新基础的提交,无论您在何处:

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

如果您只想查看特定冲突文件(您正在解决的文件)的冲突情况,请使用独立模式:

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

在构建这个答案的过程中没有虐待猫咪 :).


10

显示当前/失败的提交

这可能是一个新功能,但是REBASE_HEAD可以给你停留在哪个提交上(例如,如果提交无法应用)。如果您想查看完整的提交信息,可以使用:

git show REBASE_HEAD

作为更冗长的替代方案,您可以使用git rebase --show-commit-patch。文档中说它们是等效的。
显示自从开始工作以来发生了什么变化
如果您想查看正在重定位的分支和目标分支之间的差异,可以获取两个分支之间的差异。例如,如果您正在从master重新定位到origin/master,则可以使用:
git diff master..origin/master

如果您想将更改作为单独的提交查看:

git log -p master..origin/master

如果你更喜欢使用哈希值,或者重新创建衍合分支有一段时间了,忘记了正在衍合哪些分支,你可以使用 git status 命令查看这两个分支。例如:

你当前正在将分支“master”衍合到“b5284275”上。

然后,你可以使用以下命令查看发生的变化:

git diff master..b5284275

1
你可能想使用带有三个点的diff(master...origin/master)和带有两个点的log(master..origin/master),请参见https://dev59.com/fmw05IYBdhLWcg3wahNN#7256391。 - Henrik Karlsson
这里是 --show-current-patch - ricab
这里是 --show-current-patch - undefined

6
自Git 2.17(2018年3月)以来,您不应该需要使用rebase-apply
新的"--show-current-patch"选项提供了一种面向最终用户的方法,在"git rebase"(和"git am")在冲突时停止时获取正在应用的diff

查看 提交 fbd7a23, 提交 6633529, 提交 984913a (2018年2月11日) 由Nguyễn Thái Ngọc Duy (pclouds)完成。
(由Junio C Hamano -- gitster --合并于提交 9ca488c, 2018年3月6日)

am: 添加 --show-current-patch 选项

签署者: Nguyễn Thái Ngọc Duy

指向 $GIT_DIR/rebase-apply 可能会鼓励用户在那里胡乱操作,这是不好的。

有了这个选项,当用户需要查看补丁时,他们不必再将路径保留在某个地方(因为经过几个命令后,该路径可能已经超出滚动缓冲区)。

更多信息请参见 "显示当前 git 交互式 rebase 操作"

示例:

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

然后你会得到:

C:\Users\VonC\rep\src>git am --show-current-patch
  commit xxx (master)
  Author: VonC <vonc@vonc.com>
  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" 是一种展示停止步骤的电子邮件片段的方式,不适合直接输入到 "git apply"(它被设计为一个良好的 "git am" 输入)。

随着 Git 2.26(2020年第一季度)的推出,它学习了一个新选项,仅显示补丁部分。

查看 提交 aa416b2, 提交 f3b4822, 提交 e8ef1e8, 提交 bc8620b, 提交 62e7a6f (2020年2月20日) 由Paolo Bonzini (bonzini)提交。
(由Junio C Hamano -- gitster --合并于提交0e0d717, 2020年3月9日)

am: 支持 --show-current-patch=diff 以检索 .git/rebase-apply/patch

报告者: J. Bruce Fields
签名作者: Paolo Bonzini

commit 984913a210 ("am: 添加 --show-current-patch", 2018-02-12, Git v2.17.0-rc0 -- 合并 列在 批次 #7 中) 加入 "git am --show-current-patch" 后,"git am" 开始推荐它作为 .git/rebase-merge/patch 的替代品。

不幸的是,这个建议有些误导人;例如,"git am --show-current-patch" 的输出如果编码为 quoted-printable 或 base64,则无法传递给 "git apply"。

添加一个新模式到 "git am --show-current-patch" 中,以纠正这个建议。

新模式diff

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

显示由于冲突而git am已停止的消息。
如果指定了raw,则显示电子邮件消息的原始内容;如果指定了diff,则仅显示差异部分。
默认为raw

还有:

am:支持--show-current-patch=raw作为--show-current-patch的同义词

Signed-off-by: Paolo Bonzini

为了简化工作树操作并避免用户进入.git,最好让 "git am" 也提供一种将 .git/rebase-merge/patch 复制到标准输出的模式。

一种可能是完全分开选项,引入例如 --show-current-message(用于 .git/rebase-apply/NNNN)和 --show-current-diff(用于 .git/rebase-apply/patch),同时可能弃用 --show-current-patch。

这甚至可以删除系列中前两个补丁的需要。但是,长公共前缀会阻止使用缩写选项,例如 "--show"。

因此,我选择在 --show-current-patch 中添加一个字符串参数。


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

鉴于这个:
A---B---C---D
     \
      E---F---G

$ git checkout G
$ git rebase D

鉴于在应用F时存在合并冲突:

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

如果执行了以上操作,original-commit文件将显示F的哈希值。这是“theirs”的版本。

此外,在这种情况下,HEAD(.git/HEAD)将为E'。这是“mine”的版本。HEAD^将是“base”版本。

对于至少git 1.7.9版本,这是正确的。


2

不确定为什么在我的情况下没有 .git/rebase-apply。 对于处于相同情况的人,这是我的变化。

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

作为别名,您可以使用以下命令:
git config --global alias.sp='!git show $(cat .git/rebase-merge/stopped-sha)'

5
rebase-apply文件夹用于非交互式的变基操作,而rebase-merge则用于交互式的变基操作。 - LoKi

1
要查看冲突发生时正在应用的提交,请使用...
git am --show-current-patch

0

很多时候,在进行变基(rebase)操作时,你会想跳过那些不必要的提交。

不幸的是,尽管 git status 告诉你正在处理提交,并建议使用 git rebase --continue, git rebase --skipgit rebase --abort 命令,但它并不告诉你当前所在的提交。

因此,有时很难知道你应该使用 git rebase --skip 还是不用。

然而,仍有一种方法可以通过运行以下命令来找出你当前所在的提交:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接