fix(llm): keep quoted-path diff sections in the commit-message prompt - #4041
Conversation
git quotes a `diff --git` path pair when the name is non-ASCII (`core.quotePath`, on by default) or holds `"`/`\\`. Neither header contains a bare ` b/`, so `parse_diff_sections` found no path and dropped that file's diff from the over-budget prompt entirely. Parse the quoted form as well, and open a section on any `diff --git` header whether or not its path parses — the name only feeds lock-file filtering.
worktrunk-bot
left a comment
There was a problem hiding this comment.
Two of the new test literals use the forms CLAUDE.md rules out — "never \ continuation (silently strips following whitespace) or concat!()" — and here the rule's own rationale is what makes it worth changing: the content is dense with " and \, so the r#"…"# form the other tests in this module already use is both the mandated one and the readable one. Suggestions inline; both compile, pass, and leave cargo fmt --check clean.
Separately, and a decision rather than a change to this PR: core.quotePath=false in DIFF_PREFIX_OVERRIDES would drop the non-ASCII case at the source and put real filenames into the prompt — in the diffstat as well as the diff — instead of \303\251.txt. It does not replace the parser fix, since a name holding " stays quoted whatever that setting says (confirmed against git: with core.quotePath=false, é.txt stops quoting while the header for we"ird.lock is still diff --git "a/we\"ird.lock" "b/we\"ird.lock"). Growing the overrides — and the two --stat calls that don't carry them — is its own call, so leaving it as a note.
CLAUDE.md rules out `concat!()` and `\` continuation for multiline strings. Both new fixtures are dense with " and \, so the r#"…"# form the rest of the module already uses is both the mandated one and the readable one — the escaped git output now reads verbatim.
|
On the It is not a substitute for the parser fix — a name holding |
A commit or squash message generated over a large diff silently omitted any file whose name git quotes.
parse_diff_sectionsread the destination path withline.split(" b/").nth(1), and a quoted header carries no bareb/— so the path came backNoneand the section was never pushed onto the list, taking the file's diff out of the prompt with it. The file still appears in the diffstat, so the model is told the file changed and shown nothing of how.Two headers hit this, and both are ordinary rather than exotic:
core.quotePathis on by default, so any non-ASCII filename quotes — accented names, CJK, emoji. A name containing"or\quotes regardless of that setting. Reaching the drop needs the diff overDIFF_BUDGET(100 KB), since a diff under budget passes through whole; over it,parse_diff_sectionsis the only view of the content.This is the same trap #3486 hit in
step copy-ignored, fixed by #3487.Fix
parse_diff_header_pathreads the quoted form too, keying on the"b/thatDIFF_PREFIX_OVERRIDESalready pins. It returns the escaped name (\303\251.txt), which is what the caller needs: the name feedsis_lock_file's suffix match, never the filesystem.A header whose path still doesn't parse now opens a section anyway, with an empty name. Losing the name costs lock-file filtering for that one file; losing the section costs the diff. A path that itself contains
b/stays ambiguous — git's own plain-text output cannot express that either, which is why--numstat -zexists.Tests
Three regression tests, each failing before the fix:
test_parse_diff_sections_quoted_paths— both quoted shapes parse, and the concatenated sections still reproduce the input byte-for-byte.test_parse_diff_sections_unparsable_header_keeps_content— an unreadable header keeps its content.test_prepare_diff_keeps_quoted_path_sections— end to end throughprepare_diffover budget, which is the path that reaches users.cargo test --lib --binsandcargo clippy --all-targets --all-featuresare clean locally.