为什么这个cherry-pick会有冲突?

22

我知道git cherry-pick是一个命令,用于应用指定提交的更改,但我认为我并没有真正理解它的工作方式。

假设有一个仓库运作如下:

git init

echo a>a
git add .; git commit -am 'master add line a'

git checkout -b dev
echo b>>a
git commit -am 'dev add line b'
echo c>>a
git commit -am 'dev add line c'

git checkout master

git cherry-pick dev

我以为 cherry-pick 命令会很好地工作,并将文件 a 更改为:

a

c

但实际上我收到了以下信息:

error: could not apply 08e8d3e... dev add line c
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'

然后我运行:

git diff

输出:

diff --cc a
index 7898192,de98044..0000000
--- a/a
+++ b/a
@@@ -1,1 -1,3 +1,6 @@@
  a
++<<<<<<< HEAD
++=======
+ b
+ c
++>>>>>>> 11fff29... abc

所以我的问题是: 为什么会有像git-diff显示的冲突? 在这种情况下,cherry-pick的工作细节是什么?

@TimBiegeleisen 但是如果我运行git merge dev,就不会出现冲突,并且显示我已经成功合并了... - L_K
3
@TimBiegeleisen dev 指的是分支 dev 的最后一次提交,所以我认为 git cherry-pick dev 没有任何问题。 - L_K
谢谢您指出这一点,我怀疑过但从未亲自使用过,我刚学到了新知识 :-) 在合并期间可能已经自动解决了冲突。 - Tim Biegeleisen
1
将许多提交合并起来的过程就像是连续进行小修改一样。在这种情况下,可能没有冲突,因为Git可以处理每个小修改。当你挑选最新的提交时,Git可能会看到一些戏剧性的东西,它无法在没有手动干预的情况下解决。这是一个解释。 - Tim Biegeleisen
@TimBiegeleisen 谢谢你,但是我还是有点困惑... - L_K
显示剩余5条评论
2个回答

30

在此之后,请再次尝试你的挑选:

git config merge.conflictstyle diff3

您将获得更详细的差异:

<<<<<<< HEAD
||||||| parent of 5b2a14c... dev add line c
b
=======
b
c
>>>>>>> 5b2a14c... dev add line c

这说明在应用由dev的HEAD(bc)所表示的补丁时,Git并不知道共同祖先是什么,它会转而使用:

  • cherry-pick提交的直接父提交(显示在一行“b”后添加了一行“c”)
  • 目标提交(根本没有任何关于添加改动“c”之前的行“b”的记录)

因此会发生冲突。

Cherry-pickmerge(寻找合并基础)不同。

Cherry-pick取得一个提交并应用其引入的更改

这里引入的更改是:在b的基础上添加c
目标提交完全没有b,所以对于Git:

  • 上游(目标)提交已经“删除了b”(或者根本就没有这个提交,这是本例的情况,但Git并不知道),
  • 源提交上有一个b,在其之上添加了c

对于Git而言,在尝试应用该补丁时(这就是git cherry-pick所做的全部:应用补丁。它根本不查找被cherry-pick提交的历史信息),这是一个冲突:并发修改。

如果您确定解决方案的正确性,可以执行:

> git cherry-pick -Xtheirs dev
[master 7849e0c] dev add line c
 Date: Wed Aug 17 08:25:48 2016 +0200
 1 file changed, 2 insertions(+)

那么,您将看到 bc 被添加到原始提交中,而不会出现任何冲突(因为您使用选项 '-Xtheirs' 传递给默认合并策略recursive来指示如何解决冲突)。


@JeffPuckettII,这与“上一行”并不相关,而是与“从挑选的提交和其直接父级之间制作的补丁”有关。在这里,最后一个dev提交和其父级之间的差异是在b的基础上增加了c。它应用在最后一行并不重要。Git无法在应用补丁到HEAD时找到与所涉及行附近相似的b,这一点很重要。 - VonC
@huachengzan 没错,这个补丁的上下文是 b 行。而目标中缺少了这一行。 - VonC
感谢您和@JeffPuckettII,解决了我的难题 :-) - L_K
2
我在回复你的回复中提到过这一点,但是:git cherry-pick实际上确实使用合并基础并进行三方合并。问题在于,在许多情况下(但不是所有情况),合并基础本身相当错误,因此没有帮助,甚至可能有害。这意味着Git首先尝试简单的“直接修补”方法。如果失败了,它会退回到使用挑选的提交的父提交作为合并基础。偶尔,这可以使补丁正确应用,特别是如果该“基础”确实在当前历史记录中。 - torek
@tor 我同意回退部分:这与我在那个实例中看到的一致。 - VonC
显示剩余8条评论

-1

从技术上讲,由于您在不同的分支上编辑相同文件的同一行,Git 将其视为冲突。虽然 Cherry-picking 不是技术上的“合并”操作,但仍会寻找相同类型的冲突,并要求您解决它们。

对于冲突路径,索引文件记录了最多三个版本,如 git-merge[1] 中的“真正合并”部分所述。工作树文件将包括一个由通常的冲突标记 <<<<<<< 和 >>>>>>> 括起来的冲突描述。

来自 git-scm 文档


2
嗯...我认为我没有编辑同一文件的同一行,只是在文件末尾添加了一些行。 - L_K

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