Skip to content

Commit 4749588

Browse files
ebiedermJunio C Hamano
authored andcommitted
Implement limited context matching in git-apply.
Ok this really should be the good version. The option handling has been reworked to be automation safe. Currently to import the -mm tree I have to work around git-apply by using patch. Because some of Andrews patches in quilt will only apply with fuzz. I started out implementing a --fuzz option and then I realized fuzz is not a very safe concept for an automated system. What you really want is a minimum number of context lines that must match. This allows policy to be set without knowing how many lines of context a patch actually provides. By default the policy remains to match all provided lines of context. Allowng git-apply to match a restricted set of context makes it much easier to import the -mm tree into git. I am still only processing 1.5 to 1.6 patches a second for the 692 patches in 2.6.17-rc1-mm2 is still painful but it does help. If I just loop through all of Andrews patches in order and run git-apply --index -C1 I process the entire patchset in 1m53s or about 6 patches per second. So running git-mailinfo, git-write-tree, git-commit-tree, and git-update-ref everytime has a measurable impact, and shows things can be speeded up even more. All of these timings were taking on my poor 700Mhz Athlon with 512MB of ram. So people with fast machiens should see much better performance. When a match is found after the number of context are reduced a warning is generated. Since this is a rare event and possibly dangerous this seems to make sense. Unless you are patching a single file the error message is a little bit terse at the moment, but it should be easy to go back and fix. I have also updated the documentation for git-apply to reflect the new -C option that sets the minimum number of context lines that must match. Signed-off-by: Eric W. Biederman <ebiederm@xmission.com> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 944e3a8 commit 4749588

File tree

2 files changed

+112
-19
lines changed

2 files changed

+112
-19
lines changed

Documentation/git-apply.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ SYNOPSIS
1111
[verse]
1212
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply]
1313
[--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM]
14-
[--whitespace=<nowarn|warn|error|error-all|strip>]
14+
[-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>]
1515
[<patch>...]
1616

1717
DESCRIPTION
@@ -73,6 +73,12 @@ OPTIONS
7373
Remove <n> leading slashes from traditional diff paths. The
7474
default is 1.
7575

76+
-C<n>::
77+
Ensure at least <n> lines of surrounding context match before
78+
and after each change. When fewer lines of surrounding
79+
context exist they all most match. By default no context is
80+
ever ignored.
81+
7682
--apply::
7783
If you use any of the options marked ``Turns off
7884
"apply"'' above, git-apply reads and outputs the

apply.c

Lines changed: 105 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ static int apply = 1;
3232
static int no_add = 0;
3333
static int show_index_info = 0;
3434
static int line_termination = '\n';
35+
static unsigned long p_context = -1;
3536
static const char apply_usage[] =
36-
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
37+
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [-pNUM] [-CNUM] [--whitespace=<nowarn|warn|error|error-all|strip>] <patch>...";
3738

3839
static enum whitespace_eol {
3940
nowarn_whitespace,
@@ -100,6 +101,7 @@ static int max_change, max_len;
100101
static int linenr = 1;
101102

102103
struct fragment {
104+
unsigned long leading, trailing;
103105
unsigned long oldpos, oldlines;
104106
unsigned long newpos, newlines;
105107
const char *patch;
@@ -817,12 +819,15 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
817819
int added, deleted;
818820
int len = linelen(line, size), offset;
819821
unsigned long oldlines, newlines;
822+
unsigned long leading, trailing;
820823

821824
offset = parse_fragment_header(line, len, fragment);
822825
if (offset < 0)
823826
return -1;
824827
oldlines = fragment->oldlines;
825828
newlines = fragment->newlines;
829+
leading = 0;
830+
trailing = 0;
826831

827832
if (patch->is_new < 0) {
828833
patch->is_new = !oldlines;
@@ -860,10 +865,14 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
860865
case ' ':
861866
oldlines--;
862867
newlines--;
868+
if (!deleted && !added)
869+
leading++;
870+
trailing++;
863871
break;
864872
case '-':
865873
deleted++;
866874
oldlines--;
875+
trailing = 0;
867876
break;
868877
case '+':
869878
/*
@@ -887,6 +896,7 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
887896
}
888897
added++;
889898
newlines--;
899+
trailing = 0;
890900
break;
891901

892902
/* We allow "\ No newline at end of file". Depending
@@ -904,6 +914,9 @@ static int parse_fragment(char *line, unsigned long size, struct patch *patch, s
904914
}
905915
if (oldlines || newlines)
906916
return -1;
917+
fragment->leading = leading;
918+
fragment->trailing = trailing;
919+
907920
/* If a fragment ends with an incomplete line, we failed to include
908921
* it in the above loop because we hit oldlines == newlines == 0
909922
* before seeing it.
@@ -1087,7 +1100,7 @@ static int read_old_data(struct stat *st, const char *path, void *buf, unsigned
10871100
}
10881101
}
10891102

1090-
static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line)
1103+
static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line, int *lines)
10911104
{
10921105
int i;
10931106
unsigned long start, backwards, forwards;
@@ -1148,6 +1161,7 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
11481161
n = (i >> 1)+1;
11491162
if (i & 1)
11501163
n = -n;
1164+
*lines = n;
11511165
return try;
11521166
}
11531167

@@ -1157,6 +1171,33 @@ static int find_offset(const char *buf, unsigned long size, const char *fragment
11571171
return -1;
11581172
}
11591173

1174+
static void remove_first_line(const char **rbuf, int *rsize)
1175+
{
1176+
const char *buf = *rbuf;
1177+
int size = *rsize;
1178+
unsigned long offset;
1179+
offset = 0;
1180+
while (offset <= size) {
1181+
if (buf[offset++] == '\n')
1182+
break;
1183+
}
1184+
*rsize = size - offset;
1185+
*rbuf = buf + offset;
1186+
}
1187+
1188+
static void remove_last_line(const char **rbuf, int *rsize)
1189+
{
1190+
const char *buf = *rbuf;
1191+
int size = *rsize;
1192+
unsigned long offset;
1193+
offset = size - 1;
1194+
while (offset > 0) {
1195+
if (buf[--offset] == '\n')
1196+
break;
1197+
}
1198+
*rsize = offset + 1;
1199+
}
1200+
11601201
struct buffer_desc {
11611202
char *buffer;
11621203
unsigned long size;
@@ -1192,7 +1233,10 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
11921233
int offset, size = frag->size;
11931234
char *old = xmalloc(size);
11941235
char *new = xmalloc(size);
1236+
const char *oldlines, *newlines;
11951237
int oldsize = 0, newsize = 0;
1238+
unsigned long leading, trailing;
1239+
int pos, lines;
11961240

11971241
while (size > 0) {
11981242
int len = linelen(patch, size);
@@ -1241,23 +1285,59 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
12411285
newsize--;
12421286
}
12431287
#endif
1244-
1245-
offset = find_offset(buf, desc->size, old, oldsize, frag->newpos);
1246-
if (offset >= 0) {
1247-
int diff = newsize - oldsize;
1248-
unsigned long size = desc->size + diff;
1249-
unsigned long alloc = desc->alloc;
1250-
1251-
if (size > alloc) {
1252-
alloc = size + 8192;
1253-
desc->alloc = alloc;
1254-
buf = xrealloc(buf, alloc);
1255-
desc->buffer = buf;
1288+
1289+
oldlines = old;
1290+
newlines = new;
1291+
leading = frag->leading;
1292+
trailing = frag->trailing;
1293+
lines = 0;
1294+
pos = frag->newpos;
1295+
for (;;) {
1296+
offset = find_offset(buf, desc->size, oldlines, oldsize, pos, &lines);
1297+
if (offset >= 0) {
1298+
int diff = newsize - oldsize;
1299+
unsigned long size = desc->size + diff;
1300+
unsigned long alloc = desc->alloc;
1301+
1302+
/* Warn if it was necessary to reduce the number
1303+
* of context lines.
1304+
*/
1305+
if ((leading != frag->leading) || (trailing != frag->trailing))
1306+
fprintf(stderr, "Context reduced to (%ld/%ld) to apply fragment at %d\n",
1307+
leading, trailing, pos + lines);
1308+
1309+
if (size > alloc) {
1310+
alloc = size + 8192;
1311+
desc->alloc = alloc;
1312+
buf = xrealloc(buf, alloc);
1313+
desc->buffer = buf;
1314+
}
1315+
desc->size = size;
1316+
memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
1317+
memcpy(buf + offset, newlines, newsize);
1318+
offset = 0;
1319+
1320+
break;
1321+
}
1322+
1323+
/* Am I at my context limits? */
1324+
if ((leading <= p_context) && (trailing <= p_context))
1325+
break;
1326+
/* Reduce the number of context lines
1327+
* Reduce both leading and trailing if they are equal
1328+
* otherwise just reduce the larger context.
1329+
*/
1330+
if (leading >= trailing) {
1331+
remove_first_line(&oldlines, &oldsize);
1332+
remove_first_line(&newlines, &newsize);
1333+
pos--;
1334+
leading--;
1335+
}
1336+
if (trailing > leading) {
1337+
remove_last_line(&oldlines, &oldsize);
1338+
remove_last_line(&newlines, &newsize);
1339+
trailing--;
12561340
}
1257-
desc->size = size;
1258-
memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
1259-
memcpy(buf + offset, new, newsize);
1260-
offset = 0;
12611341
}
12621342

12631343
free(old);
@@ -1882,6 +1962,7 @@ int main(int argc, char **argv)
18821962

18831963
for (i = 1; i < argc; i++) {
18841964
const char *arg = argv[i];
1965+
char *end;
18851966
int fd;
18861967

18871968
if (!strcmp(arg, "-")) {
@@ -1945,6 +2026,12 @@ int main(int argc, char **argv)
19452026
line_termination = 0;
19462027
continue;
19472028
}
2029+
if (!strncmp(arg, "-C", 2)) {
2030+
p_context = strtoul(arg + 2, &end, 0);
2031+
if (*end != '\0')
2032+
die("unrecognized context count '%s'", arg + 2);
2033+
continue;
2034+
}
19482035
if (!strncmp(arg, "--whitespace=", 13)) {
19492036
whitespace_option = arg + 13;
19502037
parse_whitespace_option(arg + 13);

0 commit comments

Comments
 (0)