From 8fdc9a965dfb9f3b850173a924a1793d17f599fa Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Wed, 25 Feb 2026 12:38:03 +0000 Subject: [PATCH 1/7] Add test for index_patch_generic content skipping bug Test case for issue where interdiff incorrectly reports 'patch2 is empty' when the @@ line isn't immediately after the +++ line (e.g., when there's an 'index' line in between). This test currently fails on 0.4.x and will pass after applying the fix from commit 3a6e39d. Assisted-by: Claude Code --- tests/interdiff-patch2-headers/run-test | 54 +++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100755 tests/interdiff-patch2-headers/run-test diff --git a/tests/interdiff-patch2-headers/run-test b/tests/interdiff-patch2-headers/run-test new file mode 100755 index 0000000..5fcaf6c --- /dev/null +++ b/tests/interdiff-patch2-headers/run-test @@ -0,0 +1,54 @@ +#!/bin/sh + +# Test interdiff with patch2 having headers between +++ and @@ +# This tests that interdiff can handle extra headers (like "index") between +# the +++ line and @@ line in patch2, without incorrectly reporting it as empty + +. ${top_srcdir-.}/tests/common.sh + +# Create first patch (simple unified diff) +cat << EOF > patch1 +--- a/file.txt ++++ b/file.txt +@@ -1,3 +1,3 @@ + line one +-line two ++line TWO + line three +EOF + +# Create second patch (git-style with index line between +++ and @@) +cat << EOF > patch2 +--- a/file.txt ++++ b/file.txt +index abc123..def456 100644 +@@ -1,3 +1,3 @@ + line one +-line two ++line 2 + line three +EOF + +# Expected interdiff output +# This shows the difference between changing "two" to "TWO" vs "2" +cat << EOF > expected +diff -u b/file.txt b/file.txt +--- b/file.txt ++++ b/file.txt +@@ -1,3 +1,3 @@ + line one +-line TWO ++line 2 + line three +EOF + +# Run interdiff - should succeed without errors +${INTERDIFF} patch1 patch2 2>errors >actual || exit 1 + +# Should not have any errors (especially not "patch2 is empty" or similar) +[ -s errors ] && exit 1 + +# Compare the actual output with expected +diff -u expected actual || exit 1 + +exit 0 From 8bd39ed736cc1716b09b3a0a85e519b9f8f8d158 Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Fri, 14 Nov 2025 09:19:49 -0800 Subject: [PATCH 2/7] Fix content skipping for patch2 in index_patch_generic() When an @@ line isn't immediately after the +++ line in patch2, the next line is checked from the top of the loop which tries to search for a +++ line again, even though the +++ was already found. This results in the +++ not being found again and thus a spurious error that patch2 is empty. Fix this by making the patch2 case loop over the next line until either an @@ is found or the patch is exhausted. --- src/interdiff.c | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/interdiff.c b/src/interdiff.c index d1cc9e2..023347c 100644 --- a/src/interdiff.c +++ b/src/interdiff.c @@ -1442,29 +1442,36 @@ index_patch_generic (FILE *patch_file, struct file_list **file_list, int need_sk /* For patch2, we need to handle the @@ line and skip content */ if (need_skip_content) { - if (getline (&line, &linelen, patch_file) == -1) { + int found = 0; + + while (!found && + getline (&line, &linelen, patch_file) > 0) { + if (strncmp (line, "@@ ", 3)) + continue; + + p = strchr (line + 3, '+'); + if (!p) + continue; + + p = strchr (p, ','); + if (p) { + /* Like '@@ -1,3 +1,3 @@' */ + p++; + skip = strtoul (p, &end, 10); + if (p == end) + continue; + } else + /* Like '@@ -1 +1 @@' */ + skip = 1; + found = 1; + } + + if (!found) { free (names[0]); free (names[1]); break; } - if (strncmp (line, "@@ ", 3)) - goto try_next; - - p = strchr (line + 3, '+'); - if (!p) - goto try_next; - p = strchr (p, ','); - if (p) { - /* Like '@@ -1,3 +1,3 @@' */ - p++; - skip = strtoul (p, &end, 10); - if (p == end) - goto try_next; - } else - /* Like '@@ -1 +1 @@' */ - skip = 1; - add_to_list (file_list, best_name (2, names), pos); while (skip--) { @@ -1480,7 +1487,6 @@ index_patch_generic (FILE *patch_file, struct file_list **file_list, int need_sk add_to_list (file_list, best_name (2, names), pos); } - try_next: free (names[0]); free (names[1]); } From 48f3083d160fb9e74da8e818d98f9482ec6d33d7 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Wed, 25 Feb 2026 12:38:54 +0000 Subject: [PATCH 3/7] Add test for false 'doesn't contain a patch' error Test case for issue where interdiff incorrectly warns that patches don't contain a patch when they only have mode changes, binary diffs, or merge metadata without actual unified diff content. This test currently fails on 0.4.x and will pass after applying the fix from commit edded43. Assisted-by: Claude Code --- tests/interdiff-mode-only/run-test | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100755 tests/interdiff-mode-only/run-test diff --git a/tests/interdiff-mode-only/run-test b/tests/interdiff-mode-only/run-test new file mode 100755 index 0000000..2e6e789 --- /dev/null +++ b/tests/interdiff-mode-only/run-test @@ -0,0 +1,35 @@ +#!/bin/sh + +# Test interdiff with mode-only patches +# This tests that interdiff doesn't incorrectly warn "doesn't contain a patch" +# for patches that only contain mode changes, binary diffs, or merge metadata + +. ${top_srcdir-.}/tests/common.sh + +# Create first patch - mode-only change (no actual diff content) +cat << EOF > patch1 +diff --git a/script.sh b/script.sh +old mode 100644 +new mode 100755 +EOF + +# Create second patch - same mode-only change +cat << EOF > patch2 +diff --git a/script.sh b/script.sh +old mode 100644 +new mode 100755 +EOF + +# Run interdiff - should succeed without the false "doesn't contain a patch" error +${INTERDIFF} patch1 patch2 2>errors >actual || exit 1 + +# The key test: should NOT have "doesn't contain a patch" error +if grep -q "doesn't contain a patch" errors; then + echo "FAIL: Got spurious 'doesn't contain a patch' error" + cat errors + exit 1 +fi + +# For mode-only patches, interdiff should produce minimal or no output +# Just verify it ran without error +exit 0 From f8ed712c118c6a5c2335f681636ae37d3231b59c Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Mon, 23 Feb 2026 16:22:49 -0800 Subject: [PATCH 4/7] interdiff: Suppress false 'doesn't contain a patch' for non-diff patches Patches that contain only mode changes, binary diffs, merge metadata, or no diff content at all would trigger a spurious error message. Check for actual file-header '--- a/' or '--- /dev/null' lines before reporting a patch as invalid. Assisted-by: Claude Opus 4.6 (1M context) --- src/interdiff.c | 37 +++++++++++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/interdiff.c b/src/interdiff.c index 023347c..964699c 100644 --- a/src/interdiff.c +++ b/src/interdiff.c @@ -1494,10 +1494,26 @@ index_patch_generic (FILE *patch_file, struct file_list **file_list, int need_sk if (line) free (line); + /* Return success if the file is empty, has indexed files, or + * has no --- lines at all (merge commits, mode-only changes, + * notes-only commits, binary-only patches, etc.). */ if (file_is_empty || *file_list) return 0; - else - return 1; + + /* Check if the patch has any file-header --- lines (as + * opposed to commit-message separators or b4 tracking). + * If not, it simply has no diffable content. */ + line = NULL; + linelen = 0; + rewind (patch_file); + while (getline (&line, &linelen, patch_file) != -1) + if (!strncmp (line, "--- a/", 6) || + !strncmp (line, "--- /dev/null", 13)) { + free (line); + return 1; + } + free (line); + return 0; } static int @@ -2156,8 +2172,21 @@ interdiff (FILE *p1, FILE *p2, const char *patch1, const char *patch2) free (p); } - if (!file_is_empty && !patch_found) - no_patch (patch1); + if (!file_is_empty && !patch_found) { + /* Don't warn for patches with no file-header --- lines + * (merge commits, mode-only, binary-only, etc.). */ + int has_diff_content = 0; + + rewind (p1); + while (getline (&line, &linelen, p1) != -1) + if (!strncmp (line, "--- a/", 6) || + !strncmp (line, "--- /dev/null", 13)) { + has_diff_content = 1; + break; + } + if (has_diff_content) + no_patch (patch1); + } copy_residue (p2, mode == mode_flip ? flip1 : stdout); From f3d432e62cee5ea7db973e99701dff3f9042b20d Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Thu, 13 Nov 2025 17:36:12 -0800 Subject: [PATCH 5/7] Exclude newline character from colorized output Coloring the newline character results in the terminal cursor becoming colored when the final line in the interdiff is colored. Fix this by not coloring the newline character. --- src/interdiff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/interdiff.c b/src/interdiff.c index 964699c..bea35ee 100644 --- a/src/interdiff.c +++ b/src/interdiff.c @@ -1136,7 +1136,8 @@ trim_context (FILE *f /* positioned at start of @@ line */, fwrite (line, (size_t) got, 1, out); continue; } - print_color (out, type, "%s", line); + print_color (out, type, "%.*s", (int) got - 1, line); + fputc ('\n', out); } } From 1286db17f089cb6fbe0727f0734e133d2beabade Mon Sep 17 00:00:00 2001 From: Sultan Alsawaf Date: Mon, 23 Feb 2026 21:36:50 -0800 Subject: [PATCH 6/7] interdiff: Fix memory leak of parsed filename on early exit When a '---' header is parsed but the following line is not '+++' or hits EOF, the allocated filename from the '---' line is leaked. Free it on both early-exit paths, matching the pattern already used in index_patch_generic(). Assisted-by: Claude Opus 4.6 (1M context) --- src/interdiff.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/interdiff.c b/src/interdiff.c index bea35ee..96f5a92 100644 --- a/src/interdiff.c +++ b/src/interdiff.c @@ -2136,11 +2136,15 @@ interdiff (FILE *p1, FILE *p2, const char *patch1, const char *patch2) names[0] = filename_from_header (line + 4); - if (getline (&line, &linelen, p1) == -1) + if (getline (&line, &linelen, p1) == -1) { + free (names[0]); break; + } - if (strncmp (line, "+++ ", 4)) + if (strncmp (line, "+++ ", 4)) { + free (names[0]); continue; + } names[1] = filename_from_header (line + 4); From 548613e488fd0b8561b4bc8e9dc452bc5e35cbb6 Mon Sep 17 00:00:00 2001 From: Tim Waugh Date: Wed, 25 Feb 2026 12:46:37 +0000 Subject: [PATCH 7/7] Add new interdiff tests to Makefile.am Add the two new test cases to the TESTS variable: - tests/interdiff-patch2-headers/run-test - tests/interdiff-mode-only/run-test Assisted-by: Claude Code --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index e2657bb..9b90152 100644 --- a/Makefile.am +++ b/Makefile.am @@ -304,6 +304,8 @@ TESTS = tests/newline1/run-test \ tests/whitespace-regression/run-test \ tests/interdiff-whitespace-w/run-test \ tests/interdiff-color-context/run-test \ + tests/interdiff-patch2-headers/run-test \ + tests/interdiff-mode-only/run-test \ tests/patch-ignore-whitespace/run-test \ tests/whitespace-w/run-test \ tests/filterdiff-inplace1/run-test \