TL;DR: add -m
to your git log
command.
The git log
command has a nasty little habit / flaw here: when it encounters merge commits, it doesn't bother running a git diff
on them, by default. This applies to both git log -p
(the "show me a diff" variant) and git log -- foo.file
. You've (correctly) inserted --full-history
to defeat git log
's other bad habit, of turning on History Simplification when used with a file name—sometimes this is OK, but not in the case you care about—but it's still being lazy about merge commits.
The big hammer here, equivalent to --full-history
in terms of making git log
do all the work, is to use -m
. This tells Git to (virtually) split each merge. The basic problem is this:
- Every commit, including a merge commit, stores a snapshot of all files.1
- But we usually want to see what changed in the files that changed (
git log -p
).
For ordinary non-merge commits, that's easy enough: Git just extracts both commits—the parent and the child—into a temporary area (in memory and with shortcuts) and compares the two snapshots. For each file that's the same, it does nothing at all. For any file that is different—including "new in child, does not exist in parent" or "gone from child, exists in parent but was removed"—Git can now compare the files' content and produce a diff, or—with --name-status
–just tell us that the files is modified, added, or deleted as appropriate.
But a merge commit, by definition, has at least two parents. Which one should Git extract-and-compare? If we compare the child against its first parent, we'll get one set of files and changes; if we compare the child against its second parent, we get a different set of files and changes.2 Which one should git log
show?
The default, cheat-y answer for git log
is: don't bother!
That is, git log
just says to itself: Oh, hey, a merge commit ... those are tough! I'll just not bother doing anything and hope nobody notices.
If the file is deleted because of the merge, and git log
doesn't say anything, you never see the file getting deleted.
The -m
flag tells git log
: When you come across a merge commit—a commit that has n ≥ 2 parents—do one separate git diff
of each parent vs the child, as if there were n separate one-parent commits. This is easy to do: there's no "what do we do about multiple parents" problem any more. So this is an ordinary diff and will show you the deletion, if there is a deletion.
There are other flags you can use to force git log
to work harder, but they won't necessarily help for this particular case. With -m
being the Big Hammer, it always works; it just may produce more output than you need. If you're a Git Ninja you can go for surgical approaches with -c
or --cc
and/or --first-parent
and/or selective history simplification. But -m --full-history
does the trick.
1More precisely, every commit stores a snapshot of all the files that are in that commit. Said this way it sounds redundant: the commit stores the files that it stores. The idea here, though, is to distinguish these important cases: does a commit store a diff from a previous commit, or does it store a snapshot? The answer is that it stores a snapshot. If we did not have merge commits, this might not matter, but we do have merge commits, so it does end up being important.
(Even then, if Git always stored a diff against the previous first-parent, instead of storing snapshots, we could build what we need. So we're back to "maybe it doesn't matter". But in fact, Git literally stores snapshots, so we might as well just describe how it works, rather than theoretical equivalents. :-) )
2If the two parents are different commits, but both parents have the same snapshot, we will actually get the same set of changes from both comparisons. That's pretty rare though.