Skip to content

Commit 46979f5

Browse files
author
Linus Torvalds
committed
git-apply: improve error detection and messages
In particular, give line numbers when detecting corrupt patches. This makes the tool a lot more friendly (indeed, much more so than regular "patch", I think).
1 parent 5e224a2 commit 46979f5

File tree

1 file changed

+74
-42
lines changed

1 file changed

+74
-42
lines changed

apply.c

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
static int merge_patch = 1;
2222
static const char apply_usage[] = "git-apply <patch>";
2323

24+
static int linenr = 1;
25+
2426
#define CHUNKSIZE (8192)
2527

2628
static void *read_patch_file(int fd, unsigned long *sizep)
@@ -77,7 +79,7 @@ static int parse_git_header(char *line, unsigned int size)
7779
{
7880
unsigned long offset, len;
7981

80-
for (offset = 0 ; size > 0 ; offset += len, size -= len, line += len) {
82+
for (offset = 0 ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
8183
len = linelen(line, size);
8284
if (!len)
8385
break;
@@ -104,11 +106,60 @@ static int parse_git_header(char *line, unsigned int size)
104106
return offset ? :-1;
105107
}
106108

109+
static int parse_num(const char *line, int len, int offset, const char *expect, unsigned long *p)
110+
{
111+
char *ptr;
112+
int digits, ex;
113+
114+
if (offset < 0 || offset >= len)
115+
return -1;
116+
line += offset;
117+
len -= offset;
118+
119+
if (!isdigit(*line))
120+
return -1;
121+
*p = strtoul(line, &ptr, 10);
122+
123+
digits = ptr - line;
124+
125+
offset += digits;
126+
line += digits;
127+
len -= digits;
128+
129+
ex = strlen(expect);
130+
if (ex > len)
131+
return -1;
132+
if (memcmp(line, expect, ex))
133+
return -1;
134+
135+
return offset + ex;
136+
}
137+
138+
/*
139+
* Parse a unified diff fragment header of the
140+
* form "@@ -a,b +c,d @@"
141+
*/
142+
static int parse_fragment_header(char *line, int len, unsigned long *pos)
143+
{
144+
int offset;
145+
146+
if (!len || line[len-1] != '\n')
147+
return -1;
148+
149+
/* Figure out the number of lines in a fragment */
150+
offset = parse_num(line, len, 4, ",", pos);
151+
offset = parse_num(line, len, offset, " +", pos+1);
152+
offset = parse_num(line, len, offset, ",", pos+2);
153+
offset = parse_num(line, len, offset, " @@", pos+3);
154+
155+
return offset;
156+
}
157+
107158
static int find_header(char *line, unsigned long size, int *hdrsize)
108159
{
109160
unsigned long offset, len;
110161

111-
for (offset = 0; size > 0; offset += len, size -= len, line += len) {
162+
for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
112163
unsigned long nextlen;
113164

114165
len = linelen(line, size);
@@ -118,6 +169,19 @@ static int find_header(char *line, unsigned long size, int *hdrsize)
118169
/* Testing this early allows us to take a few shortcuts.. */
119170
if (len < 6)
120171
continue;
172+
173+
/*
174+
* Make sure we don't find any unconnected patch fragmants.
175+
* That's a sign that we didn't find a header, and that a
176+
* patch has become corrupted/broken up.
177+
*/
178+
if (!memcmp("@@ -", line, 4)) {
179+
unsigned long pos[4];
180+
if (parse_fragment_header(line, len, pos) < 0)
181+
continue;
182+
error("patch fragment without header at line %d: %.*s", linenr, len-1, line);
183+
}
184+
121185
if (size < len + 6)
122186
break;
123187

@@ -149,40 +213,12 @@ static int find_header(char *line, unsigned long size, int *hdrsize)
149213

150214
/* Ok, we'll consider it a patch */
151215
*hdrsize = len + nextlen;
216+
linenr += 2;
152217
return offset;
153218
}
154219
return -1;
155220
}
156221

157-
static int parse_num(const char *line, int len, int offset, const char *expect, unsigned long *p)
158-
{
159-
char *ptr;
160-
int digits, ex;
161-
162-
if (offset < 0 || offset >= len)
163-
return -1;
164-
line += offset;
165-
len -= offset;
166-
167-
if (!isdigit(*line))
168-
return -1;
169-
*p = strtoul(line, &ptr, 10);
170-
171-
digits = ptr - line;
172-
173-
offset += digits;
174-
line += digits;
175-
len -= digits;
176-
177-
ex = strlen(expect);
178-
if (ex > len)
179-
return -1;
180-
if (memcmp(line, expect, ex))
181-
return -1;
182-
183-
return offset + ex;
184-
}
185-
186222
/*
187223
* Parse a unified diff. Note that this really needs
188224
* to parse each fragment separately, since the only
@@ -193,23 +229,19 @@ static int parse_num(const char *line, int len, int offset, const char *expect,
193229
static int apply_fragment(char *line, unsigned long size)
194230
{
195231
int len = linelen(line, size), offset;
196-
unsigned long oldpos, oldlines, newpos, newlines;
232+
unsigned long pos[4], oldlines, newlines;
197233

198-
if (!len || line[len-1] != '\n')
199-
return -1;
200-
201-
/* Figure out the number of lines in a fragment */
202-
offset = parse_num(line, len, 4, ",", &oldpos);
203-
offset = parse_num(line, len, offset, " +", &oldlines);
204-
offset = parse_num(line, len, offset, ",", &newpos);
205-
offset = parse_num(line, len, offset, " @@", &newlines);
234+
offset = parse_fragment_header(line, len, pos);
206235
if (offset < 0)
207236
return -1;
237+
oldlines = pos[1];
238+
newlines = pos[3];
208239

209240
/* Parse the thing.. */
210241
line += len;
211242
size -= len;
212-
for (offset = len; size > 0; offset += len, size -= len, line += len) {
243+
linenr++;
244+
for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
213245
if (!oldlines && !newlines)
214246
break;
215247
len = linelen(line, size);
@@ -240,7 +272,7 @@ static int apply_single_patch(char *line, unsigned long size)
240272
while (size > 4 && !memcmp(line, "@@ -", 4)) {
241273
int len = apply_fragment(line, size);
242274
if (len <= 0)
243-
die("corrupt patch");
275+
die("corrupt patch at line %d", linenr);
244276

245277
printf("applying fragment:\n%.*s\n\n", len, line);
246278

0 commit comments

Comments
 (0)