f2d0e0f6ad
MapGitDiff anchored only on "+++ b/" and "@@", which drops two change
kinds that every scope it serves is supposed to cover:
- a deleted file, whose new side is "+++ /dev/null" — clearing the
current path there also skipped the "@@" that followed it;
- a 100%-similar rename, which git reports with rename from/to headers
and no "@@" header at all.
Both produced zero hunks, so the file never reached ChangedFiles and its
symbols never reached ChangedSymbols. A delete-only or `git mv`-only
change was reported as an empty diff by detect_changes, change_contract,
review, pr_risk and every other MapGitDiff caller — guards, tests and
contract checks then passed on an empty input set.
Parse deletes off the old side (the only line numbers such an entry
carries, and the span that joins the still-indexed pre-delete symbols)
and renames off their rename headers. DiffResult now carries a
file-granular FileChanges view — added/modified/deleted/renamed, with the
previous path on a rename — so a file with no indexed symbol stays
visible, and moving or removing a file marks every symbol the graph still
holds for its old path.
Two supporting fixes:
MapGitDiff and MapGitDiffWithLines discarded the git error whenever
stdout happened to be empty, so an unknown base ref, a root that is not a
work tree, or a git that never ran returned a confident "no changes".
A failed diff is not an empty diff; report it.
detect_changes answered every empty-symbol case — a clean tree, a
docs-only edit, and (before the above) a delete — with a null
changed_files and one sentence about symbols, which reads as "nothing
changed". changed_files and file_changes are now always arrays, the
summary distinguishes an empty tree from files that carry no indexed
symbol, and the response names what `git diff` structurally cannot see:
untracked and ignored files.
The overlay test fixture becomes a committed git work tree. It only ever
wanted an empty diff, and it was getting one by accident — git refused to
diff a directory that was not a repo, and that error was swallowed.
Untracked-file invisibility is unchanged and now pinned by a test: git
diff never lists them, and nothing here walks an ignored tree.
Fixes #546