Why does this cherry-pick have a conflict?

2019-01-14 12:29发布

问题:

I know git cherry-pick is a command that use to apply the changes of specified commit, but I think I just don't really understand the way it works.

Let's say a repo act like that:

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

I thought cherry-pick command would work well and change file a into:

a

c

but in fact I got the following message:

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'

And then I run:

git diff

output:

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

So my question is: Why is there a conflict like git-diff shows? What are the details of cherry-pick working in this case?

回答1:

Try again your cherry-pick after:

git config merge.conflictstyle diff3

You will get a more detailed diff:

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

It shows that, when applying the patch represented by dev's HEAD (b and c), Git does not know of a common ancestor; it defers to:

  • the immediate parent of the cherry-picked commit (showing that it adds a line 'c'after a line 'b')
  • the destination commit (which shows no line b at all on top of which it could apply the added change 'c')

Hence conflict.

Cherry-picking is not like a merge (which looks for a merge-base).

Cherry-picking takes a commit and applies the change that it introduces.

Here the change introduced is: add c on top of b.
And the destination commit has no b at all, so for Git:

  • the upstream (destination) commit has "removed b" (or never had it in the first place, which is the case here, but Git does not know that),
  • the source commit has a b on top of which c is added.

As far as Git knows when trying to apply that patch (and that is all git cherry-pick does: apply patch. It does not look for the history of the cherry-picked commit at all), that is a conflict: concurrent modification.

If you are sure of the way that resolution should go, you can do:

> 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(+)

Then, you would see b and c added to the original commit, without any conflict (since you indicated how to resolve it with the option '-Xtheirs' passed to the default merge strategy recursive)



回答2:

Technically, since you are editing the same line of the same file on different branches, Git sees this as a conflict. Cherrypicking, while not technically a 'merge' operation, still looks for the same types of conflict and asks you to resolve them.

For conflicting paths, the index file records up to three versions, as described in the "TRUE MERGE" section of git-merge[1]. The working tree files will include a description of the conflict bracketed by the usual conflict markers <<<<<<< and >>>>>>>.

From git-scm documentation