git Gitcherry-pick导致合并冲突,而合并不会
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37237637/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Git cherry-pick causes merge conflict while merging does not
提问by Curious
I am trying to learn how to use git cherry pick, I read the manual pages that git
returns by doing git cherry-pick --help
but that did not seem to help. I will try and explain the problem below. I have two branches master
and other
.
我正在尝试学习如何使用 git cherry pick,我阅读了git
通过执行返回的手册页,git cherry-pick --help
但这似乎没有帮助。我将尝试解释下面的问题。我有两个分支master
和other
.
On branch master
The commit history is
在分支上master
提交历史是
0x2 Second commit from master branch
0x1 Initial commit
And the only file in the repository that I am tracking readme
has the following contents
我正在跟踪的存储库中唯一的文件readme
具有以下内容
Some text
On branch other
The commit history is
在分支上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
And the contents of the readme
file are
readme
文件的内容是
Some text.
Adding some text from other branch. Adding some more text.
Going to cherry-pick this commit.
The working directory is clean on both branches with no untracked changes. From this point on when I switch to the master branch and merge with git merge other
the merge happens gracefully with no merge conflicts. But when I try git cherry-pick 0x5
there is a merge conflict, I get the following output from git
两个分支上的工作目录都是干净的,没有未跟踪的更改。从这一点开始,当我切换到主分支并与合并合并时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'
And the readme
file has the following contents
该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
Why is there this merge conflict? I am trying to understand why it occurs. Shouldn't cherry-pick
ing be the same as trying to make all the edits made on the commit that is to be cherry-pick
ed yourself and then commiting that change onto the branch (master
in this case)?
为什么会出现这种合并冲突?我试图理解它为什么会发生。不cherry-pick
应该与尝试对自己要编辑的提交进行所有编辑cherry-pick
,然后将该更改提交到分支(master
在这种情况下)相同吗?
Also when exactly is there a merge conflict in git
? I seem to get them at weird times. Is this implementation dependent (for example dependent on the algorithm used to merge)?
此外,究竟何时存在合并冲突git
?我似乎在奇怪的时间得到它们。这个实现是否依赖(例如依赖于用于合并的算法)?
Thank you!
谢谢!
回答by VonC
I just tested that scenario, after reading:
阅读后,我刚刚测试了该场景:
- "Why does cherry-pick always result in a merge conflict?"
- "Demystifying Git: 3 Concepts to Do Everything with Git"
If the modification to the first line (Some text.
instead of Some text
) was done immediately after 0x2, as in 0x3, then git cherry-pick 0x3
works without conflict.
如果对第一行(Some text.
而不是Some text
)的修改是在 0x2 之后立即完成的,如在 0x3 中那样,则git cherry-pick 0x3
可以无冲突地工作。
But if that same change is done a few commits later, the git cherry-pick
won't generate a patch compatible with the content of 0x2.
但是,如果在几次提交之后完成相同的更改,git cherry-pick
则不会生成与 0x2 的内容兼容的补丁。
There is no notion of "common ancestor" here (as incorrectly stated here): only of "patch".
这里没有“共同祖先”的概念(正如这里错误地陈述的那样):只有“补丁”。
A git cherry-pick
, even if it uses merging strategies, is nota git merge
.
A git cherry-pick
,即使它使用合并策略,也不是git merge
。
- A
git merge
will look for a common ancestor (going back in the history of commits of 0x5, to find a common commit with 0x2: here 0x2 itself) - A
git cherry-pick
only creates a patch by doing a unified diff between two commits (by default, the commit you mention and its immediate parent 0x4)
- A
git merge
将寻找一个共同的祖先(回到 0x5 的提交历史,找到一个与 0x2 的共同提交:这里是 0x2 本身) - A
git cherry-pick
仅通过在两次提交之间进行统一差异来创建补丁(默认情况下,您提到的提交及其直接父级 0x4)
The point is: that patch won't be able to be applied on top of 0x2.
关键是:该补丁将无法应用于 0x2 之上。
Try if for yourself: a git cherry-pick 0x5
is like doing a git diff -p 0x4..0x5
.
You will see in that patch lines which are notpart of 0x2, and reflect changes coming from 0x3 or 0x4.
自己试试看: agit cherry-pick 0x5
就像做 a 一样git diff -p 0x4..0x5
。
您将在补丁线中看到不属于 0x2 的部分,并反映来自 0x3 或 0x4 的更改。
Such a patch cannot be applied on 0x2, as it is based on a content which does not exist for 0x2.
这种补丁不能应用于 0x2,因为它基于 0x2 不存在的内容。
I have created a test repo in which I made from master two modifications.
我创建了一个测试 repo,我在其中进行了两次修改。
- Adding some text from other branch (no modification to the first line)
- Then a second commit modifying the first line.
- 从其他分支添加一些文本(不修改第一行)
- 然后第二次提交修改第一行。
Here what my patch looks like:
这是我的补丁的样子:
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.
I cannot apply (cherry-pick) that commit onto 0x2, as 0x2 has no line "Adding some text from other branch
" yet. It only has one line "Some text"
.
我不能应用 (cherry-pick) 提交到 0x2,因为 0x2 还没有行“ Adding some text from other branch
”。它只有一行"Some text"
。
That is why git reports a conflict: the content has diverged, and that patch is supposed to change line 1 on top of a file including "Adding some text from other branch
", which 0x2 does not have.
这就是 git 报告冲突的原因:内容发生了分歧,该补丁应该更改文件顶部的第 1 行,包括“ Adding some text from other branch
”,而 0x2 没有。
This differs from a git merge
of course, as a git merge will look for a common ancestor (here 0x2) and reports all modifications since 0x2 onto 0x2 without any issue.
这与 agit merge
的当然不同,因为 git merge 将寻找一个共同的祖先(这里是 0x2)并将自 0x2 以来的所有修改报告到 0x2 没有任何问题。
回答by Mort
I'm going to be less technical, and more "common sense" than the other answers. (Which is not always the right thing to do, but I think it is in this case.)
与其他答案相比,我将不那么技术化,而更加“常识”。(这并不总是正确的做法,但我认为在这种情况下是正确的。)
You are thinking, "It's obvious, the change that I want simply appends a line to the end of the file. Why can git not just do that?"
您在想,“很明显,我想要的更改只是在文件末尾附加一行。为什么 git 不能这样做?”
Git is applying a patch. Applying a patch involves trying to figure out where the patch should go. This involves looking at the approximate line number and looking for nlines of identical context before and after the change being made. In this case, the after context is End Of File. But the before context "Adding some text from other branch. Adding some more text." cannot be found in the destination file. Git therefore flags this as "I can't match up some before and after context for this change, so I will put conflict markers in where the change should probably go, but leave it up to human intelligence to do the final resolution."
Git 正在应用补丁。应用补丁包括试图找出补丁应该去哪里。这涉及查看大致的行号,并在进行更改之前和之后查找具有相同上下文的n行。在这种情况下,after 上下文是文件结尾。但是之前的上下文“从其他分支添加一些文本。添加更多文本。” 在目标文件中找不到。因此,Git 将此标记为“我无法匹配此更改之前和之后的一些上下文,因此我会将冲突标记放在更改可能发生的位置,但让人类智能来完成最终解决方案。”
While in this case you happen to know that this could have been trivially resolved, there are many similar cases where coders have been very glad that git flagged a situation like this and let them manually fix it.
虽然在这种情况下,您碰巧知道这可以轻松解决,但在许多类似的情况下,编码人员很高兴 git 标记了这样的情况并让他们手动修复它。
If this is something you will run into often, git allows you to write custom merge handlers for files so if you know more about the expected and allowed merge patterns and syntax of your files, you can code that into a merge handler.
如果这是您经常遇到的事情,git 允许您为文件编写自定义合并处理程序,因此如果您对文件的预期和允许的合并模式和语法有更多了解,您可以将其编码到合并处理程序中。
回答by ElpieKay
Let's make a small experiment of your case.
让我们对你的情况做一个小实验。
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?
The 2nd commit in the master branch is equal to modify the 2nd line, from empty to 111
The 3rd commit in the master branch is equal to modify the 2nd line, from 111 to 112
master 分支中的第二次提交等于 master 分支中modify the 2nd line, from empty to 111
的第三次提交等于modify the 2nd line, from 111 to 112
We can see the 3rd depends on the 2nd. If there was no 111, we could not update 111 to 112. When we cherry-picked the 3rd without the 2nd, no 111 can be found in the file hello of the exp branch, which led to the conflict.
我们可以看到第三个依赖于第二个。如果没有111,我们就无法将111更新为112。当我们在没有第二个的情况下挑选第三个时,在exp分支的文件hello中找不到111,从而导致冲突。
If we cherry-picked the 2nd first, hello's 2nd line would be updated from empty to 111. And then if we cherry-picked the 3rd, hello's 2nd line would be updated from 111 to 112.
如果我们先挑选第 2 行,hello 的第 2 行将从空更新为 111。然后如果我们挑选第 3 行,则 hello 的第 2 行将从 111 更新为 112。
Let's go on with the experiment.
让我们继续实验。
#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
The 4th commit is equal to add a new file world
. This change does not depend on the 2nd or the 3rd commit. It can be cherry-picked into the exp branch without any trouble.
第 4 次提交等于add a new file world
. 此更改不依赖于第二次或第三次提交。它可以毫无困难地被挑选到 exp 分支中。
Let's go back to the conflict scenario.
让我们回到冲突场景。
#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
<<< and >>> part is the conflict context. The <<< and === part is what it is now in the current branch, aka the exp branch. It's blank. === and >>> part is what it is in the commit 6b86a6d. It's 112. The cherry-pick process cannot decide which part to apply, so we have to make it by ourselves. If we insist it should be blank, then just remove the lines of <<<,===,>>>, and 112. If we want 112 instead, then just remove the lines of <<<,===,>>>. Of course we can keep all except <<<,===, and >>> in some case. In a word, keep what you want and remove what is not needed. Save and exit. git add hello;git cherry-pick --continue
Then all is done. For more info please refer to 'HOW CONFLICTS ARE PRESENTED' in git merge --help
.
<<< 和 >>> 部分是冲突上下文。<<< 和 === 部分是它现在在当前分支中的内容,也就是 exp 分支。它是空白的。=== 和 >>> 部分是提交 6b86a6d 中的内容。是112,挑选的过程不能决定申请哪个部分,所以我们必须自己做。如果我们坚持它应该是空白的,那么只需删除 <<<,===,>>> 和 112 的行。如果我们想要 112,那么只需删除 <<<,===,> 的行>>。当然,在某些情况下,我们可以保留除 <<<、=== 和 >>> 之外的所有内容。总之一句话,保留你想要的,去掉不需要的。保存并退出。git add hello;git cherry-pick --continue
然后一切都完成了。有关更多信息,请参阅 中的“如何呈现冲突” git merge --help
。
In another case, if two commits from two branches update the same line or lines with different contents and git cherry-pick one commit to the other branch, it leads to conflict too.
在另一种情况下,如果来自两个分支的两次提交更新了同一行或具有不同内容的行,并且 git cherry-pick 一个提交到另一个分支,也会导致冲突。
回答by Sami Kuhmonen
If you say merge
, all three (3-5) commits are merged. If you cherry-pick
only the last one is used. The file(s) it concerns may have changes already in 3 and/or 4 so the initial stage doesn't match what is expected.
如果您说merge
,则所有三个 (3-5) 提交都将合并。如果你cherry-pick
只使用最后一个。它涉及的文件可能已经在 3 和/或 4 中发生了变化,因此初始阶段与预期不符。
Do check the conflict to see why it happens with a merge tool.
请检查冲突以了解为什么使用合并工具会发生冲突。
For example this will cause a conflict:
例如,这将导致冲突:
Original
Original
Original
Commit3
Original
Commit5
Original
Here git's diff engine will show that you changed a line that is preceded by a few lines, including a line from commit 3. Cherry-pick doesn't see that line in the original so git will scream conflict and ask you to handle it. There is no conflicting edit per se (for example the same line was edited), git just isn't sure where to put the edited part.
这里 git 的 diff 引擎将显示您更改了前面几行的一行,包括来自 commit 3 的一行。 Cherry-pick 没有看到原始行中的那一行,因此 git 会尖叫冲突并要求您处理它。编辑本身没有冲突(例如编辑了同一行),git 只是不确定将编辑的部分放在哪里。
Of course you can manually edit the files however you want. Only merging branches will have issues and conflicts and not all conflicts mean "you can't do this", they just mean "I don't know exactly what to do, you handle it yourself."
当然,您可以根据需要手动编辑文件。只有合并分支才会有问题和冲突,并不是所有的冲突都意味着“你不能这样做”,它们只是意味着“我不知道该怎么做,你自己处理”。