git cherry-pick does not just pick the diff of the commit
Asked Answered
D

2

20

I have two branches: A and B.

  • A's commit history: a <- b <- c;
  • B's commit history: a <- h <- i;

Assume that there is only one file here.

  1. In commit b, I adds some texts like "foo".
  2. In commit c, I adds some texts like "bar".
  3. Then I git cherry-pick c on B branch. I thought cherry-pick will only pick the changes in c to branch B. However, it will add both foo and bar to branch B. Which is obviously not what I want.

Therefore, cherry-pick will pick all the changes of those files touched in commit c since the ancestor commit a. Is that right? What if I only want to pick the diff from b to c and apply it onto i?

Update the exact steps

  1. Init a git repo;
  2. Add file test.txt and issue the first commit init commit. test.txt is now:

    first line  
    second line
    
  3. Create a new branch dev but stay in branch master;

  4. Add added in commit b to the file and issue the commit b. test.txt is now:

    first line
    added in commit b
    second line
    
  5. Add added in commit c to the file and issue the commit c. test.txt is now:

    first line
    added in commit b
    added in commit c
    second line
    
  6. Check out dev branch and issue the commit h. test.txt is now:

    first line
    second line
    
    adding by commit h
    
  7. git cherry-pick <commit c SHA1 ID> to cherry-pick commit c onto commit h.

  8. The conflict message:

    index 6a8dc57,594c6ec..0000000
    @@@ -1,4 -1,4 +1,9 @@@
      first line
    ++<<<<<<< HEAD
    ++=======
    + added in commit b
    + added in commit c
    ++>>>>>>> 06ce9b1... commit c adding another line
      second line
     +
     +adding by commit h
    
  9. See? cherry-pick also brings the changed in commit b.

Thanks!

Dovecote answered 3/3, 2017 at 5:57 Comment(5)
This may help you #9339929Alcuin
@ArunG Thanks! I know what cherry-pick means. But I am just confused that it is not equal to getting the diff of that commit and applying the diff to the target branch. It also contains contents NOT in that commit.Dovecote
@tamlok, run git show a (replacing a with the relevant commit SHA obviously) to inspect the changes that commit will actually introduce to see why you might be getting deltas from commit b. You can always run cherry-pick with the -n flag to stop and manually trim away unwanted changes as well.Kyanize
Please show the exact sequence of commands (including what you are using to display the cherry-picked commit). I think you misinterpret what you are seeing.Iraidairan
@Iraidairan Please see the updates. Thanks!Dovecote
F
5

git cherry-pick tries to bring just one commit. But it does this by applying a patch which requires some context. The change done in commit C is very close to the change done by commit b, so you get the conflict - it can't just find the correct place where change has to be applied. And when you have conflict, you also get some of the conflicting context which is at least a part of your commit B.

Here's how it would work without the conflict:

$ git init
$ cat > f
line1
line2
line3
$ git add f
$ git commit -a -m "initial"
# Edited to add a line in the beginning of f
$ cat f
Commit b
line1
line2
line3
$ git commit f -m "B"
# Edited to add a line in the end of f
$ cat f
Commit b
line1
line2
line3
Commit c
$ git commit f -m "C"
$ git checkout HEAD^^
$ git cherry-pick master
$ cat f
line1
line2
line3
Commit c
Framework answered 3/3, 2017 at 7:12 Comment(2)
So git cherry-pick master will just pick commit c instead of both b and c? That is when using a branch name with git cherry-pick, it will only pick the last commit? Thanks!Dovecote
I just tried git cherry-pick master. The result is the same, the conflict content still contains the changes in commit b. :(Dovecote
C
1

What if I only want to pick the diff from b to c and apply it onto i?

You can find/write the diff of a file between two commits (c..d). Then apply that in your current branch.

$ git checkout <B-branch>

# write the diff in a file named 'change.patch' (root directory) 
$ git diff <b-commit> <c-commit> <file-name> >> ~/changes.patch

$ git apply ~/changes.patch       # apply the changes
$ git add .

# merge the changes to i (previous commit)
$ git commit --amend -m 'Apply the diff of b and c'

$ git push -f origin HEAD      # force(-f) push since history is changed
Columella answered 3/3, 2017 at 6:56 Comment(2)
I know this "original" way. But according to git cherry-pick's description, why will it also pick extra changes from other not-specified commits? Thanks!Dovecote
In my case, the patch does not cleanly apply, so I have to use the --3way option for a three-way merge. Then I get the exact same result as with using git cherry-pick, except there the error is muted and it seems to fall back to three way with no heads up. I still don't see why the three way merge end up with that result, but my two branches have diverged a lot so that may be relevant.Espinoza

© 2022 - 2024 — McMap. All rights reserved.