Git cherry-pick导致合并冲突,而合并不会

时间:2016-05-15 11:21:07

标签: git version-control merge cherry-pick

我正在尝试学习如何使用git cherry pick,我通过git阅读了git cherry-pick --help返回的手册页,但这似乎没有帮助。我将尝试解释下面的问题。我有两个分支masterother

在分支master 上 提交历史记录是

0x2 Second commit from master branch
0x1 Initial commit

我跟踪的readme存储库中唯一的文件具有以下内容

Some text

在分支other 上 提交历史记录是

0x5 CHECKPOINT, going to cherry-pick onto master
0x4 second commit from branch other
0x3 first commit from other branch:
0x2 Second commit from master branch
0x1 Initial commit

readme文件的内容是

Some text.

Adding some text from other branch.  Adding some more text.

Going to cherry-pick this commit.

两个分支上的工作目录都是干净的,没有未跟踪的更改。从这一点开始,当我切换到主分支并与git merge other合并时,合并优雅地发生,没有合并冲突。但是当我尝试git cherry-pick 0x5时出现合并冲突时,我从git得到以下输出

error: could not apply 0x5... CHECKPOINT, going to cherry-pick onto master
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'

readme文件包含以下内容

<<<<<<< HEAD
Some text
=======
Some text.

Adding some text from other branch.  Adding some more text.

Going to cherry-pick this commit.
>>>>>>> 0x5... CHECKPOINT, going to cherry-pick onto master

为什么会出现这种合并冲突?我试图理解它为什么会发生。不应该cherry-pick与尝试在自己cherry-pick提交的提交上进行所有编辑,然后将更改提交到分支(master在这种情况下)?

git中确实存在合并冲突时?我似乎在奇怪的时候得到它们。此实现是否依赖(例如,取决于用于合并的算法)?

谢谢!

4 个答案:

答案 0 :(得分:4)

我刚刚在阅读后测试了这个场景:

如果第一行(Some text.而不是Some text)的修改是在0x2之后立即完成的,如在0x3中,则git cherry-pick 0x3可以正常工作。

但如果稍后提交相同的更改,则git cherry-pick不会生成与0x2内容兼容的补丁。

没有&#34;共同祖先的概念&#34;这里(as incorrectly stated here):仅限于#34; patch&#34;。

git cherry-pick,即使它使用合并策略, a git merge

  • git merge将寻找一个共同的祖先(返回提交历史为0x5,找到一个常见的提交0x2:这里是0x2本身)
  • git cherry-pick只通过在两次提交之间执行统一差异来创建补丁(默认情况下,您提及的提交及其直接父级0x4)

重点是:该补丁无法在0x2之上应用。

请亲自尝试:git cherry-pick 0x5就像做git diff -p 0x4..0x5一样 您将在那些 0x2的补丁行中看到,并反映来自0x3或0x4的变化。

这样的补丁不能应用于0x2,因为它基于0x2不存在的内容。

我已经创建了一个测试仓库,我在其中进行了两次修改。

  • 从其他分支添加一些文本(不修改第一行)
  • 然后第二次提交修改第一行。

这是我的补丁的样子:

C:\Users\vonc\prog\git\tests\cp>git diff -p dev~..dev
diff --git a/f b/f
index 2b6dd7b..1c4bb79 100644
--- a/f
+++ b/f
@@ -1,4 +1,4 @@
-"Some text"
+"Some text."^M

 Adding some text from other branch.  Adding some more text.

我无法应用(cherry-pick)提交到0x2,因为0x2没有行&#34; Adding some text from other branch&#34;然而。它只有一行"Some text"

这就是git报告冲突的原因:内容已经分歧,并且该补丁应该在包含&#34; Adding some text from other branch&#34;的文件顶部更改第1行,其中0x2没有

这当然不同于git merge,因为git merge会寻找一个共同的祖先(这里是0x2)并将所有修改从0x2报告到0x2而没有任何问题。

答案 1 :(得分:2)

与其他答案相比,我的技术性更强,更具“常识性”。 (这并不总是正确的做法,但我认为就是这种情况。)

你在想,“很明显,我想要的改变只是在文件的末尾附加一行。为什么git不能只是这样做?”

Git正在应用补丁。应用补丁涉及尝试找出补丁应该去的位置。这包括查看近似行号并在进行更改之前和之后查找相同上​​下文的 n 行。在这种情况下,后上下文是文件结束。但前面的上下文“从其他分支添加一些文本。添加更多文本。”在目标文件中找不到。因此,Git将此标记为“我无法在此更改的前后环境中进行匹配,因此我会将冲突标记放在可能发生更改的位置,但要将其留给人工智能进行最终解决。”

虽然在这种情况下你碰巧知道这本来可以轻易解决,但是有许多类似的情况,编码人员非常高兴git标记了这样的情况并让他们手动修复它。

如果这是您经常遇到的问题,git允许您为文件编写自定义合并处理程序,因此如果您了解有关文件的预期和允许的合并模式和语法的更多信息,则可以将其编码到合并处理程序中。

答案 2 :(得分:1)

如果您说merge,则会合并所有三(3-5)次提交。如果cherry-pick仅使用最后一个。它所涉及的文件可能已经在3和/或4中发生了变化,因此初始阶段与预期的不匹配。

检查冲突以查看合并工具发生的原因。

例如,这会导致冲突:

Original
Original
Original
Commit3
Original
Commit5
Original

这里git的差异引擎会显示你改变了一行前面有几行,包括来自提交3的一行.Cherry-pick没有看到原来的那一行所以git会尖叫冲突并要求你处理它。本身没有冲突的编辑(例如编辑了相同的行),git只是不确定将编辑的部分放在何处。

当然,您可以根据需要手动编辑文件。只有合并分支才会有问题和冲突,而不是所有冲突都意味着&#34;你不能做到这一点&#34;,他们只是意味着&#34;我不知道该做什么,你处理它自己。&#34;

答案 3 :(得分:1)

让我们对你的案子做一个小实验。

mkdir tmp
cd tmp
git init
echo "root" >> hello
git add hello
git commit -m 'root commit'
git branch exp
#open the file hello and write 111 in the 2nd line
git add hello
git commit -m '111'
#open the file hello and modify 111 to 112 in the 2nd line
git add hello
git commit -m '222'
#now we have 3 commits in the master branch
git log --oneline
6b86a6d 112
663a36b 111
28b2e12 root commit
git checkout exp
#now we are in the exp branch, which has only 1 root commit.
git log --oneline
28b2e12 root commit
#now we try to cherry pick 6b86a6d 
git cherry-pick 6b86ad6
error: could not apply 6b86a6d... 112
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'
#a conflict occurs,why?

主分支中的第二次提交等于modify the 2nd line, from empty to 111 主分支中的第3次提交等于modify the 2nd line, from 111 to 112

我们可以看到第3个取决于第2个。如果没有111,我们就无法更新111到112.当我们选择没有第二个的第三个时,在exp分支的文件hello中找不到111,这导致了冲突。

如果我们首先选择了第二名,你好的第二行将从空更新到111.如果我们选择了第3行,你好的第二行将从111更新为112.

让我们继续进行实验。

#abor the cherry-pick first
git cherry-pick --abort
#go back to the master branch
git checkout master
> world
git add world
git commit -m 'add new file world'
git log --oneline
1cf8f90 add new file world
6b86a6d 112
663a36b 111
28b2e12 root commit
#now we have 4 commits in the master branch
#cherry pick the 4th commit into the exp branch
git checkout exp
git cherry-pick 1cf8f90
[exp 79a34bb] add new file world
 Date: Sun May 15 20:55:39 2016 +0800
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 world
#no error

第4次提交等于add a new file world。此更改不依赖于第2次或第3次提交。它可以毫无困难地被挑选到exp分支中。

让我们回到冲突场景。

#cherry pick the 3rd commit again
git cherry-pick 6b86a6d
error: could not apply 6b86a6d... 112
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 status
On branch exp
You are currently cherry-picking commit 6b86a6d.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

    both modified:   hello
#we can see conflict ocurrs in the file hello. open hello with text editor
hello world
<<<<<<< HEAD
=======
112
>>>>>>> 6b86a6d... 112

&LT;&LT;&LT;和&gt;&gt;&gt;部分是冲突背景。 &lt;&lt;&lt;&lt;&lt;和=== part是它现在在当前分支中的名称,也就是exp分支。这是空白的。 ===和&gt;&gt;&gt;部分是它在提交6b86a6d中的含义。它是112.樱桃挑选过程无法决定应用哪个部分,因此我们必须自己制作。如果我们坚持它应该是空白的,那么只需删除&lt;&lt;&lt;,&lt;,===,&gt;&gt;&gt;和112的行。如果我们想要112,那么只需删除&lt;&lt; ;&LT;,===,&GT;&GT;取代。当然,我们可以保留除&lt;&lt;,&lt;,===和&gt;&gt;&gt;之外的所有内容。某些情况下。总之,保持你想要的东西并删除不需要的东西。保存并退出。 git add hello;git cherry-pick --continue然后一切都完成了。有关详细信息,请参阅git merge --help中的“如何提出冲突”。

在另一种情况下,如果来自两个分支的两个提交更新相同的一行或多个具有不同内容的行,并且git cherry-pick一个提交到另一个分支,则也会导致冲突。