"git show" does not return the same result as "git diff"

I have a commit in a git repository where it git show

says only one file has changed, but git diff

displays two files as changed.

I've created a simple sample repository that demonstrates the same behavior.

In the sample repo, we have this simple story:

*   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 for example. SourceTree, I see that fileA

and fileB

changed. Usage git diff

gives the same result:

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

      

or using shorthand notation:

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

      

When I try to get the same information with git show

, only one of the two files appears:

$ 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
$

      

Likewise, git diff-tree

it shows nothing:

$ git diff-tree c248261
$

      

Can someone explain the difference please?

I am using git version 2.0.4.

+3


source to share


3 answers


What is it git show

?

Command git show

is a general 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 git show

is different because it is more of a commit than a simple commit.

The man page 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 a look at this last line, which I am repeating here:

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

      

This way, git show

when merging, the commit will only list the files that have changed when comparing the commit against all the parents of the merge. For a normal merge (i.e. 2 parents), these are exactly the files that were merged. If, for example, you do this merge:

git checkout master
git merge feature

      

The files listed git show

are files that have changed both in master

and feature

before the merge and have been merged together in the merge commits.

To merge octopus, that is, more than two parents, the resulting file must be different from the all-parent file to be displayed git show

.

Note that other files may also appear when used git show

in merge compilation. If, for example, you call git merge --no-commit

, and then before performing any actions, then these files will also show up git show

in merge compilation:

  • Add a new file.
  • Delete existing file.
  • Modify any file - including any of the files already shipped with git merge.


Conclusion

What it does git show

is display a so called combined diff.

The result of executing a command git show

when compiling a merge can be thought of as showing only the files that have been merged, not files that have simply been left unchanged by either parent.

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

So what if you want to see the difference between both parents?

Let's take a look at the man page 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.

      

Thus, we can use the option -m

to get both differences like this:

git show -m commit

      

This will give you a difference in relation to all parents one by one.

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

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

      

The parameter --first-parent

does apply to git log

, but also works for git show

. The designation commit^!

is an abbreviation for commit^..commit

.

To see the difference between a merge commit and the branch you merged into (for example, to see what you left outside of another branch), use

git diff commit^2..commit

      

The designator commit^2

means the second parent element commit

.

If you have more than two parents (i.e. merging octopuses), I'm sure you can guess how to distinguish from 3rd, 4th, etc.

+2


source


c248261

Looking at the commit message for commit , the commit you are looking for is probably a merge commit. A merge negotiator has more than one parent.

The ^ suffix to the revision parameter denotes the first parent of that commit object.

So when you say

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

      

It performs a diff from the first parent commit c248261

.

You may try,



$ git diff --name-only c248261^2..c248261

      

which will give you the following output:

fileA
--other files if exist--

      

After merging the two parents, a new commit will be created, i.e. c248261

which has only one file with differences, which you see when you say,

git show --name-only c248261

      

0


source


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 git show

is different because it is more of a commit than a simple 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.

      

0


source







All Articles