Skip to content

Commit 5885902

Browse files
authored
Merge pull request #170 from twaugh/git-extended-diffs-default-include
Merge 0.4.x changes
2 parents bb61dfb + 231979a commit 5885902

28 files changed

Lines changed: 441 additions & 121 deletions

File tree

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,7 @@ TESTS = tests/newline1/run-test \
423423
tests/git-rename-issue22/run-test \
424424
tests/git-binary-issue57/run-test \
425425
tests/git-binary-formats/run-test \
426+
tests/git-extended-diffs/run-test \
426427
tests/git-mode-issue59/run-test \
427428
tests/git-exclude-issue27/run-test \
428429
tests/git-prefixes-option/run-test \

NEWS

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,17 @@ Patchutils news
33

44
0.4.5 (stable)
55

6+
Reverted incompatible behavior change from version 0.4.4. In 0.4.4,
7+
Git diffs without content hunks (renames, copies, mode-only changes,
8+
binary files) were included in output and file numbering, breaking
9+
compatibility with 0.4.3 and earlier versions. This caused file numbers
10+
to change for scripts using -N/-F options. The 0.4.3 behavior has been
11+
restored as the default: these diffs are now excluded from output and
12+
numbering. Added --git-extended-diffs option to control this behavior:
13+
use --git-extended-diffs=include to get the 0.4.4 behavior if needed.
14+
The default will change to 'include' in version 0.5.0 for modern Git
15+
workflow support. Addresses GitHub issue #157.
16+
617
Fixed grepdiff -s/--status to display correct file status indicators.
718
Previously, grepdiff -s incorrectly showed '!' (modification) for all
819
matching files regardless of whether they were additions, deletions,

doc/patchutils.xml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,7 @@
592592
</group>
593593
<arg choice="opt">--strip=<replaceable>n</replaceable></arg>
594594
<arg choice="opt">--git-prefixes=<replaceable>strip|keep</replaceable></arg>
595+
<arg choice="opt">--git-extended-diffs=<replaceable>exclude|include</replaceable></arg>
595596
<arg choice="opt">--addprefix=<replaceable>PREFIX</replaceable></arg>
596597
<arg choice="opt">--addoldprefix=<replaceable>PREFIX</replaceable></arg>
597598
<arg choice="opt">--addnewprefix=<replaceable>PREFIX</replaceable></arg>
@@ -799,6 +800,18 @@
799800
in version 0.5.0.</para>
800801
</listitem>
801802
</varlistentry>
803+
<varlistentry>
804+
<term><option>--git-extended-diffs</option>=<replaceable>exclude|include</replaceable></term>
805+
<listitem>
806+
<para>Controls whether to process Git diffs without traditional content hunks.
807+
This includes renames, copies, mode-only changes, and binary files.
808+
With <literal>exclude</literal>, these diffs are skipped (this was
809+
the default in versions prior to 0.5.0). With <literal>include</literal>
810+
(the default), these diffs are processed normally. This option affects
811+
file numbering with <option>-N</option> and file filtering with
812+
<option>-F</option>.</para>
813+
</listitem>
814+
</varlistentry>
802815
<varlistentry>
803816
<term><option>--addprefix</option>=<replaceable>PREFIX</replaceable></term>
804817
<listitem>
@@ -1078,6 +1091,7 @@ patch.file]]></screen></para>
10781091
</group>
10791092
<arg choice="opt">--strip=<replaceable>n</replaceable></arg>
10801093
<arg choice="opt">--git-prefixes=<replaceable>strip|keep</replaceable></arg>
1094+
<arg choice="opt">--git-extended-diffs=<replaceable>exclude|include</replaceable></arg>
10811095
<arg choice="opt">--addprefix=<replaceable>PREFIX</replaceable></arg>
10821096
<arg choice="opt">--addoldprefix=<replaceable>PREFIX</replaceable></arg>
10831097
<arg choice="opt">--addnewprefix=<replaceable>PREFIX</replaceable></arg>
@@ -1264,6 +1278,18 @@ patch.file]]></screen></para>
12641278
in version 0.5.0.</para>
12651279
</listitem>
12661280
</varlistentry>
1281+
<varlistentry>
1282+
<term><option>--git-extended-diffs</option>=<replaceable>exclude|include</replaceable></term>
1283+
<listitem>
1284+
<para>Controls whether to process Git diffs without traditional content hunks.
1285+
This includes renames, copies, mode-only changes, and binary files.
1286+
With <literal>exclude</literal>, these diffs are skipped (this was
1287+
the default in versions prior to 0.5.0). With <literal>include</literal>
1288+
(the default), these diffs are processed normally. This option affects
1289+
file numbering with <option>-N</option> and file filtering with
1290+
<option>-F</option>.</para>
1291+
</listitem>
1292+
</varlistentry>
12671293
<varlistentry>
12681294
<term><option>--addprefix</option>=<replaceable>PREFIX</replaceable></term>
12691295
<listitem>
@@ -1457,6 +1483,7 @@ done)]]></screen></para>
14571483
</group>
14581484
<arg choice="opt">--strip=<replaceable>n</replaceable></arg>
14591485
<arg choice="opt">--git-prefixes=<replaceable>strip|keep</replaceable></arg>
1486+
<arg choice="opt">--git-extended-diffs=<replaceable>exclude|include</replaceable></arg>
14601487
<arg choice="opt">--addprefix=<replaceable>PREFIX</replaceable></arg>
14611488
<group choice="opt">
14621489
<arg>-s</arg>
@@ -1666,6 +1693,18 @@ This is the same as gitdiff but uses git show instead of git diff.
16661693
in version 0.5.0.</para>
16671694
</listitem>
16681695
</varlistentry>
1696+
<varlistentry>
1697+
<term><option>--git-extended-diffs</option>=<replaceable>exclude|include</replaceable></term>
1698+
<listitem>
1699+
<para>Controls whether to process Git diffs without traditional content hunks.
1700+
This includes renames, copies, mode-only changes, and binary files.
1701+
With <literal>exclude</literal>, these diffs are skipped (this was
1702+
the default in versions prior to 0.5.0). With <literal>include</literal>
1703+
(the default), these diffs are processed normally. This option affects
1704+
file numbering with <option>-N</option> and file filtering with
1705+
<option>-F</option>.</para>
1706+
</listitem>
1707+
</varlistentry>
16691708
<varlistentry>
16701709
<term><option>--addprefix</option>=<replaceable>PREFIX</replaceable></term>
16711710
<listitem>
@@ -2050,6 +2089,7 @@ This is the same as gitdiff but uses git show instead of git diff.
20502089
</group>
20512090
<arg choice="opt">--strip=<replaceable>n</replaceable></arg>
20522091
<arg choice="opt">--git-prefixes=<replaceable>strip|keep</replaceable></arg>
2092+
<arg choice="opt">--git-extended-diffs=<replaceable>exclude|include</replaceable></arg>
20532093
<arg choice="opt">--addprefix=<replaceable>PREFIX</replaceable></arg>
20542094
<arg choice="opt">--addoldprefix=<replaceable>PREFIX</replaceable></arg>
20552095
<arg choice="opt">--addnewprefix=<replaceable>PREFIX</replaceable></arg>
@@ -2199,6 +2239,18 @@ This is the same as gitdiff but uses git show instead of git diff.
21992239
in version 0.5.0.</para>
22002240
</listitem>
22012241
</varlistentry>
2242+
<varlistentry>
2243+
<term><option>--git-extended-diffs</option>=<replaceable>exclude|include</replaceable></term>
2244+
<listitem>
2245+
<para>Controls whether to process Git diffs without traditional content hunks.
2246+
This includes renames, copies, mode-only changes, and binary files.
2247+
With <literal>exclude</literal>, these diffs are skipped (this was
2248+
the default in versions prior to 0.5.0). With <literal>include</literal>
2249+
(the default), these diffs are processed normally. This option affects
2250+
file numbering with <option>-N</option> and file filtering with
2251+
<option>-F</option>.</para>
2252+
</listitem>
2253+
</varlistentry>
22022254
<varlistentry>
22032255
<term><option>--addprefix</option>=<replaceable>PREFIX</replaceable></term>
22042256
<listitem>

src/filterdiff.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,33 @@ static unsigned long filecount=0;
112112

113113
static enum git_prefix_mode git_prefix_mode = GIT_PREFIX_KEEP;
114114

115+
enum git_extended_diffs_mode {
116+
GIT_EXTENDED_DIFFS_EXCLUDE = 0, /* Skip extended diffs (0.4.3 behavior) */
117+
GIT_EXTENDED_DIFFS_INCLUDE = 1 /* Process extended diffs (Git workflow) */
118+
};
119+
120+
static enum git_extended_diffs_mode git_extended_diffs_mode = GIT_EXTENDED_DIFFS_INCLUDE;
121+
122+
/* Helper function to check if a git diff type should be excluded based on mode */
123+
static int
124+
should_skip_git_extended_diff (enum git_diff_type git_type)
125+
{
126+
if (git_extended_diffs_mode == GIT_EXTENDED_DIFFS_INCLUDE)
127+
return 0; /* Don't skip anything if include mode */
128+
129+
/* In exclude mode, skip all diffs without content hunks.
130+
* This includes renames, copies, mode-only changes, binary files,
131+
* and new/deleted files without content (e.g., binary files).
132+
* This restores 0.4.3 behavior where only files with actual
133+
* patch hunks were shown. */
134+
switch (git_type) {
135+
case GIT_DIFF_NORMAL:
136+
return 0; /* Don't skip - has hunks */
137+
default:
138+
return 1; /* Skip all extended/special types */
139+
}
140+
}
141+
115142
/* Helper function to check if current patch is a Git patch */
116143
static int
117144
is_git_patch (char **headers, unsigned int num_headers)
@@ -1314,7 +1341,11 @@ static int filterdiff (FILE *f, const char *patchname)
13141341
/* Process as git diff without hunks and then exit */
13151342
enum git_diff_type git_type = detect_git_diff_type (header, num_headers);
13161343

1317-
if (git_type != GIT_DIFF_NORMAL) {
1344+
1345+
/* Skip extended diffs if in exclude mode */
1346+
if (should_skip_git_extended_diff (git_type))
1347+
goto eof;
1348+
if (git_type != GIT_DIFF_NORMAL) {
13181349
char *git_old_name = NULL, *git_new_name = NULL;
13191350
const char *p_stripped;
13201351
int match;
@@ -1391,7 +1422,11 @@ static int filterdiff (FILE *f, const char *patchname)
13911422
/* Check if this is a git diff without hunks or with content like Binary files */
13921423
enum git_diff_type git_type = detect_git_diff_type (header, num_headers);
13931424

1394-
if (git_type != GIT_DIFF_NORMAL) {
1425+
1426+
/* Skip extended diffs if in exclude mode */
1427+
if (should_skip_git_extended_diff (git_type))
1428+
goto flush_continue;
1429+
if (git_type != GIT_DIFF_NORMAL) {
13951430
/* This is a git diff without hunks - handle it */
13961431
char *git_old_name = NULL, *git_new_name = NULL;
13971432
const char *p_stripped;
@@ -1633,6 +1668,9 @@ const char * syntax_str =
16331668
" --git-prefixes=strip|keep\n"
16341669
" how to handle a/ and b/ prefixes in Git diffs for both filename\n"
16351670
" matching (-i/-x) and output (default: keep)\n"
1671+
" --git-extended-diffs=exclude|include\n"
1672+
" process Git diffs without hunks: renames, copies, mode-only\n"
1673+
" changes, binary files; default is include\n"
16361674
" --addprefix=PREFIX\n"
16371675
" prefix pathnames with PREFIX\n"
16381676
" --addoldprefix=PREFIX\n"
@@ -1893,6 +1931,7 @@ int main (int argc, char *argv[])
18931931
{"file", 1, 0, 'f'},
18941932
{"in-place", 0, 0, 1000 + 'w'},
18951933
{"git-prefixes", 1, 0, 1000 + 'G'},
1934+
{"git-extended-diffs", 1, 0, 1000 + 'D'},
18961935
{0, 0, 0, 0}
18971936
};
18981937
char *end;
@@ -2085,6 +2124,15 @@ int main (int argc, char *argv[])
20852124
error(EXIT_FAILURE, 0, "invalid argument to --git-prefixes: %s (expected 'strip' or 'keep')", optarg);
20862125
}
20872126
break;
2127+
case 1000 + 'D':
2128+
if (!strcmp(optarg, "exclude")) {
2129+
git_extended_diffs_mode = GIT_EXTENDED_DIFFS_EXCLUDE;
2130+
} else if (!strcmp(optarg, "include")) {
2131+
git_extended_diffs_mode = GIT_EXTENDED_DIFFS_INCLUDE;
2132+
} else {
2133+
error(EXIT_FAILURE, 0, "invalid argument to --git-extended-diffs: %s (expected 'exclude' or 'include')", optarg);
2134+
}
2135+
break;
20882136
default:
20892137
syntax(1);
20902138
}

src/grep.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ static void syntax(int err)
150150
fprintf(f, " --addoldprefix=PREFIX add PREFIX to old filenames\n");
151151
fprintf(f, " --addnewprefix=PREFIX add PREFIX to new filenames\n");
152152
fprintf(f, " --git-prefixes=strip|keep handle a/ and b/ prefixes in Git diffs (default: keep)\n");
153+
fprintf(f, " --git-extended-diffs=exclude|include\n");
154+
fprintf(f, " process Git diffs without hunks: renames, copies, mode-only\n");
155+
fprintf(f, " changes, binary files; default is include\n");
153156
fprintf(f, " --output-matching=file|hunk output mode: full files or matching hunks only\n");
154157
fprintf(f, " --only-match=rem|add|mod|all show only removed, added, modified, or all matching lines\n");
155158
fprintf(f, " --as-numbered-lines=before|after show matching lines with line numbers\n");

src/ls.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ static void syntax(int err)
9595
fprintf(f, " --addoldprefix=PREFIX add PREFIX to old filenames\n");
9696
fprintf(f, " --addnewprefix=PREFIX add PREFIX to new filenames\n");
9797
fprintf(f, " --git-prefixes=strip|keep handle a/ and b/ prefixes in Git diffs (default: keep)\n");
98+
fprintf(f, " --git-extended-diffs=exclude|include\n");
99+
fprintf(f, " process Git diffs without hunks: renames, copies, mode-only\n");
100+
fprintf(f, " changes, binary files; default is include\n");
98101
fprintf(f, " -i PAT, --include=PAT include only files matching PAT\n");
99102
fprintf(f, " -x PAT, --exclude=PAT exclude files matching PAT\n");
100103
fprintf(f, " -I FILE, --include-from-file=FILE include only files matching patterns in FILE\n");
@@ -159,6 +162,15 @@ static void process_patch_file(FILE *fp, const char *filename)
159162

160163
while ((result = patch_scanner_next(scanner, &content)) == PATCH_SCAN_OK) {
161164
if (content->type == PATCH_CONTENT_HEADERS) {
165+
/* Check if we should skip git extended diffs in exclude mode */
166+
if (git_extended_diffs_mode == GIT_EXTENDED_DIFFS_EXCLUDE) {
167+
enum git_diff_type git_type = content->data.headers->git_type;
168+
/* In exclude mode, skip all extended/special types (only keep GIT_DIFF_NORMAL) */
169+
if (git_type != GIT_DIFF_NORMAL) {
170+
continue;
171+
}
172+
}
173+
162174
filecount++;
163175

164176
/* If we have a pending file, display it now */

src/patch_common.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ int strip_output_components = 0; /* --strip */
4141
int verbose = 0; /* -v, --verbose */
4242
int unzip = 0; /* -z, --decompress */
4343
enum git_prefix_mode git_prefix_mode = GIT_PREFIX_KEEP; /* --git-prefixes */
44+
enum git_extended_diffs_mode git_extended_diffs_mode = GIT_EXTENDED_DIFFS_INCLUDE; /* --git-extended-diffs */
4445

4546
/* Path prefix options */
4647
char *add_prefix = NULL; /* --addprefix */
@@ -174,6 +175,15 @@ int parse_common_option(int c, char *optarg)
174175
case 1000 + 'N':
175176
add_new_prefix = optarg;
176177
return 1;
178+
case 1000 + 'D':
179+
if (!strcmp(optarg, "exclude")) {
180+
git_extended_diffs_mode = GIT_EXTENDED_DIFFS_EXCLUDE;
181+
} else if (!strcmp(optarg, "include")) {
182+
git_extended_diffs_mode = GIT_EXTENDED_DIFFS_INCLUDE;
183+
} else {
184+
error(EXIT_FAILURE, 0, "invalid argument to --git-extended-diffs: %s (expected 'exclude' or 'include')", optarg);
185+
}
186+
return 1;
177187
}
178188

179189
return 0; /* Not handled */
@@ -190,6 +200,7 @@ void init_common_options(void)
190200
verbose = 0;
191201
unzip = 0;
192202
git_prefix_mode = GIT_PREFIX_KEEP;
203+
git_extended_diffs_mode = GIT_EXTENDED_DIFFS_INCLUDE;
193204
add_prefix = NULL;
194205
add_old_prefix = NULL;
195206
add_new_prefix = NULL;
@@ -233,6 +244,7 @@ void add_common_long_options(struct option *options, int *next_index)
233244
options[idx++] = (struct option){"verbose", 0, 0, 'v'};
234245
options[idx++] = (struct option){"decompress", 0, 0, 'z'};
235246
options[idx++] = (struct option){"git-prefixes", 1, 0, 1000 + 'G'};
247+
options[idx++] = (struct option){"git-extended-diffs", 1, 0, 1000 + 'D'};
236248
options[idx++] = (struct option){"strip", 1, 0, 1000 + 'S'};
237249
options[idx++] = (struct option){"addprefix", 1, 0, 1000 + 'A'};
238250
options[idx++] = (struct option){"addoldprefix", 1, 0, 1000 + 'O'};

src/patch_common.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ extern int verbose; /* -v, --verbose */
3232
extern int unzip; /* -z, --decompress */
3333
extern enum git_prefix_mode git_prefix_mode; /* --git-prefixes */
3434

35+
/* Git extended diffs mode */
36+
enum git_extended_diffs_mode {
37+
GIT_EXTENDED_DIFFS_EXCLUDE = 0, /* Skip extended diffs */
38+
GIT_EXTENDED_DIFFS_INCLUDE = 1 /* Process extended diffs (default) */
39+
};
40+
extern enum git_extended_diffs_mode git_extended_diffs_mode; /* --git-extended-diffs */
41+
3542
/* Path prefix options */
3643
extern char *add_prefix; /* --addprefix */
3744
extern char *add_old_prefix; /* --addoldprefix */
@@ -62,7 +69,7 @@ void init_common_options(void);
6269
void cleanup_common_options(void);
6370

6471
/* Common option parsing helpers */
65-
#define MAX_COMMON_OPTIONS 16
72+
#define MAX_COMMON_OPTIONS 20
6673
#define MAX_TOOL_OPTIONS 16 /* Generous space for tool-specific options */
6774
#define MAX_TOTAL_OPTIONS (MAX_COMMON_OPTIONS + MAX_TOOL_OPTIONS)
6875

tests/filterdiff-binary-filtering/run-test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ Binary files /dev/null and b/file2.bin differ
2626
EOF
2727

2828
# Test -F2: should only show file #2 (file1.bin), NOT file2.bin
29-
${FILTERDIFF} -F2 diff 2>errors >output || exit 1
29+
# Note: using --git-extended-diffs=include to test binary file filtering
30+
${FILTERDIFF} --git-extended-diffs=include -F2 diff 2>errors >output || exit 1
3031
[ -s errors ] && exit 1
3132

3233
cat << 'EOF' | cmp - output || exit 1

tests/git-binary-formats/run-test

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ EOF
6262

6363
# Test filtering binary files with GIT binary patch format - should include binary content
6464
echo "Testing GIT binary patch format (include)..."
65-
${FILTERDIFF} --git-prefixes=strip -i "file1.bin" git-binary-patch.patch 2>errors1 >result1 || exit 1
65+
${FILTERDIFF} --git-extended-diffs=include --git-prefixes=strip -i "file1.bin" git-binary-patch.patch 2>errors1 >result1 || exit 1
6666
[ -s errors1 ] && { echo "Unexpected errors in test 1:"; cat errors1; exit 1; }
6767

6868
cat << 'EOF' | cmp - result1 || { echo "Test 1 failed"; exit 1; }
@@ -78,7 +78,7 @@ EOF
7878

7979
# Test filtering binary files with literal format - should include binary content
8080
echo "Testing literal format (include)..."
81-
${FILTERDIFF} --git-prefixes=strip -i "file3.bin" literal-patch.patch 2>errors2 >result2 || exit 1
81+
${FILTERDIFF} --git-extended-diffs=include --git-prefixes=strip -i "file3.bin" literal-patch.patch 2>errors2 >result2 || exit 1
8282
[ -s errors2 ] && { echo "Unexpected errors in test 2:"; cat errors2; exit 1; }
8383

8484
cat << 'EOF' | cmp - result2 || { echo "Test 2 failed"; exit 1; }
@@ -93,7 +93,7 @@ EOF
9393

9494
# Test filtering binary files with delta format - should include binary content
9595
echo "Testing delta format (include)..."
96-
${FILTERDIFF} --git-prefixes=strip -i "file5.bin" delta-patch.patch 2>errors3 >result3 || exit 1
96+
${FILTERDIFF} --git-extended-diffs=include --git-prefixes=strip -i "file5.bin" delta-patch.patch 2>errors3 >result3 || exit 1
9797
[ -s errors3 ] && { echo "Unexpected errors in test 3:"; cat errors3; exit 1; }
9898

9999
cat << 'EOF' | cmp - result3 || { echo "Test 3 failed"; exit 1; }
@@ -108,7 +108,7 @@ EOF
108108

109109
# Test excluding binary files - should skip binary content
110110
echo "Testing binary patch exclusion..."
111-
${FILTERDIFF} --git-prefixes=strip -x "file1.bin" git-binary-patch.patch 2>errors4 >result4 || exit 1
111+
${FILTERDIFF} --git-extended-diffs=include --git-prefixes=strip -x "file1.bin" git-binary-patch.patch 2>errors4 >result4 || exit 1
112112
[ -s errors4 ] && { echo "Unexpected errors in test 4:"; cat errors4; exit 1; }
113113

114114
cat << 'EOF' | cmp - result4 || { echo "Test 4 failed"; exit 1; }

0 commit comments

Comments
 (0)