git里面的每一个commit都有一个parent commit,所有的commit构成一个commit树(tree)
- 对于multiple parent commit的场景
普通commit只有一个parent commit,而merge commit会有多个parent commit,取决于从多少个分支进行merge,如果从两个分支merge则有两个parent commit,如果从三个分支merge则有三个parent commit,。。。
举例来说:
- 主干分支master有A,B,C三个commit
- 拉出一个开发分支developer,并且在开发分支上产生D,E两个commit。
- 同时主干分支上又有了F,G,H三个commit。

此时我们要把开发分支developer合并回主干master分支:
- 产生一个新的merge commit: M
- M包含两个parent commit,即E,和H

很多人会有疑问,为什么要生成merge commit,它其实没有任何实效的代码变化,为什么不把分支developer和master拉直,比如:

或者

这样做是需要更改历史commit的,或者把commit D的parent commit的改掉,或者把commit F的parent commit改掉,这都是不被允许的,即修改历史信息。所以只能添加一个新的commit M,它包含两个parent commit分别指向E和H,那么以后的commit也只需要一个parent commit即M就可以了。
git cherry-pick -m的使用
还是从前面的例子继续来说。
假设之前还有一个分支feature从G拉出,并且已经生成提交G1,G2两次提交:

此时发现需要把分支developer的改动(D和E)合并进来,怎么办呢:
- 分支developer已经删除了。
- 不能从master分支merge,因为不要commit H。
可以把commit D和E一个一个cherry-pick进来;但是如果developer上的commit数目很多,就会很麻烦,那么能不能通过把commit M合并进来了,是可以的,但是必须指定-m <parent>选项。
查看master分支的提交历史:
$ git checkout master
$ git log --oneline
<hash M> Merge pull request # in <repo> from developer to master
<hash E> Comment of commit E
<hash D> Comment of commit D
<hash H> Comment of commit H
...
再查看merge commit M的信息:
$ git cat-file -p <hash M>
tree <hash tree>
parent <hash E>
parent <hash H>
author ...
committer ..
Merge pull request #4 in <repo> from <developer> to <master>
* commit '<hash E>':
...
很清楚的看到commit M有两个parent commit,即H和E,(是谁排先谁排后,还不清除呢,待研究)
此时使用使用git cherry-pick -m 1 <hash M>
就会把commit D和E的内容复制到分支feature上面来。(注意parent 的序号从1开始)
另外需要特别说明的是,用这种产出的commit会抹去之前的提交历史,我们看命令输出:
$ git checkout feature
$ git log --oneline
dc54985 Merge pull request #4 in <repo> from <developer> to <master>
...
$ git cat-file -p dc54985
tree <hash>
parent <hash G2>
author ...
committer ...
Merge pull request #4 in <repo> from <developer> to <master>
* commit 'Hash E':
...

也就是说cherry-pick之后生成了一个新的commit N ‘hash=dc54985’,这个新的commit包含之前D和E的改动,但是D和E的提交历史并没有带进分支feature,用git log并不能看到D和E的提交过程,而且新commit E并不是作为一个merge commit而存在,它只有parent commit G2,只是左右一个普通commit存在。
网友评论