From 6ae57a26b50056735ce4c51c028bdab140fc3217 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 25 Jan 2026 15:37:54 -0500 Subject: [PATCH 1/2] test(patch): trailing garbage after no-newline-at-eof marker This was a regression introduced by commit 79c086b3eeb231c (#7), which added garbage handling for complete hunks but missed the `no_newline_context` branch. When a hunk ends with `\ No newline at end of file` on a context line and is followed by trailing content (e.g., `diff --git` headers from UniDiff splitting), the parser fails with "expected end of hunk". Pattern first appeared in rust-lang/cargo@b119b891d. --- src/patch/parse.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/patch/parse.rs b/src/patch/parse.rs index eff7232f..e3a2cac7 100644 --- a/src/patch/parse.rs +++ b/src/patch/parse.rs @@ -390,6 +390,38 @@ index 1234567..89abcdef 100644 assert_eq!(patch.hunks().len(), 1); } + /// Regression test for parsing UniDiff patches from `git diff` output. + /// + /// When UniDiff mode splits patches by `---/+++` boundaries, trailing + /// `diff --git` lines from the next patch may be included. If the last + /// hunk ends with `\ No newline at end of file`, the parser should still + /// recognize the hunk as complete and ignore the trailing garbage. + /// + /// This pattern appears in rust-lang/cargo@b119b891df93f128abef634215cd8f967c3cd120 + /// where HTML files lost their trailing newlines. + #[test] + fn no_newline_at_eof_followed_by_trailing_garbage() { + // Simulates UniDiff split including next patch's git headers + let s = "\ +--- a/file.html ++++ b/file.html +@@ -1,3 +1,3 @@ +
+-

old

++

new

+
+\\ No newline at end of file +diff --git a/other.html b/other.html +index 1234567..89abcdef 100644 +"; + let result = parse(s); + assert!( + result.is_err(), + "BUG: This test documents current broken behavior. \ + After fix, change to unwrap() and verify hunks." + ); + } + #[test] fn multi_hunk_with_trailing_garbage() { let s = "\ From f2bbe2a9dc2575b232fbd55183230ea96ca97aa6 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Sun, 25 Jan 2026 15:41:47 -0500 Subject: [PATCH 2/2] fix(patch): trailing garbage after no-newline-at-eof marker This was a regression introduced by commit 79c086b3eeb231c (#7), which added garbage handling for complete hunks but missed the `no_newline_context` branch. When a hunk ends with `\ No newline at end of file` on a context line and is followed by trailing content (e.g., `diff --git` headers from UniDiff splitting), the parser now correctly recognizes the hunk as complete instead of erroring with "expected end of hunk". This fixes parsing of `git diff` output in UniDiff mode where patches are split by `---/+++` boundaries, causing subsequent git headers to be appended to the previous patch. Pattern first appeared in rust-lang/cargo@b119b891d. --- src/patch/parse.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/patch/parse.rs b/src/patch/parse.rs index e3a2cac7..d09db633 100644 --- a/src/patch/parse.rs +++ b/src/patch/parse.rs @@ -252,6 +252,12 @@ fn hunk_lines<'a, T: Text + ?Sized>( let line = if line.starts_with("@") { break; } else if no_newline_context { + // After `\ No newline at end of file` on a context line, + // only a new hunk header is valid. Any other line means + // the hunk should be complete, or it's an error. + if hunk_complete { + break; + } return Err(ParsePatchError::new("expected end of hunk")); } else if let Some(line) = line.strip_prefix(" ") { Line::Context(line) @@ -414,12 +420,10 @@ index 1234567..89abcdef 100644 diff --git a/other.html b/other.html index 1234567..89abcdef 100644 "; - let result = parse(s); - assert!( - result.is_err(), - "BUG: This test documents current broken behavior. \ - After fix, change to unwrap() and verify hunks." - ); + let patch = parse(s).unwrap(); + assert_eq!(patch.hunks().len(), 1); + assert_eq!(patch.hunks()[0].old_range().len(), 3); + assert_eq!(patch.hunks()[0].new_range().len(), 3); } #[test]