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.
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.
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:
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With