22

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?

L_K
  • 2,838
  • 2
  • 16
  • 36
  • Git cherry pick is a way to choose and apply individual commits from another branch onto the current branch. It is essentially doing a merge of one commit, hence you can get conflicts. There is not necessarily anything wrong with what you are seeing. – Tim Biegeleisen Aug 10 '16 at 05:42
  • @TimBiegeleisen But if I run `git merge dev`, there is no conflict and shows that I merged successfully... – L_K Aug 10 '16 at 06:56
  • `git cherry-pick dev` does not even make sense to me, because you are specifying a branch name rather than a commit. – Tim Biegeleisen Aug 10 '16 at 06:57
  • 3
    @TimBiegeleisen `dev` refers to the last commit of branch `dev`, so I think there is nothing wrong with `git cherry-pick dev`. – L_K Aug 10 '16 at 06:59
  • Thanks for pointing this out, I suspected this but have never used this myself, I just learned something :-) It's possible that the conflicts were resolved automatically during the merge. – Tim Biegeleisen Aug 10 '16 at 07:00
  • 1
    Think of doing a merge of many commits like making small changes, each in succession. In this case, there may be no conflicts because Git can handle each small change. When you cherry pick the latest commit, Git may be seeing something dramatic which it cannot resolve without manual intervention. This is one explanation. – Tim Biegeleisen Aug 10 '16 at 07:02
  • @TimBiegeleisen :-) – L_K Aug 10 '16 at 07:02
  • @TimBiegeleisen Thanks, but I still got confused... – L_K Aug 10 '16 at 07:08
  • I suspect this is because git is looking for the line with a `b` in order to know where to put the `c`. It seems like it should be able to recognize that it's on the last line, but apparently not. Good question. – Jeff Puckett Aug 10 '16 at 14:44
  • I think your suspection makes some points, maybe there is no common solution for merging in git, sometimes we must handle it by ourselves. – L_K Aug 10 '16 at 15:26

2 Answers2

30

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)

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Coincidentally, the conflict error message is being rewritten and discussed as we speak: http://marc.info/?t=146956231900001&r=1&w=2 – VonC Aug 17 '16 at 06:54
  • *"the immediate parent of the cherry-picked commit (showing that it adds a line 'c'after a line 'b')"* is a suspicion I had for the way it worked, but I also thought git would be clever enough to recognize it was on the last line of the file so that it wouldn't conflict. Does git always defer to the **line above** for placement? – Jeff Puckett Aug 17 '16 at 12:05
  • @JeffPuckettII it is not so much "the line above" as it is "the patch made from between the cherry-picked commit and its immediate parent". Here the delta between the last `dev` commit and its parent is '`c`' on top of '`b`'. The fact that it is applied at the last line does not matter here. The fact that Git cannot find '`b`' anywhere near the lines concerned (when applying the patch on `HEAD`) does matter. – VonC Aug 17 '16 at 12:12
  • I understand that to apply changes git will compute a patch that include not only lines changed but also some context lines surrounding it? – L_K Aug 18 '16 at 09:50
  • @huachengzan exactly, and the context of that patch is the line b. Line which is missing in the destination. – VonC Aug 18 '16 at 09:53
  • Thank you and @JeffPuckettII, for resolving my puzzle :-) – L_K Aug 18 '16 at 10:02
  • 2
    I mentioned this on my other reply-to-your-reply, but: `git cherry-pick` *does* actually use a merge base and do a three-way merge. The issue here is that in many (but not all) cases, the merge base itself is quite bogus and is therefore not helpful, or possibly even harmful. This means that Git tries a simple "straight patch" approach *first*. If that fails, it falls back to using the cherry-picked commit's parent commit as a merge base. Occasionally this allows the patch to apply correctly, especially if that "base" really is in the current history. – torek Aug 24 '16 at 20:25
  • @tor I agree on the fallback part: this is consistent with what I have seen in that instance. – VonC Aug 24 '16 at 20:27
  • @VonC when I add b to top of a, then I add c to bottom of a. I can cherry-pick c. I don't understand the scene. – study_20160808 Jan 21 '21 at 13:13
  • @study_20160808 `a`, `b` and `c` are just text lines: you can reorder them as you want during the cherry-pick resolution. The `merge.conflictstyle diff3` setting just helps understanding from *where* those lines are coming from (source, destination or common ancestor) – VonC Jan 21 '21 at 13:16
  • @VonC I can get more detail about cherry-pick conflict by merge.conflictstyle diff3. I still don't understand how to check conflict about different position for cherry-pick. As first commit, I add b under a. As second commit, I add c above a. I cherry-pick second commit to target branch and no conflict. But Why your scene have conflict. I'm confused. – study_20160808 Jan 22 '21 at 04:00
  • @study_20160808 here a, b and c are examples about multiple lines which are overlapping. In your case, please ask a separate question showing how to reproduce your issue, and the community will be able to make a better answer. – VonC Jan 22 '21 at 08:59
  • @VonC https://stackoverflow.com/questions/65842371/add-line-to-differnent-position-cause-conflict-by-cherry-pick – study_20160808 Jan 22 '21 at 09:19
-1

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

Briana Swift
  • 1,047
  • 8
  • 9
  • 2
    Hm...I don't think I have editing the same line of the same file, just add some lines at the end of the file. – L_K Aug 11 '16 at 01:37