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!

tamlok
  • 697
  • 1
  • 5
  • 15
  • This may help you http://stackoverflow.com/questions/9339429/what-does-cherry-picking-a-commit-with-git-mean – Arun G Mar 03 '17 at 06:03
  • 4
    @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. – tamlok Mar 03 '17 at 06:14
  • @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. – miqh Mar 03 '17 at 06:25
  • 1
    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. – MikeMB Mar 03 '17 at 06:28
  • @MikeMB Please see the updates. Thanks! – tamlok Mar 03 '17 at 07:03

2 Answers2

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
aragaer
  • 17,238
  • 6
  • 47
  • 49
  • 1
    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! – tamlok Mar 03 '17 at 07:25
  • I just tried `git cherry-pick master`. The result is the same, the conflict content still contains the changes in commit `b`. :( – tamlok Mar 03 '17 at 07:31
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
Sajib Khan
  • 22,878
  • 9
  • 63
  • 73
  • 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! – tamlok Mar 03 '17 at 07:04
  • 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. – angularsen May 23 '17 at 11:09