Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between git diff and git cherry

Tags:

git

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.

like image 805
BestPractices Avatar asked May 04 '16 18:05

BestPractices


People also ask

What is the difference between a git merge and git cherry-pick?

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.

What is git cherry?

git cherry-pick is a powerful command that enables arbitrary Git commits to be picked by reference and appended to the current working HEAD. Cherry picking is the act of picking a commit from a branch and applying it to another. git cherry-pick can be useful for undoing changes.

What is git diff used for?

Diffing is a function that takes two input data sets and outputs the changes between them. git diff is a multi-use Git command that when executed runs a diff function on Git data sources. These data sources can be commits, branches, files and more.

Does git cherry-pick change hash?

When you are performing a regular cherry-pick, you will get a new commit hash but the commit message will be the same. However, there is a way to append the origin of a cherry-pick to the commit message : by using the cherry-pick with the “-x” option.


3 Answers

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

like image 182
Vishal Avatar answered Sep 30 '22 09:09

Vishal


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.

like image 45
Joseph K. Strauss Avatar answered Sep 30 '22 08:09

Joseph K. Strauss


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.

like image 36
torek Avatar answered Sep 30 '22 07:09

torek