Why does this cherry-pick have a conflict?

L_K picture L_K · Aug 10, 2016 · Viewed 7.3k times · Source

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?

Answer

VonC picture VonC · Aug 17, 2016

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)