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).
This commit is contained in:
Linus Torvalds 2005-05-23 14:38:49 -07:00
Родитель 5e224a2ed0
Коммит 46979f567b
1 изменённых файлов: 94 добавлений и 62 удалений

156
apply.c
Просмотреть файл

@ -21,6 +21,8 @@
static int merge_patch = 1;
static const char apply_usage[] = "git-apply <patch>";
static int linenr = 1;
#define CHUNKSIZE (8192)
static void *read_patch_file(int fd, unsigned long *sizep)
@ -77,7 +79,7 @@ static int parse_git_header(char *line, unsigned int size)
{
unsigned long offset, len;
for (offset = 0 ; size > 0 ; offset += len, size -= len, line += len) {
for (offset = 0 ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
len = linelen(line, size);
if (!len)
break;
@ -104,56 +106,6 @@ static int parse_git_header(char *line, unsigned int size)
return offset ? :-1;
}
static int find_header(char *line, unsigned long size, int *hdrsize)
{
unsigned long offset, len;
for (offset = 0; size > 0; offset += len, size -= len, line += len) {
unsigned long nextlen;
len = linelen(line, size);
if (!len)
break;
/* Testing this early allows us to take a few shortcuts.. */
if (len < 6)
continue;
if (size < len + 6)
break;
/*
* Git patch? It might not have a real patch, just a rename
* or mode change, so we handle that specially
*/
if (!memcmp("diff --git ", line, 11)) {
int git_hdr_len = parse_git_header(line + len, size - len);
if (git_hdr_len < 0)
continue;
*hdrsize = len + git_hdr_len;
return offset;
}
/** --- followed by +++ ? */
if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4))
continue;
/*
* We only accept unified patches, so we want it to
* at least have "@@ -a,b +c,d @@\n", which is 14 chars
* minimum
*/
nextlen = linelen(line + len, size - len);
if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
continue;
/* Ok, we'll consider it a patch */
*hdrsize = len + nextlen;
return offset;
}
return -1;
}
static int parse_num(const char *line, int len, int offset, const char *expect, unsigned long *p)
{
char *ptr;
@ -183,6 +135,90 @@ static int parse_num(const char *line, int len, int offset, const char *expect,
return offset + ex;
}
/*
* Parse a unified diff fragment header of the
* form "@@ -a,b +c,d @@"
*/
static int parse_fragment_header(char *line, int len, unsigned long *pos)
{
int offset;
if (!len || line[len-1] != '\n')
return -1;
/* Figure out the number of lines in a fragment */
offset = parse_num(line, len, 4, ",", pos);
offset = parse_num(line, len, offset, " +", pos+1);
offset = parse_num(line, len, offset, ",", pos+2);
offset = parse_num(line, len, offset, " @@", pos+3);
return offset;
}
static int find_header(char *line, unsigned long size, int *hdrsize)
{
unsigned long offset, len;
for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
unsigned long nextlen;
len = linelen(line, size);
if (!len)
break;
/* Testing this early allows us to take a few shortcuts.. */
if (len < 6)
continue;
/*
* Make sure we don't find any unconnected patch fragmants.
* That's a sign that we didn't find a header, and that a
* patch has become corrupted/broken up.
*/
if (!memcmp("@@ -", line, 4)) {
unsigned long pos[4];
if (parse_fragment_header(line, len, pos) < 0)
continue;
error("patch fragment without header at line %d: %.*s", linenr, len-1, line);
}
if (size < len + 6)
break;
/*
* Git patch? It might not have a real patch, just a rename
* or mode change, so we handle that specially
*/
if (!memcmp("diff --git ", line, 11)) {
int git_hdr_len = parse_git_header(line + len, size - len);
if (git_hdr_len < 0)
continue;
*hdrsize = len + git_hdr_len;
return offset;
}
/** --- followed by +++ ? */
if (memcmp("--- ", line, 4) || memcmp("+++ ", line + len, 4))
continue;
/*
* We only accept unified patches, so we want it to
* at least have "@@ -a,b +c,d @@\n", which is 14 chars
* minimum
*/
nextlen = linelen(line + len, size - len);
if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
continue;
/* Ok, we'll consider it a patch */
*hdrsize = len + nextlen;
linenr += 2;
return offset;
}
return -1;
}
/*
* Parse a unified diff. Note that this really needs
* 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,
static int apply_fragment(char *line, unsigned long size)
{
int len = linelen(line, size), offset;
unsigned long oldpos, oldlines, newpos, newlines;
unsigned long pos[4], oldlines, newlines;
if (!len || line[len-1] != '\n')
return -1;
/* Figure out the number of lines in a fragment */
offset = parse_num(line, len, 4, ",", &oldpos);
offset = parse_num(line, len, offset, " +", &oldlines);
offset = parse_num(line, len, offset, ",", &newpos);
offset = parse_num(line, len, offset, " @@", &newlines);
offset = parse_fragment_header(line, len, pos);
if (offset < 0)
return -1;
oldlines = pos[1];
newlines = pos[3];
/* Parse the thing.. */
line += len;
size -= len;
for (offset = len; size > 0; offset += len, size -= len, line += len) {
linenr++;
for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
if (!oldlines && !newlines)
break;
len = linelen(line, size);
@ -240,7 +272,7 @@ static int apply_single_patch(char *line, unsigned long size)
while (size > 4 && !memcmp(line, "@@ -", 4)) {
int len = apply_fragment(line, size);
if (len <= 0)
die("corrupt patch");
die("corrupt patch at line %d", linenr);
printf("applying fragment:\n%.*s\n\n", len, line);