Git cherry-pick syntax and merge branches
Asked Answered
M

5

97

So I have done countless cherry picks before and it seems that I must fail at life with this right now, I am trying to cherry pick from one branch to another which should be easy, how ever I get an error about it being a merge but not -m was given?

$ git cherry-pick a8c5ad438f6173dc34f6ec45bddcef2ab23285e0
error: Commit a8c5ad438f6173dc34f6ec45bddcef2ab23285e0 is a merge but no -m option was given.
fatal: cherry-pick failed

That looks wrong.......it should be:

$ git cherry-pick a8c5ad438f6173dc34f6ec45bddcef2ab23285e0

Since when do I have to supply a -m function?

Munford answered 27/9, 2012 at 17:16 Comment(1)
"it seems that I must fail at life with this right now". Genius. I think this is how git is designed to make you feel. At least I feel your pain and you can be safe in the knowledge that a decade since your question has made not much difference.Benedikt
F
108

You have to supply -m if the commit is a merge commit, i.e. a commit with more than one parent.

Normally, what git cherry-pick REV does can be described as:

  1. Take the changes between rev and its parent.

  2. Apply these changes to the current HEAD and commit the result with rev's commit message.

A merge commit joins two lines of development. For example, one line implements widget, and the other line removes clutter. The merge gives you the code with the widget, sans the clutter.

Now consider step #1 of the cherry-pick process: git can't guess whether you want to remove the clutter or to implement the widget. Nor can you do both, because the information on how to do both is not contained inside a single merge commit, only the content of the resultant merged tree is.

The -m option allows you to tell git how to proceed. For example, if clutter removal happened on master and the merge commit was created using git merge WIDGET, then git cherry-pick -m 1 merged-commit will cherry-pick the new widget because diff between the merged tree and parent 1 (the last of clutter-removing commits) will have been exactly the widget addition. On the other hand, git cherry-pick -m 2 merge-commit will delete the clutter, because the difference between parent 2 (the last of the widget-adding commits) and merge-commit is exactly the clutter-removal missing from the widget branch.

Foliole answered 27/9, 2012 at 19:17 Comment(6)
To verify git cherry-pick did what you meant it to do, run git show before pushing; this'll show you the diff of what is about to be pushed.Thiamine
For what it's worth, reverting a merge commit has exactly the same considerations and command-line argument required to be successful.Auroraauroral
what if I want both widget-addition AND clutter-deletion to be in? that is the point of cherry-picking the merge commit right? other wise, I'd cherry-pick the last commit of the widget-addition or the last commit of the clutter-deletion insteadHyrcania
@Hyrcania If you want both, then I guess you need a merge, not a cherry-pick. The whole point of cherry-picking is not to recreate the tree after a commit (which would include both sides of a change), but to recreate the change. What the change of a merge commit is depends on which side of the merge you are looking from.Foliole
God bless you user4815162342Maine
A small code formatted diagram of which "side" of the merge you'd be picking by using -m 1 vs -m 2 would be good. I just had to try both to see which one gave me the correct side, and I don't know if it's relative to your currently checked out branch or if there're other gotchas here.Codeine
P
58

The git is requesting you to specify parent number (-m), because your merge commit has two parents and git do not know which side of the merge should be considered the mainline. So using this option you can specify the parent number (starting from 1) of the mainline and cherry-pick in order to replay the change relative to the specified parent.

To find out your commit parents, try either:

git show --pretty=raw <merge_commit>

or:

git cat-file -p <merge_commit>

or even for better GUI visibility, try:

gitk <merge_commit>

As result, you should get something like:

commit fc70b1e9f940a6b511cbf86fe20293b181fb7821
tree 8d2ed6b21f074725db4f90e6aca1ebda6bc5d050 
parent 54d59bedb9228fbbb9d645b977173009647a08a9 = <parent1_commit>
parent 80f1016b327cd8482a3855ade89a41ffab64a792 = <parent2_commit>

Then check your each parent details by:

git show <parent1_or_2_commit>

Add --stat to see list of modified files.

Or use the following command to compare the changes (based on the above parent):

git diff <parent1_or_2_commit>..<commit>

Add --stat to see list of modified files.

or use the combined diff to compare the two parents by:

git diff --cc <parent1_commit>
git diff --cc <parent2_commit>

Then specify the parent number starting from 1 for your cherry-pick, e.g.

git cherry-pick -m 1 <merge_commit>

Then run git status to see what's going on. If you don't want to commit the changes yet, add -n option to see what happens. Then when you're not happy, reset to HEAD (git reset HEAD --hard). If you'll get git conflicts, you'll probably have to solve them manually or specify merge strategy (-X), see: How to resolve merge conflicts in Git?

Perrone answered 30/7, 2016 at 1:23 Comment(0)
O
5

Personally what i normally do is that since a merge combines 2 commits, for instance if i have merge commit C which is composed of 2 parents e.g commit A in master and commit B from the other branch getting merged, if i need to cherry pick the merge i wouldn't bother with the confusing command to cherry pick the merge commit itself but instead i would just cherry each of the parents A and B individually, this is also helpful in a situation where you only want to cherry pick commit B only in case commit A from master was already cherry-picked to the branch one is cherry picking to before the merge happened.

Odontograph answered 20/1, 2016 at 16:40 Comment(0)
R
2

The syntax from the man pages is as follows:

git cherry-pick [--edit] [-n] [-m parent-number] [-s] [-x] [--ff] <commit>...

The parent-number refers to:

-m parent-number, --mainline parent-number, Usually you cannot cherry-pick a merge because you do not know which side of the merge should be considered the mainline. This option specifies the parent number (starting from 1) of the mainline and allows cherry-pick to replay the change relative to the specified parent.

So I would double check to make sure you have the correct commit hash. It might be that you want one that isn't from a merge but rather the commit before it. Otherwise, you need to use this flag and point to the correct side of the merge to disambiguate your request.

Rica answered 27/9, 2012 at 17:45 Comment(0)
V
-4

parent1 commit change widget

parent2 commit change clutter

merge commit (contains both changes) widget clutter

git cherry-pick -m 1 merge-commit diff of merge and parent1(master) merge - master= widget

git cherry-pick -m 2 merge-commit diff of merge and parent2(WIDGET) merge - WIDGET= clutter

Viburnum answered 25/11, 2020 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.