【问题标题】:Why does this cherry-pick have a conflict?为什么这个樱桃挑选会发生冲突?
【发布时间】:2016-12-16 08:01:56
【问题描述】:

我知道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的工作细节是什么?

【问题讨论】:

  • Git cherry pick 是一种选择并将来自另一个分支的单个提交应用到当前分支的方法。它本质上是对一个提交进行合并,因此您可能会遇到冲突。您所看到的不一定有任何问题。
  • @TimBiegeleisen 但是如果我运行git merge dev,没有冲突,说明我合并成功了...
  • git cherry-pick dev 对我来说甚至没有意义,因为您指定的是分支名称而不是提交。
  • @TimBiegeleisen dev 指的是分支dev 的最后一次提交,所以我认为git cherry-pick dev 没有问题。
  • 考虑合并许多提交,例如进行小的更改,每个提交都是连续的。在这种情况下,可能不会有冲突,因为 Git 可以处理每个小的更改。当你挑选最新的提交时,Git 可能会看到一些戏剧性的事情,如果没有人工干预,它就无法解决。这是一种解释。

标签: git cherry-pick git-cherry-pick


【解决方案1】:

之后再试一次:

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-picked 提交的直接父级(表明它在“b”行之后添加了一行“c”)
  • 目标提交(它根本没有显示任何行 b 可以应用添加的更改 'c')

因此发生冲突。

Cherry-picking 不像 merge(它会查找 merge-base)。

Cherry-picking 接受提交并应用它引入的更改

这里引入的变化是:在b之上添加c
并且目标提交根本没有b,所以对于Git:

  • 上游(目标)提交已“删除 b”(或者一开始就没有,这里就是这种情况,但 Git 不知道),
  • 源提交有一个b,在其顶部添加了c

据 Git 在尝试应用该补丁时所知道的(这就是 git 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 添加到原始提交中,没有任何冲突(因为您指示如何使用传递给 default merge strategy recursive 的选项“-Xtheirs”来解决它)

【讨论】:

  • 巧合的是,冲突错误信息正在被重写和讨论中:marc.info/?t=146956231900001&r=1&w=2
  • "cherry-picked commit 的直接父级(表明它在行 'b' 之后添加行 'c')" 是我对它的工作方式,但我也认为 git 会足够聪明地识别它位于文件的最后一行,这样它就不会发生冲突。 git 是否总是按照 上面的行 进行放置?
  • @JeffPuckettII 与其说是“上面的那一行”,不如说是“在精选的提交与其直接父级之间制作的补丁”。这里最后一个dev 提交与其父级之间的增量是'c' 在'b' 之上。在最后一行应用它的事实在这里无关紧要。 Git 在相关行附近找不到“b”这一事实(在 HEAD 上应用补丁时)确实很重要。
  • 我知道要应用更改,git 将计算一个补丁,其中不仅包括更改的行,还包括围绕它的一些上下文行?
  • 我在另一个回复你的回复中提到了这一点,但是:git cherry-pick 确实实际上使用了合并基础并进行了三向合并。这里的问题是,在许多(但不是全部)情况下,合并基础本身是非常虚假的,因此没有帮助,甚至可能有害。这意味着 Git 尝试了一种简单的“直接补丁”方法首先。如果失败,它会退回到使用精心挑选的提交的父提交作为合并基础。有时,这可以让补丁正确应用,特别是如果该“基础”确实存在于当前历史记录中。
【解决方案2】:

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

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

来自git-scm documentation

【讨论】:

  • 嗯...我想我没有编辑同一个文件的同一行,只是在文件末尾添加一些行。
  • 不知道为什么这个答案在最后几分钟内获得了两个赞成票:这并不能解释考虑到并发更改,cherry-pick 最终是如何结束的。它只说明了明显的(即:“并发更改意味着冲突,如果冲突,Git 会要求您解决它”)。所有的问题都是“Git 是如何得出并发变化的结论的?”。
猜你喜欢
  • 1970-01-01
  • 2013-12-30
  • 1970-01-01
  • 2019-12-24
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-21
相关资源
最近更新 更多