BestPractices
BestPractices

Reputation: 12876

Difference between git diff and git cherry

I have two branches: branchA, branchB

The following returns no output (including no differences, at least for the git diff command):

git cherry branchA branchB
git diff branchA branchB
git diff branchB branchA

When I run this however:

git cherry branchB branchA

I get a list of commit ids all with a plus sign in front of them:

e.g.

+ c5f84105c242939a9d18fb9d6355534a80735277
+ 41acd0a40bfeaf3d68185a540c131838651cd889
+ 4859fd89c5dafeed6a68f0881ea6ad081a53fd68
+ 7226c9e5acf5a9d2d33b6aef3e5abf9b040f0b76
+ 4fc3206508d6ce7a19477e4c006608c78bb28801
+ 8816c66ed72da762b9b34858eec5b52a16d0ea99
+ 692d271ab07d4b92e54e72bcda09ee067654acee

Can someone please explain what this implies? Looking to understand why git diff shows no differences but git cherry shows differences.

Upvotes: 5

Views: 5106

Answers (4)

Guildenstern
Guildenstern

Reputation: 3791

git-diff(1) compares the contents of two branches. git-cherry(1) reports commits that are not in some other branches and (out of those) which of them have a patch-id equivalent [1] in the other branch.

You could create a repository where two branches have the same tree (the empty tree for example) but not the same commits since one of the other branches did a commit and then an immediate revert:

$ d=$(mktemp -d)
$ cd $d
$ # branch is `main`
$ git init
$ git commit --allow-empty -minit
$ git checkout -b other
$ echo 'start new project' >README.md
$ git add README.md
$ git commit -mReadme
$ git revert --no-edit HEAD
$ git diff main other
[empty output]
$ git cherry main other
+ 028db15895414e94f230618cb00a52036b185d50
+ f0e5528c570e7db4cb6012c56e2c9e4969d2e9c1

Notes

  1. “Patch-id” is the terminology used for what git-cherry(1) (and git-patch-id(1)) uses to compare commits: do these commits introduce the same change?

Upvotes: 0

torek
torek

Reputation: 488203

I'm not at all sure where your confusion comes from, especially since git diff and git cherry do very different things.

It would probably help (as is almost always true in Git) to draw the commit graph, or at least the relevant part, e.g., run git log --graph --oneline --all --decorate, or snip some window image from gitk, or (this is just the documentation's suggestion from below, modified to match your branch names, but it would show just what we need):

git log --oneline --graph --boundary branchA...branchB

(In some cases leaving out --boundary can make this significantly clearer, when there are very many commits that lie on the boundary of the symmetric difference, but for most simple cases it just adds the merge base commit and thus aids in reading the result.)

Now, let's also take a look at the git cherry documentation, which has a nice example of how we use the three-dot symmetric difference notation to understand the output of git cherry:

In a situation where [branch] topic consisted of three commits, and the maintainer applied two of them, the situation might look like:

$ git log --graph --oneline --decorate --boundary origin/master...topic
* 7654321 (origin/master) upstream tip commit
[... snip some other commits ...]
* cccc111 cherry-pick of C
* aaaa111 cherry-pick of A
[... snip a lot more that has happened ...]
| * cccc000 (topic) commit C
| * bbbb000 commit B
| * aaaa000 commit A
|/
o 1234567 branch point

Here, commit 1234567 is the one that --boundary included. Normally the three-dot notation would have selected only the origin/master (left side) commits like 7654321, cccc1111, and aaaa111 for the left half of the ..., and the master (right side) commits like cccc000, bbbb000, and aaaa000 commits for the right half. Adding --boundary adds the shared commit that, in effect, separates these left and right halves.

Look at what our maintainer guy upstream did: he accepted our commit A and our commit C, copying them change-for-change into his master which we see in our origin/master. He did not accept our commit B, or at least, not change-for-change (we can't tell from this, nor from git cherry, if he has taken some alternate version of our change).

Now look at the sample output from running git cherry:

In such cases, git-cherry shows a concise summary of what has yet to be applied:

$ git cherry origin/master topic
- cccc000... commit C
+ bbbb000... commit B
- aaaa000... commit A

This shows us all three commits on the right side (topic), while not showing us any of the commits on the left side (origin/master), of our log [flags] origin/master...topic listing.

For the commits it does show—those exclusively on the right—it marks each one with - (taken) or + (not taken).


We don't have a graph fragment, though, so let's work with what we do have at the moment.

git cherry branchA branchB

[is empty, but]

git cherry branchB branchA

(includes at least)

+ c5f84105c242939a9d18fb9d6355534a80735277
+ 41acd0a40bfeaf3d68185a540c131838651cd889
+ 4859fd89c5dafeed6a68f0881ea6ad081a53fd68
+ 7226c9e5acf5a9d2d33b6aef3e5abf9b040f0b76
+ 4fc3206508d6ce7a19477e4c006608c78bb28801
+ 8816c66ed72da762b9b34858eec5b52a16d0ea99
+ 692d271ab07d4b92e54e72bcda09ee067654acee

This mean that there are no commits on branchB that are not also on branchA, while there are many (at least the 7 listed above) commits on branchA that not on branchB. In fact, all of those 7 listed are all not-copied as well—not surprising, none of the exclusive-to-right-side branchA commits could possibly have been copied, if there are no exlusive-to-left-side (branchB) commits at all.

This means we can draw an approximation of the graph we would have gotten, if we had asked for one (I may have the wrong abbreviated hashes here):

* 692d271 (branchB) commit 7
* ....... commit 6
* ....... commit 5
* ....... commit 4
* ....... commit 3
* ....... commit 2
* c5f8410 commit 1
* xxxxxxx (branchA) branch point

This is a way—probably the only way—to get the output you saw.

Meanwhile:

git diff branchA branchB
git diff branchB branchA

(both produce no output at all.)

This just means that the source tree attached to branchA matches the source tree attached to branchB (after, perhaps, any filtering you selected when doing the diff). There are a bunch of ways to achieve that and it is hard to say which one(s) may have been used, but here is an example of what would do that:

$ git branch remember-where-we-parked-kids
$ echo new file > new-file && git add new-file
$ echo add another line >> existing-file && git add existing-file
$ git commit -m 'commit a change'
[master 643b37e] commit a change
 2 files changed, 2 insertions(+)
 create mode 100644 new-file
$ git revert --no-edit HEAD
[master 0af7c6a] Revert "commit a change"
 2 files changed, 2 deletions(-)
 delete mode 100644 new-file

The first commit contains a change that adds a file and modifies a file. The second (revert) commit contains a change that removes the added file new-file and puts the old file existing-file back the way it was just a moment ago, removing the added line. Comparing the current commit to the commit made two steps ago will show no differences, since the sum total of the two commits was to do nothing after all.

Using git cherry on branch remember-where-we-parked-kids, we would see that neither of the two added commits (the modification and its revert) are in the "other" branch (which is just two commits behind from where we are now):

$ git cherry remember-where-we-parked-kids master
+ 643b37ef242fdc35dfdd4551b42393af3eb91a85
+ 0af7c6a3cf5e49928de132c341c848be80ab84c7

(these are our two commits, the revert, 0af7c6a, and the initial change, 643b37e). Reversing the arguments to git-cherry, we get nothing, and of course the git diff is equally empty:

$ git cherry master remember-where-we-parked-kids 
$ git diff master remember-where-we-parked-kids
$

It's not possible to say how you got where you are now without more information, but this is what the output you have seen means.

Upvotes: 3

Vishal
Vishal

Reputation: 589

Git diff shows no output as the files in both branches are identical in content. However the commit ID (SHA string) might be different.

git cherry branchA branxhB is giving no output as all the commits (SHA and not file contents) in branch B is present in branch A. However vice versa is not true. There are some commits in branch A that are not in branch B and hence the output of git cherry branchB branchA. This is very well explained with example here. http://jafrog.com/2012/03/22/git-cherry.html

Upvotes: 4

Joseph K. Strauss
Joseph K. Strauss

Reputation: 4903

It means that the content of branchA and branchB are the same, but for some reason there are commits on branchB that are not on branchA.

These commits are being displayed. However, the sum total of these commits results in the same content, so you will not see any diff. You will not see anything with git cherry branchA branchB either, because there are no commits on branchB that are not on branchA.

Upvotes: 2

Related Questions