Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

“git show” does not return same result as “git diff”

Tags:

git

I have a commit in a git repository where git show says only one file is modified while git diff shows two files as being modified.

I have created a simple sample repository that exhibits the same behaviour.

  • https://github.com/martingd/git-show-vs-diff

In the sample repo we have this simple history:

*   c248261      (HEAD, origin/master, master) Merged feature into master.
|\  
| * d23c497      (feature) Modified fileA and fileB in feature.
* | 06a7f5e      Modified fileA and fileC in master.
* |   9cd1a6e    Merged feature into master.
|\ \  
| |/  
| * aed2e5e      Modified fileA and fileB in feature.
* | c6e4fe7      Mofified fileC in master.
* |   19ed298    Merged feature to master.
|\ \  
| |/  
| * c0f2abc      Added fileB and modified fileA in feature.
* | 47c67cf      Added fileC in master.
|/  
* 56a9b73        Added fileA in master.

When I look at commit c248261 in e.g. SourceTree, I can see that fileA and fileB is modified. Using git diff gives the same result:

$ git diff --name-only c248261^..c248261
fileA
fileB
$

or using the shorthand notation:

$ git diff --name-only c248261^!
fileA
fileB
$

When I try to get the same information using git show, only one of the two files show up:

$ git show --name-only c248261
commit c2482616b6b6781d0580ec1008ef7d0ab5f73a70
Merge: 06a7f5e d23c497
Author: ...
Date:   Fri Aug 15 16:19:02 2014 +0200

    Merged feature into master.

fileA
$

Similarly, git diff-tree shows nothing:

$ git diff-tree c248261
$

Can anybody explain the difference, please?

I am using git version 2.0.4.

like image 653
mgd Avatar asked Aug 15 '14 11:08

mgd


2 Answers

What is git show?

The command git show is a generic command for displaying information about an object in git.

From the man page (git help show):

Shows one or more objects (blobs, trees, tags and commits).
...
For commits it shows the log message and textual diff.
It also presents the merge commit in a special format as produced by
  git diff-tree --cc
...

So, the output of git show is different because this is a merge commit rather than a plain commit.

The man page for git diff-tree --cc says:

--cc
    This flag changes the way a merge commit patch is displayed, in a 
    similar way to the -c option. It implies the -c and -p options and
    further compresses the patch output by omitting uninteresting hunks whose
    the contents in the parents have only two variants and the merge result
    picks one of them without modification. When all hunks are uninteresting,
    the commit itself and the commit log message is not shown, just like in
    any other "empty diff" case.

further

-c
    This flag changes the way a merge commit is displayed (which means
    it is useful only when the command is given one <tree-ish>, or
    --stdin). It shows the differences from each of the parents to the
    merge result simultaneously instead of showing pairwise diff
    between a parent and the result one at a time (which is what the -m
    option does). Furthermore, it lists only files which were modified
    from all parents.

Please take notice of that last line which I repeat here:

Furthermore, it lists only files which were modified from all parents.

So git show on a merge commit will only list the files that were modified when comparing the commit to all parents of the merge. For an ordinary merge (i.e. 2 parents) this is exactly the files that were merged. If, for example, you do this merge:

git checkout master
git merge feature

The files listed by git show are the files that were modified in both master and feature prior to the merge and were merged together in the merge commit.

For an octopus merge – i.e. more than two parents – the resulting file must differ from the file in all parents to be displayed by git show.

Please note, that other files can also show up when using git show on a merge commit. If, for example, you call git merge --no-commit and then before committing do any of the following, then these files will also be displayed by git show on the merge commit:

  • Add a new file.
  • Delete an existing file.
  • Modify any file – including any of the files already staged by git merge.

Conclusion

What git show is doing is displaying a so-called combined diff.

The result of running the command git show on a merge commit can be thought of as showing only the files which were merged and not the files that were simply taken unchanged from one of the two parents.

This is a bit surprising as many people expect to see which files were modified compared to the branch into which you just merged.

So what to do if you want to look at the diff between either of the parents?

Let's have a final look at the man page of git show:

COMBINED DIFF FORMAT
  Any diff-generating command can take the `-c` or --cc option to produce
  a combined diff when showing a merge. This is the default format when
  showing merges with git-diff(1) or git-show(1). Note also that you can
  give the `-m' option to any of these commands to force generation of
  diffs with individual parents of a merge.

So we can use the -m option to get both diffs like this:

git show -m commit

This will give you the diffs against all parents one by one.

To see the difference between the merge commit and the previous commit on the branch into which you merged (i.e. the first parent), use either of these commands:

git show --first-parent commit
git diff commit^..commit
git diff commit^!

The --first-parent option really belongs to git log but also works for git show. The notation commit^! is a shorthand for commit^..commit.

To see the difference between the merge commit and the branch from which you merged (e.g. to see what you left out from the other branch), use

git diff commit^2..commit

The notation commit^2 means the 2nd parent of commit.

If you have more than 2 parents (i.e. an octopus merge) I am sure you can guess how to diff against the 3rd, 4th, etc. commits.

like image 106
mgd Avatar answered Oct 20 '22 01:10

mgd


From the man page (git help show)

For commits it shows the log message and textual diff.
It also presents the merge commit in a special format as produced by
  git diff-tree --cc
...
For plain blobs, it shows the plain contents.

So, the output of git show is different because this is a merge commit rather than a plain blob. The docs for git diff-tree --cc say:

--cc
    This flag changes the way a merge commit patch is displayed, in a 
    similar way to the -c option. It implies the -c and -p options and
    further compresses the patch output by omitting uninteresting hunks whose
    the contents in the parents have only two variants and the merge result
    picks one of them without modification. When all hunks are uninteresting,
    the commit itself and the commit log message is not shown, just like in
    any other "empty diff" case.
like image 26
Useless Avatar answered Oct 20 '22 02:10

Useless