2

I have two branches: master, and a long-running feature branch, called feature. I continually merge from master into feature.

One of my merge conflicts is:

Unmerged paths:
  (use "git add/rm <file>..." as appropriate to mark resolution)
    deleted by us:   foo.file

When merging, I saw the following message:

CONFLICT (modify/delete): foo.file deleted in HEAD and modified in origin/master. Version origin/master of foo.file left in tree.

I'm interpreting this to mean that the file was updated on master but deleted on feature. I'd like to see which feature commit deleted it. Following Find when a file was deleted in git, I tried:

$ git log --oneline --name-status --full-history -- foo.file
0e051cd636 (feature) Merge from master
ee5c4f1ccc (feature) Merge from master
c200d5d8b2 (master) Add foo.file
A   foo.file

I was hoping to see a D foo.file line from --name-status, but I didn't. And when I did show on the two merge commits, I got empty output:

$ git show 0e051cd636 -- foo.file
$ git show ee5c4f1ccc -- foo.file
$ 

This command also gives empty output:

$ git log --diff-filter=D --summary foo.file
$

I also tried rev-list as suggested in the linked question, but it had empty output as well:

$ git rev-list -n 1 HEAD -- foo.file
$

So, how can I figure out when this file was deleted?

Nick Heiner
  • 119,074
  • 188
  • 476
  • 699
  • 1
    Add `-m` to your `git log --oneline --name-status --full-history -- foo.file` command. Otherwise `git log` does not say where the file was deleted in a merge commit, assuming a merge commit is where it was deleted. (You may want to disable rename detection as well, with `--no-renames`, although that's probably unnecessary here since you're not using `--follow`.) – torek May 04 '20 at 21:18
  • Note, however, that `git status` in a conflicted merge can say *deleted by us* when the actual issue was a high level conflict involving a file rename. If the original `git merge` command said, somewhere, `CONFLICT (rename/...)`, you may be seeing a bogus "deleted by us" status. – torek May 04 '20 at 21:23
  • Adding `-m` revealed that the file was deleted in a merge commit. Thanks! Convert that comment to an answer and I'll accept it. – Nick Heiner May 07 '20 at 19:02

2 Answers2

1

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.

torek
  • 448,244
  • 59
  • 642
  • 775
0

What about to list all files that have been deleted from a git repository:

git log --diff-filter=D --summary

So you see all deleted file with the commit-hash where you delete the file.

from git diff documentation

SwissCodeMen
  • 4,222
  • 8
  • 24
  • 34