You mentioned that:
This does show a commit which delete the files, but it doesn't, because the commit is just a merge commit.
A merge commit that deletes the files ... deletes the files.
Now, looking at the merge commit, you may not "see" a deletion. That's because of the way Git normally shows a commit. Each commit really does hold a snapshot: a full set of all files that will be used from here on, except to the extent that things change in the any later snapshot.
But we don't normally want to see every file. If:
git show a123456
showed us exactly the set of files that git switch --detach a123456
would get us, that wouldn't be all that useful. (If we want to see the set of files in the commit, we just check out that commit, with said git switch
or the old git checkout
equivalent.)
Instead, for an ordinary—i.e., non-merge, non-root—commit, git show
locates not just the commit itself and its files, but also the commit's parent commit, and that commit's snapshot. So if a123456
has parent 987654b
, git show a123456
will:
- extract
987654b
to a temporary area (in memory, really, and Git takes shortcuts);
- extract
a123456
to a temporary memory area likewise; and
- compare the two snapshots.
It then tells us what changed between them, having played a game of Spot the Difference with the two snapshots.
That's great for regular everyday ("ordinary") commits, which have just the one parent. It does not work for:
- merge commits, which have two or more parents;
- root commits, which have no parent.
There's a simple fix that Git uses for a root commit: Git just pretends that there is a parent commit whose snapshot is the empty tree, so that the difference between the non-existent parent commit and the very first commit is that every file is added.
But for merge commits, it's not clear what to do. Git could:
- compare against the first parent;
- compare against the second parent;
- compare, one at a time, against each parent and print multiple diffs; or
- something else.
Both git log
and git show
choose the last option by default, but each one chooses a different "something else":
git log -p
chooses not to show anything. That's pretty clearly not so great; you may wish to add flags to make it show something.
git show
chooses to produce what Git calls a combined diff, by default.
Combined diffs are somewhat useful, especially for merge commits, but they have a couple of deep flaws (that I don't think can be fixed without a redesign here: the new "merge-ort" merge strategy offers the opportunity for this redesign, and that's apparently ongoing now, but it will be some time yet). In particular, the combined diff may not show the deletion of deleted files (if the deletion happened earlier so that one of the parents also omits the files). This leads to a common pattern:
- Someone not well versed in Git creates a feature branch.
- Someone merges a new feature into the main line, which adds a new file.
- The first person merges their feature into the main line and—being unfamiliar with the right way to resolve merge conflicts—winds up deleting the new file in the merge result.
Because the file never existed in the commits they made between steps 1 and 3, the combined diff won't show the file as being "deleted".
Currently, the only way to discover this sort of thing is to do what you did, or to run a process that "retries" each merge to see whether the automated-merge result differs in any way from the committed merge-result. The latter is more useful—it can be fully automated—but until the new merge-ort-related feature goes in, it's painful to actually do it (and you have to write some fairly complicated, messy code).