简体   繁体   中英

git cherry / git log --cherry confused by merges

I've got two branches, with a commit cherry-picked from one branch to the other. Like so:

> git init
Initialized empty Git repository in foo/.git/
> touch foobar; git add foobar; git commit -m "initial commit"
[master (root-commit) d109ffc] initial commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foobar
> git branch somebranch
> echo a >> foobar; git add foobar; git commit -m "add a"
[master 1527212] add a
 1 file changed, 1 insertion(+)
> touch anotherfile; git add anotherfile; git commit -m "add blank anotherfile"
[master 84821fc] add blank anotherfile
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 anotherfile
> git checkout somebranch; git cherry-pick -x 1527212
Switched to branch 'somebranch'
[somebranch b59ad0a] add a
 Date: Sat May 13 15:41:16 2017 +0800
 1 file changed, 1 insertion(+)
> git tree somebranch master
* b59ad0a (somebranch) add a
| * 84821fc (HEAD -> master) add blank anotherfile
| * 1527212 add a
|/
* d109ffc initial commit

At this stage, git cherry and git log --cherry --oneline display what I'd expect:

> git cherry somebranch master
- 1527212b9047f882c7438d083505941c47ea9a5b
+ 84821fcb3d33d41b64dddcc56728dd3e22466e4a
> git log --cherry --oneline somebranch...master
+ 84821fc (HEAD -> master) add blank anotherfile
= 1527212 add a

However, if I merge the two branches back together, I lose this output - both git cherry and git log --cherry fail to recognise the cherry-picked commit as identical:

> git checkout master
Already on 'master'
> git merge somebranch
Merge made by the 'recursive' strategy.
> git tree
*   90810f6 (HEAD -> master) Merge branch 'somebranch'
|\
| * b59ad0a (somebranch) add a
* | 84821fc add blank anotherfile
* | 1527212 add a
|/
* d109ffc initial commit
> git cherry somebranch master
+ 1527212b9047f882c7438d083505941c47ea9a5b
+ 84821fcb3d33d41b64dddcc56728dd3e22466e4a
> git log --cherry --oneline somebranch...master
+ 84821fc add blank anotherfile
+ 1527212 add a

Is there a way I can get git to show that these commits are the same, even though they've been merged back together?

The problem comes about because Git no longer looks at one of the commits.

Remember that the three-dot syntax (implied in the case of git cherry AB , explicit in the case of git log --cherry A...B ) means to take a symmetric difference: commits reachable from either A or B , but not from both.

(I assume git tree here is an alias for git log --all --decorate --oneline --graph :)

 > git tree * 90810f6 (HEAD -> master) Merge branch 'somebranch' |\\ | * b59ad0a (somebranch) add a * | 84821fc add blank anotherfile * | 1527212 add a |/ * d109ffc initial commit 

The name master means 90810f6 , and the name somebranch means b59ad0a . The first thing we have to do is find commits that are reachable from these two commits, keeping in mind just how we reached them:

  • 90810f6 : reached from 90810f6 1
  • b59ad0a : reached from both b59ad0a and 90810f6 : discard
  • 84821fc : reached from 90810f6
  • 1527212 : reached from 84821fc
  • d109ffc : also reached from both sides: discard

We've tossed out the one interesting (cherry-equivalent) b59ad0a commit already, so we never see it.

As the documentation tells us, --cherry is:

A synonym for --right-only --cherry-mark --no-merges ...

so from our list of three not-yet-discarded commits, we toss out 90810f6 as well, as it is a merge. That leaves the two commits we do see.

Is there a way I can get git to show that these commits are the same, even though they've been merged back together?

You must start the inspection from just below the merge. Use any method (see the gitrevisions documentation for the many ways to spell this) to start the listing from the first parent of the merge commit that master identifies, eg:

git log --cherry --oneline somebranch...master~1

(which we know only because we looked at the graph and saw that master was the merge). Note that we can specify both of the two commits that fed into the merge using the ^1 and ^2 suffixes:

git log --cherry --oneline master^2...master^1

The notation master^1 and master~1 mean exactly the same thing, since each says to move back one step to the first parent. This is not the case for master^2 vs master~2 : the former means "second parent" while the latter means "first parent of first parent".


1 A commit always reaches itself, even if that sounds vaguely suggestive. :-)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM