Skip to content

Commit a4acb0e

Browse files
author
Linus Torvalds
committed
git-apply: parse the diff headers (both traditional and new)
.. and print out the information. This sets up all the pathname information, and whether it's a new file, deleted file, rename, copy or whatever. It's slowly getting to the point where it all comes together, and we can actually apply all the information that we've gathered.
1 parent 46979f5 commit a4acb0e

File tree

1 file changed

+217
-31
lines changed

1 file changed

+217
-31
lines changed

apply.c

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

24+
/*
25+
* Various "current state", notably line numbers and what
26+
* file (and how) we're patching right now.. The "is_xxxx"
27+
* things are flags, where -1 means "don't know yet".
28+
*/
2429
static int linenr = 1;
30+
static int old_mode, new_mode;
31+
static char *old_name, *new_name, *def_name;
32+
static int is_rename, is_copy, is_new, is_delete;
2533

2634
#define CHUNKSIZE (8192)
35+
#define SLOP (16)
2736

2837
static void *read_patch_file(int fd, unsigned long *sizep)
2938
{
@@ -48,6 +57,15 @@ static void *read_patch_file(int fd, unsigned long *sizep)
4857
size += nr;
4958
}
5059
*sizep = size;
60+
61+
/*
62+
* Make sure that we have some slop in the buffer
63+
* so that we can do speculative "memcmp" etc, and
64+
* see to it that it is NUL-filled.
65+
*/
66+
if (alloc < size + SLOP)
67+
buffer = xrealloc(buffer, size + SLOP);
68+
memset(buffer + size, 0, SLOP);
5169
return buffer;
5270
}
5371

@@ -62,48 +80,200 @@ static unsigned long linelen(char *buffer, unsigned long size)
6280
return len;
6381
}
6482

65-
static int match_word(const char *line, const char *match)
83+
static int is_dev_null(const char *str)
84+
{
85+
return !memcmp("/dev/null", str, 9) && isspace(str[9]);
86+
}
87+
88+
static char * find_name(const char *line, char *def, int p_value)
6689
{
90+
int len;
91+
const char *start = line;
92+
char *name;
93+
6794
for (;;) {
68-
char c = *match++;
69-
if (!c)
95+
char c = *line;
96+
if (isspace(c))
7097
break;
71-
if (*line++ != c)
72-
return 0;
98+
line++;
99+
if (c == '/' && !--p_value)
100+
start = line;
101+
}
102+
if (!start)
103+
return def;
104+
len = line - start;
105+
if (!len)
106+
return def;
107+
108+
/*
109+
* Generally we prefer the shorter name, especially
110+
* if the other one is just a variation of that with
111+
* something else tacked on to the end (ie "file.orig"
112+
* or "file~").
113+
*/
114+
if (def) {
115+
int deflen = strlen(def);
116+
if (deflen < len && !strncmp(start, def, deflen))
117+
return def;
73118
}
74-
return *line == ' ';
119+
120+
name = xmalloc(len + 1);
121+
memcpy(name, start, len);
122+
name[len] = 0;
123+
free(def);
124+
return name;
125+
}
126+
127+
/*
128+
* Get the name etc info from the --/+++ lines of a traditional patch header
129+
*
130+
* NOTE! This hardcodes "-p1" behaviour in filename detection.
131+
*/
132+
static int parse_traditional_patch(const char *first, const char *second)
133+
{
134+
int p_value = 1;
135+
char *name;
136+
137+
first += 4; // skip "--- "
138+
second += 4; // skip "+++ "
139+
if (is_dev_null(first)) {
140+
is_new = 1;
141+
name = find_name(second, def_name, p_value);
142+
} else if (is_dev_null(second)) {
143+
is_delete = 1;
144+
name = find_name(first, def_name, p_value);
145+
} else {
146+
name = find_name(first, def_name, p_value);
147+
name = find_name(second, name, p_value);
148+
}
149+
if (!name)
150+
die("unable to find filename in patch at line %d", linenr);
151+
old_name = name;
152+
new_name = name;
153+
}
154+
155+
static int gitdiff_hdrend(const char *line)
156+
{
157+
return -1;
158+
}
159+
160+
static int gitdiff_oldname(const char *line)
161+
{
162+
if (!old_name)
163+
old_name = find_name(line, NULL, 1);
164+
return 0;
165+
}
166+
167+
static int gitdiff_newname(const char *line)
168+
{
169+
if (!new_name)
170+
new_name = find_name(line, NULL, 1);
171+
return 0;
172+
}
173+
174+
static int gitdiff_oldmode(const char *line)
175+
{
176+
old_mode = strtoul(line, NULL, 8);
177+
return 0;
178+
}
179+
180+
static int gitdiff_newmode(const char *line)
181+
{
182+
new_mode = strtoul(line, NULL, 8);
183+
return 0;
184+
}
185+
186+
static int gitdiff_delete(const char *line)
187+
{
188+
is_delete = 1;
189+
return gitdiff_oldmode(line);
190+
}
191+
192+
static int gitdiff_newfile(const char *line)
193+
{
194+
is_new = 1;
195+
return gitdiff_newmode(line);
196+
}
197+
198+
static int gitdiff_copysrc(const char *line)
199+
{
200+
is_copy = 1;
201+
old_name = find_name(line, NULL, 0);
202+
return 0;
203+
}
204+
205+
static int gitdiff_copydst(const char *line)
206+
{
207+
is_copy = 1;
208+
new_name = find_name(line, NULL, 0);
209+
return 0;
210+
}
211+
212+
static int gitdiff_renamesrc(const char *line)
213+
{
214+
is_rename = 1;
215+
old_name = find_name(line, NULL, 0);
216+
return 0;
217+
}
218+
219+
static int gitdiff_renamedst(const char *line)
220+
{
221+
is_rename = 1;
222+
new_name = find_name(line, NULL, 0);
223+
return 0;
224+
}
225+
226+
static int gitdiff_similarity(const char *line)
227+
{
228+
return 0;
75229
}
76230

77231
/* Verify that we recognize the lines following a git header */
78-
static int parse_git_header(char *line, unsigned int size)
232+
static int parse_git_header(char *line, int len, unsigned int size)
79233
{
80-
unsigned long offset, len;
234+
unsigned long offset;
235+
236+
/* A git diff has explicit new/delete information, so we don't guess */
237+
is_new = 0;
238+
is_delete = 0;
239+
240+
line += len;
241+
size -= len;
242+
linenr++;
243+
for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
244+
static const struct opentry {
245+
const char *str;
246+
int (*fn)(const char *);
247+
} optable[] = {
248+
{ "@@ -", gitdiff_hdrend },
249+
{ "--- ", gitdiff_oldname },
250+
{ "+++ ", gitdiff_newname },
251+
{ "old mode ", gitdiff_oldmode },
252+
{ "new mode ", gitdiff_newmode },
253+
{ "deleted file mode ", gitdiff_delete },
254+
{ "new file mode ", gitdiff_newfile },
255+
{ "copy from ", gitdiff_copysrc },
256+
{ "copy to ", gitdiff_copydst },
257+
{ "rename from ", gitdiff_renamesrc },
258+
{ "rename to ", gitdiff_renamedst },
259+
{ "similarity index ", gitdiff_similarity },
260+
};
261+
int i;
81262

82-
for (offset = 0 ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
83263
len = linelen(line, size);
84-
if (!len)
85-
break;
86-
if (line[len-1] != '\n')
87-
return -1;
88-
if (len < 4)
264+
if (!len || line[len-1] != '\n')
89265
break;
90-
if (!memcmp(line, "@@ -", 4))
91-
return offset;
92-
if (match_word(line, "new file mode"))
93-
continue;
94-
if (match_word(line, "deleted file mode"))
95-
continue;
96-
if (match_word(line, "copy"))
97-
continue;
98-
if (match_word(line, "rename"))
99-
continue;
100-
if (match_word(line, "similarity index"))
101-
continue;
102-
break;
266+
for (i = 0; i < sizeof(optable) / sizeof(optable[0]); i++) {
267+
const struct opentry *p = optable + i;
268+
int oplen = strlen(p->str);
269+
if (len < oplen || memcmp(p->str, line, oplen))
270+
continue;
271+
if (p->fn(line + oplen) < 0)
272+
return offset;
273+
}
103274
}
104275

105-
/* We want either a patch _or_ something real */
106-
return offset ? :-1;
276+
return offset;
107277
}
108278

109279
static int parse_num(const char *line, int len, int offset, const char *expect, unsigned long *p)
@@ -159,6 +329,10 @@ static int find_header(char *line, unsigned long size, int *hdrsize)
159329
{
160330
unsigned long offset, len;
161331

332+
is_rename = is_copy = 0;
333+
is_new = is_delete = -1;
334+
old_mode = new_mode = -1;
335+
def_name = old_name = new_name = NULL;
162336
for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
163337
unsigned long nextlen;
164338

@@ -190,11 +364,11 @@ static int find_header(char *line, unsigned long size, int *hdrsize)
190364
* or mode change, so we handle that specially
191365
*/
192366
if (!memcmp("diff --git ", line, 11)) {
193-
int git_hdr_len = parse_git_header(line + len, size - len);
367+
int git_hdr_len = parse_git_header(line, len, size);
194368
if (git_hdr_len < 0)
195369
continue;
196370

197-
*hdrsize = len + git_hdr_len;
371+
*hdrsize = git_hdr_len;
198372
return offset;
199373
}
200374

@@ -212,6 +386,7 @@ static int find_header(char *line, unsigned long size, int *hdrsize)
212386
continue;
213387

214388
/* Ok, we'll consider it a patch */
389+
parse_traditional_patch(line, line+len);
215390
*hdrsize = len + nextlen;
216391
linenr += 2;
217392
return offset;
@@ -237,6 +412,11 @@ static int apply_fragment(char *line, unsigned long size)
237412
oldlines = pos[1];
238413
newlines = pos[3];
239414

415+
if (is_new < 0 && (pos[0] || oldlines))
416+
is_new = 0;
417+
if (is_delete < 0 && (pos[1] || newlines))
418+
is_delete = 0;
419+
240420
/* Parse the thing.. */
241421
line += len;
242422
size -= len;
@@ -294,6 +474,12 @@ static int apply_chunk(char *buffer, unsigned long size)
294474
header = buffer + offset;
295475

296476
printf("Found header:\n%.*s\n\n", hdrsize, header);
477+
printf("Rename: %d\n", is_rename);
478+
printf("Copy: %d\n", is_copy);
479+
printf("New: %d\n", is_new);
480+
printf("Delete: %d\n", is_delete);
481+
printf("Mode: %o->%o\n", old_mode, new_mode);
482+
printf("Name: '%s'->'%s'\n", old_name, new_name);
297483

298484
patch = header + hdrsize;
299485
patchsize = apply_single_patch(patch, size - offset - hdrsize);

0 commit comments

Comments
 (0)