{"record":{"id":"94c592317eed63e2","repo":"rtk-ai/rtk","slug":"numstat-p-must-parse","errorCode":null,"errorMessage":"--numstat -p must parse","messagePattern":"--numstat -p must parse","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cmds/git/diff_cmd.rs","lineNumber":6611,"sourceCode":"        // `git diff --cached -M --no-prefix`: `diff --git src.txt dst.txt`\n        // shares no tail; `rename to` names it and the pair may not.\n        let np = include_str!(\"../../../tests/fixtures/diff/git_diff_no_prefix_rename_raw.txt\");\n        let out = condense_unified_diff_strict(np).expect(\"must parse\");\n        assert!(\n            out.contains(\"[file] dst.txt (renamed from src.txt) (+1 -0)\"),\n            \"got:\\n{out}\"\n        );\n        // `--submodule=diff`: nested column-0 sections under the range.\n        let sd = include_str!(\"../../../tests/fixtures/diff/git_diff_submodule_diff_raw.txt\");\n        assert_eq!(\n            condense_unified_diff_strict(sd).as_deref(),\n            Some(\n                \"[file] o.txt (+1 -1)\\n-k\\n+k2\\n[file] sub (submodule, 0641fef..19e2e29)\\n[file] sub/s.txt (+1 -1)\\n-s\\n+s2\\n[file] sub/sb.bin (binary)\"\n            )\n        );\n        // `git log --numstat -p`: `-<TAB>-<TAB>b.bin` before the diff.\n        let ns = include_str!(\"../../../tests/fixtures/diff/git_log_numstat_p_raw.txt\");\n        let out = condense_unified_diff_strict(ns).expect(\"--numstat -p must parse\");\n        assert!(out.contains(\"[file] b.bin (binary)\"), \"got:\\n{out}\");\n        assert!(out.contains(\"[file] d/e/f (+1 -0)\"), \"got:\\n{out}\");\n        // `hg log -p --template '{node|short} {desc}\\n'`: a prose prologue\n        // with no `changeset:` line, then hg's echo — prose is not a fact.\n        let tmpl = include_str!(\"../../../tests/fixtures/diff/hg_log_p_template_raw.txt\");\n        let out = condense_unified_diff_strict(tmpl).expect(\"templated hg log must parse\");\n        assert!(out.contains(\"[file] bin.dat (binary)\"), \"got:\\n{out}\");\n        assert!(out.contains(\"[file] f.txt (+1 -1)\"), \"got:\\n{out}\");\n        // `LC_ALL=fr_FR.UTF-8 diff -u`: the marker is localized, position\n        // decides — kept after `-x` and after `+y`.\n        let fr = include_str!(\"../../../tests/fixtures/diff/diff_u_fr_no_newline_raw.txt\");\n        assert_eq!(\n            condense_unified_diff_strict(fr).as_deref(),\n            Some(\n                \"[file] n2 (+1 -1)\\n-x\\n\\\\ Pas de fin de ligne à la fin du fichier\\n+y\\n\\\\ Pas de fin de ligne à la fin du fichier\"\n            )\n        );\n        // A git binary section followed by another producer's header pair","sourceCodeStart":6593,"sourceCodeEnd":6629,"githubUrl":"https://github.com/rtk-ai/rtk/blob/0924356b4caba4989607227b7c8824d3d8098719/src/cmds/git/diff_cmd.rs#L6593-L6629","documentation":"`.expect(\"--numstat -p must parse\")` panic: `condense_unified_diff_strict()` returned None on a `git log --numstat -p` fixture. The challenge is a mixed stream: a `--numstat` block (including `-<TAB>-<TAB>b.bin`, the binary marker) precedes the patches, and the condenser must consume numstat facts and the patches together. None indicates the numstat prelude derails header detection.","triggerScenarios":"Calling `condense_unified_diff_strict()` on tests/fixtures/diff/git_log_numstat_p_raw.txt returns None because the leading numstat lines (`added<TAB>deleted<TAB>path`, with `-` for binary) are not recognized, so the parser never enters patch mode and yields None. The expected output mixes `[file] b.bin (binary)` and patch-derived entries like `[file] d/e/f (+1 -0)`.","commonSituations":"Running `rtk git log --numstat -p`; combining stat-style producers with `-p` patches; changes to numstat/binary-marker handling in the diff filter.","solutions":["Teach the strict condenser to skip/collect `--numstat` lines (tab-separated, with `-` placeholder for binary) until the first `commit`/`diff --git` boundary.","Treat `-<TAB>-<TAB>path` as a binary fact, not a malformed header.","Verify sub-commit boundaries in `git log -p` streams reset state between patches.","Add a standalone test for the numstat prelude scanner."],"exampleFix":"// before\nlet lines = diff.split('\\n').collect(); // first line '3\\t1\\tpath' => no header => None\n// after\nlet lines = skip_numstat_prelude(diff).split('\\n').collect();","handlingStrategy":"validation","validationCode":"assert!(condense_unified_diff_strict(&numstat_p_fixture)\n    .map_or(false, |o| o.contains(\"[file] b.bin (binary)\") && o.contains(\"[file] d/e/f (+1 -0)\")));","typeGuard":"fn can_condense(diff: &str) -> bool { crate::cmds::git::diff_cmd::condense_unified_diff_strict(diff).is_some() }","tryCatchPattern":"let out = match condense_unified_diff_strict(input) {\n    Some(o) if o.len() < input.len() => o,\n    _ => input.to_string(),\n};","preventionTips":["Whenever combining stat-style flags (-numstat/--stat/--shortstat) with -p, add a mixed-stream fixture.","Treat tab-separated numeric preludes as facts, never as candidate headers.","Reset parser state at each commit boundary in log -p streams.","Note binary markers (`-\\t-\\tpath`) explicitly in the numstat scanner."],"tags":["rust","test-failure","diff-parsing","git-log"],"backgroundTag":"internal-invariant-violation","analyzedSha":"0924356b4caba4989607227b7c8824d3d8098719","analyzedAt":"2026-09-19T19:25:29.612Z","contentChangedAt":"2026-09-19T19:25:29.612Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}