Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mainline parent number when cherry picking merge commits

Suppose this is my git history

  Z
 /
A -- C -- D
 \  /      
  B

My HEAD is currently at Z. I want to cherry-pick B and C. If my understanding is correct, I should do this:

git cherry-pick B
git cherry-pick C -m 1
git commit --allow-empty

It worked in my case because C is a no-op (hence the empty commit afterwards, I needed the commit for other reasons), but I am wondering what the parameter after -m does. Here is what I read from the docs:

-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.

In my case, C has two parents but how do I know which one is 1 and 2, and more importantly when does it matter when I pick 1 or 2?

like image 239
rink.attendant.6 Avatar asked Oct 20 '16 07:10

rink.attendant.6


People also ask

What happens when you cherry pick a merge commit?

the git cherry-pick Command in Git git cherry-pick is an influential command that allows random Git commits to be moved from one branch in a repository and adds to the repository's current working HEAD . This command is also beneficial for undoing changes related to the past development done by the team.

How many parents does merge commits have?

Unlike other commits, the merge commit is a commit which has multiple (generally two) parents. For instance, when a branch named feature is merged with master, a new commit is created on the branch master which has two parents, the previous head of master and the head of feature.

What are parents of a merge commit?

This new commit records both as its parents; the branch/commit you're merging in and the branch you are on when the merge occurs. The parent that gets recorded first (in the commit object of the new commit) is considered the "first parent", while the other one is considered the "second parent".

How do you cherry pick a merge commit *?

With the cherry-pick command, Git lets you incorporate selected individual commits from any branch into your current Git HEAD branch. When performing a git merge or git rebase , all the commits from a branch are combined. The cherry-pick command allows you to select individual commits for integration.

How to cherry pick the mainline parent for a merge commit?

When attempting to cherry pick a merge commit, the command fails because there is no -m option specified (mainline parent number). Though it may not be possible to automatically determine the mainline parent for a merge commit, perhaps allowing the user to simply select the mainline from one of the parents of the merge commit may suffice.

How do I cherry-pick the parent number of a merge?

-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.

Should I cherry-pick a merge commit?

Don't cherry-pick the merge. 1 Use the -moption to do so. For example, git cherry-pick -m 1 fd9f578will use the first parent listed in the merge as the... 2 Also consider that when you cherry-pick a merge commit, it collapses allthe changes made in the parent you didn't... More ...

What is cherry-pick parent number in Git?

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. Usually the command automatically creates a sequence of commits.


2 Answers

My understanding based off this answer is that parent 1 is the branch being merged into, and parent 2 is the branch being merged from. So in your case, parent 1 is A, and parent 2 is B. Since a cherry-pick is really applying the diff between two commits, you use -m 1 to apply only the changes from B (because the diff between A and C contains the changes from B). In your case, it probably doesn't matter, since you have no commits between A and C.

So yes, -m 1 is what you want, and that is true even if there were extra commits between A and C.

If you want to make the new history look a little more like the original history, there's another way to do this:

git cherry-pick B
git checkout Z
git merge --no-ff --no-commit B
git commit --author="Some Dev <[email protected]>" --date="<commit C author date>"

(If needed, you can create a new branch for B before cherry-picking.)

This will retain the author information, should give you a history that looks like this:

    B'
   /  \
  Z -- C'
 /
A -- C -- D
 \  /      
  B
like image 164
Scott Weldon Avatar answered Oct 12 '22 08:10

Scott Weldon


I've upvoted Scott Weldon's answer, which is correct, but I just want to add an attempt at ASCII art that includes parent numbering. Given a graph that looks like this:

       B
      / \
...--A   D--...
      \ /
       C

we can tell that node D is a merge commit, but we cannot tell whether B or C is the first parent of D. One of the two is necessarily the first parent, and the other is the second. So if we need to know, we must label the drawing, which takes more room. Here is one such attempt.

         B
       /   \²
...--A       D--...
       \   /¹
         C

We now see that, for some reason,1 I have drawn the graph "upside down": that commit C is in fact the first parent of D, while commit B is the second parent.

It's possible to create arbitrary merges using lower level ("plumbing") commands. In particular, git commit-tree just takes however many -p arguments you wish to give it, in the order you give them, and makes a new commit with the given commits as its parents. Give it one -p and it makes an ordinary commit with one parent. Give it no -p arguments and it makes a root commit. Give it 155 distinct -p arguments (all must of course resolve to valid commit IDs) and it makes one massive octopus merge commit.

The git merge command, however, always makes its new commit with the first parent being the current HEAD (hence the current branch, if on a branch). The second parent, for a standard two-parent merge, comes from .git/MERGE_HEAD, into which git merge writes the other commit ID. If the merge is conflicted, or the final merge commit is delayed with --no-commit, this MERGE_HEAD file is in fact the only place that commit ID is available.

(When git merge makes an octopus merge, using the -s octopus strategy—this strategy is forced on for such merges—and multiple additional parents, it aborts and leaves no trace at all if there are merge conflicts, so the conflict case never occurs. I have not tried combining --no-commit with an octopus merge, but that would, logically, leave the 2nd through N'th parents in MERGE_HEAD, if Git allows this at all.)


1Obstinacy.

like image 24
torek Avatar answered Oct 12 '22 08:10

torek