Skip to content

Commit 5032e70

Browse files
committed
Merge branch 'jw/apply-corrupt-location'
"git apply" now reports the name of the input file along with the line number when it encounters a corrupt patch, and correctly resets the line counter when processing multiple patch files. * jw/apply-corrupt-location: apply: report input location in binary and garbage patch errors apply: report input location in header parsing errors apply: report the location of corrupt patches
2 parents 295eb2c + 2ef795b commit 5032e70

7 files changed

Lines changed: 185 additions & 33 deletions

File tree

apply.c

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
struct gitdiff_data {
4444
struct strbuf *root;
45+
const char *patch_input_file;
4546
int linenr;
4647
int p_value;
4748
};
@@ -900,7 +901,8 @@ static int parse_traditional_patch(struct apply_state *state,
900901
}
901902
}
902903
if (!name)
903-
return error(_("unable to find filename in patch at line %d"), state->linenr);
904+
return error(_("unable to find filename in patch at %s:%d"),
905+
state->patch_input_file, state->linenr);
904906

905907
return 0;
906908
}
@@ -937,20 +939,35 @@ static int gitdiff_verify_name(struct gitdiff_data *state,
937939

938940
if (*name) {
939941
char *another;
940-
if (isnull)
942+
if (isnull) {
943+
if (state->patch_input_file)
944+
return error(_("git apply: bad git-diff - expected /dev/null, got %s at %s:%d"),
945+
*name, state->patch_input_file, state->linenr);
941946
return error(_("git apply: bad git-diff - expected /dev/null, got %s on line %d"),
942947
*name, state->linenr);
948+
}
943949
another = find_name(state->root, line, NULL, state->p_value, TERM_TAB);
944950
if (!another || strcmp(another, *name)) {
945951
free(another);
952+
if (state->patch_input_file)
953+
return error((side == DIFF_NEW_NAME) ?
954+
_("git apply: bad git-diff - inconsistent new filename at %s:%d") :
955+
_("git apply: bad git-diff - inconsistent old filename at %s:%d"),
956+
state->patch_input_file, state->linenr);
946957
return error((side == DIFF_NEW_NAME) ?
947-
_("git apply: bad git-diff - inconsistent new filename on line %d") :
948-
_("git apply: bad git-diff - inconsistent old filename on line %d"), state->linenr);
958+
_("git apply: bad git-diff - inconsistent new filename on line %d") :
959+
_("git apply: bad git-diff - inconsistent old filename on line %d"),
960+
state->linenr);
949961
}
950962
free(another);
951963
} else {
952-
if (!is_dev_null(line))
953-
return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
964+
if (!is_dev_null(line)) {
965+
if (state->patch_input_file)
966+
return error(_("git apply: bad git-diff - expected /dev/null at %s:%d"),
967+
state->patch_input_file, state->linenr);
968+
return error(_("git apply: bad git-diff - expected /dev/null on line %d"),
969+
state->linenr);
970+
}
954971
}
955972

956973
return 0;
@@ -974,12 +991,19 @@ static int gitdiff_newname(struct gitdiff_data *state,
974991
DIFF_NEW_NAME);
975992
}
976993

977-
static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
994+
static int parse_mode_line(const char *line,
995+
const char *patch_input_file,
996+
int linenr,
997+
unsigned int *mode)
978998
{
979999
char *end;
9801000
*mode = strtoul(line, &end, 8);
981-
if (end == line || !isspace(*end))
1001+
if (end == line || !isspace(*end)) {
1002+
if (patch_input_file)
1003+
return error(_("invalid mode at %s:%d: %s"),
1004+
patch_input_file, linenr, line);
9821005
return error(_("invalid mode on line %d: %s"), linenr, line);
1006+
}
9831007
*mode = canon_mode(*mode);
9841008
return 0;
9851009
}
@@ -988,14 +1012,16 @@ static int gitdiff_oldmode(struct gitdiff_data *state,
9881012
const char *line,
9891013
struct patch *patch)
9901014
{
991-
return parse_mode_line(line, state->linenr, &patch->old_mode);
1015+
return parse_mode_line(line, state->patch_input_file, state->linenr,
1016+
&patch->old_mode);
9921017
}
9931018

9941019
static int gitdiff_newmode(struct gitdiff_data *state,
9951020
const char *line,
9961021
struct patch *patch)
9971022
{
998-
return parse_mode_line(line, state->linenr, &patch->new_mode);
1023+
return parse_mode_line(line, state->patch_input_file, state->linenr,
1024+
&patch->new_mode);
9991025
}
10001026

10011027
static int gitdiff_delete(struct gitdiff_data *state,
@@ -1314,6 +1340,7 @@ static int check_header_line(int linenr, struct patch *patch)
13141340
}
13151341

13161342
int parse_git_diff_header(struct strbuf *root,
1343+
const char *patch_input_file,
13171344
int *linenr,
13181345
int p_value,
13191346
const char *line,
@@ -1345,6 +1372,7 @@ int parse_git_diff_header(struct strbuf *root,
13451372
size -= len;
13461373
(*linenr)++;
13471374
parse_hdr_state.root = root;
1375+
parse_hdr_state.patch_input_file = patch_input_file;
13481376
parse_hdr_state.linenr = *linenr;
13491377
parse_hdr_state.p_value = p_value;
13501378

@@ -1382,6 +1410,7 @@ int parse_git_diff_header(struct strbuf *root,
13821410
int res;
13831411
if (len < oplen || memcmp(p->str, line, oplen))
13841412
continue;
1413+
parse_hdr_state.linenr = *linenr;
13851414
res = p->fn(&parse_hdr_state, line + oplen, patch);
13861415
if (res < 0)
13871416
return -1;
@@ -1396,21 +1425,33 @@ int parse_git_diff_header(struct strbuf *root,
13961425
done:
13971426
if (!patch->old_name && !patch->new_name) {
13981427
if (!patch->def_name) {
1399-
error(Q_("git diff header lacks filename information when removing "
1400-
"%d leading pathname component (line %d)",
1401-
"git diff header lacks filename information when removing "
1402-
"%d leading pathname components (line %d)",
1403-
parse_hdr_state.p_value),
1404-
parse_hdr_state.p_value, *linenr);
1428+
if (patch_input_file)
1429+
error(Q_("git diff header lacks filename information when removing "
1430+
"%d leading pathname component at %s:%d",
1431+
"git diff header lacks filename information when removing "
1432+
"%d leading pathname components at %s:%d",
1433+
parse_hdr_state.p_value),
1434+
parse_hdr_state.p_value, patch_input_file, *linenr);
1435+
else
1436+
error(Q_("git diff header lacks filename information when removing "
1437+
"%d leading pathname component (line %d)",
1438+
"git diff header lacks filename information when removing "
1439+
"%d leading pathname components (line %d)",
1440+
parse_hdr_state.p_value),
1441+
parse_hdr_state.p_value, *linenr);
14051442
return -128;
14061443
}
14071444
patch->old_name = xstrdup(patch->def_name);
14081445
patch->new_name = xstrdup(patch->def_name);
14091446
}
14101447
if ((!patch->new_name && !patch->is_delete) ||
14111448
(!patch->old_name && !patch->is_new)) {
1412-
error(_("git diff header lacks filename information "
1413-
"(line %d)"), *linenr);
1449+
if (patch_input_file)
1450+
error(_("git diff header lacks filename information at %s:%d"),
1451+
patch_input_file, *linenr);
1452+
else
1453+
error(_("git diff header lacks filename information (line %d)"),
1454+
*linenr);
14141455
return -128;
14151456
}
14161457
patch->is_toplevel_relative = 1;
@@ -1577,8 +1618,9 @@ static int find_header(struct apply_state *state,
15771618
struct fragment dummy;
15781619
if (parse_fragment_header(line, len, &dummy) < 0)
15791620
continue;
1580-
error(_("patch fragment without header at line %d: %.*s"),
1581-
state->linenr, (int)len-1, line);
1621+
error(_("patch fragment without header at %s:%d: %.*s"),
1622+
state->patch_input_file, state->linenr,
1623+
(int)len-1, line);
15821624
return -128;
15831625
}
15841626

@@ -1590,7 +1632,9 @@ static int find_header(struct apply_state *state,
15901632
* or mode change, so we handle that specially
15911633
*/
15921634
if (!memcmp("diff --git ", line, 11)) {
1593-
int git_hdr_len = parse_git_diff_header(&state->root, &state->linenr,
1635+
int git_hdr_len = parse_git_diff_header(&state->root,
1636+
state->patch_input_file,
1637+
&state->linenr,
15941638
state->p_value, line, len,
15951639
size, patch);
15961640
if (git_hdr_len < 0)
@@ -1875,7 +1919,8 @@ static int parse_single_patch(struct apply_state *state,
18751919
len = parse_fragment(state, line, size, patch, fragment);
18761920
if (len <= 0) {
18771921
free(fragment);
1878-
return error(_("corrupt patch at line %d"), state->linenr);
1922+
return error(_("corrupt patch at %s:%d"),
1923+
state->patch_input_file, state->linenr);
18791924
}
18801925
fragment->patch = line;
18811926
fragment->size = len;
@@ -2065,8 +2110,8 @@ static struct fragment *parse_binary_hunk(struct apply_state *state,
20652110
corrupt:
20662111
free(data);
20672112
*status_p = -1;
2068-
error(_("corrupt binary patch at line %d: %.*s"),
2069-
state->linenr-1, llen-1, buffer);
2113+
error(_("corrupt binary patch at %s:%d: %.*s"),
2114+
state->patch_input_file, state->linenr-1, llen-1, buffer);
20702115
return NULL;
20712116
}
20722117

@@ -2102,7 +2147,8 @@ static int parse_binary(struct apply_state *state,
21022147
forward = parse_binary_hunk(state, &buffer, &size, &status, &used);
21032148
if (!forward && !status)
21042149
/* there has to be one hunk (forward hunk) */
2105-
return error(_("unrecognized binary patch at line %d"), state->linenr-1);
2150+
return error(_("unrecognized binary patch at %s:%d"),
2151+
state->patch_input_file, state->linenr-1);
21062152
if (status)
21072153
/* otherwise we already gave an error message */
21082154
return status;
@@ -2264,7 +2310,8 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si
22642310
*/
22652311
if ((state->apply || state->check) &&
22662312
(!patch->is_binary && !metadata_changes(patch))) {
2267-
error(_("patch with only garbage at line %d"), state->linenr);
2313+
error(_("patch with only garbage at %s:%d"),
2314+
state->patch_input_file, state->linenr);
22682315
return -128;
22692316
}
22702317
}
@@ -4825,6 +4872,7 @@ static int apply_patch(struct apply_state *state,
48254872
int flush_attributes = 0;
48264873

48274874
state->patch_input_file = filename;
4875+
state->linenr = 1;
48284876
if (read_patch_file(&buf, fd) < 0)
48294877
return -128;
48304878
offset = 0;

apply.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ int check_apply_state(struct apply_state *state, int force_apply);
167167
* Returns -1 on failure, the length of the parsed header otherwise.
168168
*/
169169
int parse_git_diff_header(struct strbuf *root,
170+
const char *patch_input_file,
170171
int *linenr,
171172
int p_value,
172173
const char *line,

range-diff.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ static int read_patches(const char *range, struct string_list *list,
140140
if (eol)
141141
*eol = '\n';
142142
orig_len = len;
143-
len = parse_git_diff_header(&root, &linenr, 0, line,
143+
len = parse_git_diff_header(&root, NULL, &linenr, 0, line,
144144
len, size, &patch);
145145
if (len < 0) {
146146
error(_("could not parse git header '%.*s'"),

t/t4012-diff-binary.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ test_expect_success 'apply detecting corrupt patch correctly' '
6868
sed -e "s/-CIT/xCIT/" <output >broken &&
6969
test_must_fail git apply --stat --summary broken 2>detected &&
7070
detected=$(cat detected) &&
71-
detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") &&
71+
detected=$(expr "$detected" : "error.*broken:\\([0-9]*\\)\$") &&
7272
detected=$(sed -ne "${detected}p" broken) &&
7373
test "$detected" = xCIT
7474
'
@@ -77,7 +77,7 @@ test_expect_success 'apply detecting corrupt patch correctly' '
7777
git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
7878
test_must_fail git apply --stat --summary broken 2>detected &&
7979
detected=$(cat detected) &&
80-
detected=$(expr "$detected" : "error.*at line \\([0-9]*\\)\$") &&
80+
detected=$(expr "$detected" : "error.*broken:\\([0-9]*\\)\$") &&
8181
detected=$(sed -ne "${detected}p" broken) &&
8282
test "$detected" = xCIT
8383
'

t/t4100-apply-stat.sh

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,93 @@ test_expect_success 'applying a hunk header which overflows fails' '
4848
+b
4949
EOF
5050
test_must_fail git apply patch 2>err &&
51-
echo "error: corrupt patch at line 4" >expect &&
51+
echo "error: corrupt patch at patch:4" >expect &&
52+
test_cmp expect err
53+
'
54+
55+
test_expect_success 'applying a hunk header which overflows from stdin fails' '
56+
cat >patch <<-\EOF &&
57+
diff -u a/file b/file
58+
--- a/file
59+
+++ b/file
60+
@@ -98765432109876543210 +98765432109876543210 @@
61+
-a
62+
+b
63+
EOF
64+
test_must_fail git apply <patch 2>err &&
65+
echo "error: corrupt patch at <stdin>:4" >expect &&
66+
test_cmp expect err
67+
'
68+
69+
test_expect_success 'applying multiple patches reports the corrupted input' '
70+
cat >good.patch <<-\EOF &&
71+
diff -u a/file b/file
72+
--- a/file
73+
+++ b/file
74+
@@ -1 +1 @@
75+
-a
76+
+b
77+
EOF
78+
cat >bad.patch <<-\EOF &&
79+
diff -u a/file b/file
80+
--- a/file
81+
+++ b/file
82+
@@ -98765432109876543210 +98765432109876543210 @@
83+
-a
84+
+b
85+
EOF
86+
test_must_fail git apply --stat --summary good.patch bad.patch 2>err &&
87+
echo "error: corrupt patch at bad.patch:4" >expect &&
88+
test_cmp expect err
89+
'
90+
91+
test_expect_success 'applying a patch without a header reports the input' '
92+
cat >fragment.patch <<-\EOF &&
93+
@@ -1 +1 @@
94+
-a
95+
+b
96+
EOF
97+
test_must_fail git apply fragment.patch 2>err &&
98+
echo "error: patch fragment without header at fragment.patch:1: @@ -1 +1 @@" >expect &&
99+
test_cmp expect err
100+
'
101+
102+
test_expect_success 'applying a patch with a missing filename reports the input' '
103+
cat >missing.patch <<-\EOF &&
104+
diff --git a/f b/f
105+
index 7898192..6178079 100644
106+
--- a/f
107+
@@ -1 +1 @@
108+
-a
109+
+b
110+
EOF
111+
test_must_fail git apply missing.patch 2>err &&
112+
echo "error: git diff header lacks filename information at missing.patch:4" >expect &&
113+
test_cmp expect err
114+
'
115+
116+
test_expect_success 'applying a patch with an invalid mode reports the input' '
117+
cat >mode.patch <<-\EOF &&
118+
diff --git a/f b/f
119+
old mode 10x644
120+
EOF
121+
test_must_fail git apply mode.patch 2>err &&
122+
cat >expect <<-\EOF &&
123+
error: invalid mode at mode.patch:2: 10x644
124+
125+
EOF
126+
test_cmp expect err
127+
'
128+
129+
test_expect_success 'applying a patch with only garbage reports the input' '
130+
cat >garbage.patch <<-\EOF &&
131+
diff --git a/f b/f
132+
--- a/f
133+
+++ b/f
134+
this is garbage
135+
EOF
136+
test_must_fail git apply garbage.patch 2>err &&
137+
echo "error: patch with only garbage at garbage.patch:4" >expect &&
52138
test_cmp expect err
53139
'
54140
test_done

t/t4103-apply-binary.sh

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,24 @@ test_expect_success PERL_TEST_HELPERS 'reject truncated binary diff' '
179179
" <patch >patch.trunc &&
180180
181181
do_reset &&
182-
test_must_fail git apply patch.trunc
182+
test_must_fail git apply patch.trunc 2>err &&
183+
line=$(awk "END { print NR + 1 }" patch.trunc) &&
184+
grep "error: corrupt binary patch at patch.trunc:$line: " err
185+
'
186+
187+
test_expect_success 'reject unrecognized binary diff' '
188+
cat >patch.bad <<-\EOF &&
189+
diff --git a/f b/f
190+
new file mode 100644
191+
index 0000000..7898192
192+
GIT binary patch
193+
bogus
194+
EOF
195+
test_must_fail git apply patch.bad 2>err &&
196+
cat >expect <<-\EOF &&
197+
error: unrecognized binary patch at patch.bad:4
198+
error: No valid patches in input (allow with "--allow-empty")
199+
EOF
200+
test_cmp expect err
183201
'
184202
test_done

0 commit comments

Comments
 (0)