Skip to content

Commit 757bf26

Browse files
committed
Merge branch 'jc/apply-binary-p0'
"git apply -p0" did not parse pathnames on "diff --git" line correctly. This caused patches that had pathnames in no other places to be mistakenly rejected (most notably, binary patch that does not rename nor change mode). Textual patches, renames or mode changes have preimage and postimage pathnames in different places in a form that can be parsed unambiguously and did not suffer from this problem. * jc/apply-binary-p0: apply: compute patch->def_name correctly under -p0
2 parents 7764a3b + 6a2abdc commit 757bf26

File tree

2 files changed

+76
-46
lines changed

2 files changed

+76
-46
lines changed

builtin/apply.c

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,15 +1095,23 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
10951095
return -1;
10961096
}
10971097

1098-
static const char *stop_at_slash(const char *line, int llen)
1098+
/*
1099+
* Skip p_value leading components from "line"; as we do not accept
1100+
* absolute paths, return NULL in that case.
1101+
*/
1102+
static const char *skip_tree_prefix(const char *line, int llen)
10991103
{
1100-
int nslash = p_value;
1104+
int nslash;
11011105
int i;
11021106

1107+
if (!p_value)
1108+
return (llen && line[0] == '/') ? NULL : line;
1109+
1110+
nslash = p_value;
11031111
for (i = 0; i < llen; i++) {
11041112
int ch = line[i];
11051113
if (ch == '/' && --nslash <= 0)
1106-
return &line[i];
1114+
return (i == 0) ? NULL : &line[i + 1];
11071115
}
11081116
return NULL;
11091117
}
@@ -1133,12 +1141,11 @@ static char *git_header_name(const char *line, int llen)
11331141
if (unquote_c_style(&first, line, &second))
11341142
goto free_and_fail1;
11351143

1136-
/* advance to the first slash */
1137-
cp = stop_at_slash(first.buf, first.len);
1138-
/* we do not accept absolute paths */
1139-
if (!cp || cp == first.buf)
1144+
/* strip the a/b prefix including trailing slash */
1145+
cp = skip_tree_prefix(first.buf, first.len);
1146+
if (!cp)
11401147
goto free_and_fail1;
1141-
strbuf_remove(&first, 0, cp + 1 - first.buf);
1148+
strbuf_remove(&first, 0, cp - first.buf);
11421149

11431150
/*
11441151
* second points at one past closing dq of name.
@@ -1152,22 +1159,21 @@ static char *git_header_name(const char *line, int llen)
11521159
if (*second == '"') {
11531160
if (unquote_c_style(&sp, second, NULL))
11541161
goto free_and_fail1;
1155-
cp = stop_at_slash(sp.buf, sp.len);
1156-
if (!cp || cp == sp.buf)
1162+
cp = skip_tree_prefix(sp.buf, sp.len);
1163+
if (!cp)
11571164
goto free_and_fail1;
11581165
/* They must match, otherwise ignore */
1159-
if (strcmp(cp + 1, first.buf))
1166+
if (strcmp(cp, first.buf))
11601167
goto free_and_fail1;
11611168
strbuf_release(&sp);
11621169
return strbuf_detach(&first, NULL);
11631170
}
11641171

11651172
/* unquoted second */
1166-
cp = stop_at_slash(second, line + llen - second);
1167-
if (!cp || cp == second)
1173+
cp = skip_tree_prefix(second, line + llen - second);
1174+
if (!cp)
11681175
goto free_and_fail1;
1169-
cp++;
1170-
if (line + llen - cp != first.len + 1 ||
1176+
if (line + llen - cp != first.len ||
11711177
memcmp(first.buf, cp, first.len))
11721178
goto free_and_fail1;
11731179
return strbuf_detach(&first, NULL);
@@ -1179,10 +1185,9 @@ static char *git_header_name(const char *line, int llen)
11791185
}
11801186

11811187
/* unquoted first name */
1182-
name = stop_at_slash(line, llen);
1183-
if (!name || name == line)
1188+
name = skip_tree_prefix(line, llen);
1189+
if (!name)
11841190
return NULL;
1185-
name++;
11861191

11871192
/*
11881193
* since the first name is unquoted, a dq if exists must be
@@ -1196,10 +1201,9 @@ static char *git_header_name(const char *line, int llen)
11961201
if (unquote_c_style(&sp, second, NULL))
11971202
goto free_and_fail2;
11981203

1199-
np = stop_at_slash(sp.buf, sp.len);
1200-
if (!np || np == sp.buf)
1204+
np = skip_tree_prefix(sp.buf, sp.len);
1205+
if (!np)
12011206
goto free_and_fail2;
1202-
np++;
12031207

12041208
len = sp.buf + sp.len - np;
12051209
if (len < second - name &&
@@ -1231,13 +1235,27 @@ static char *git_header_name(const char *line, int llen)
12311235
case '\n':
12321236
return NULL;
12331237
case '\t': case ' ':
1234-
second = stop_at_slash(name + len, line_len - len);
1238+
/*
1239+
* Is this the separator between the preimage
1240+
* and the postimage pathname? Again, we are
1241+
* only interested in the case where there is
1242+
* no rename, as this is only to set def_name
1243+
* and a rename patch has the names elsewhere
1244+
* in an unambiguous form.
1245+
*/
1246+
if (!name[len + 1])
1247+
return NULL; /* no postimage name */
1248+
second = skip_tree_prefix(name + len + 1,
1249+
line_len - (len + 1));
12351250
if (!second)
12361251
return NULL;
1237-
second++;
1238-
if (second[len] == '\n' && !strncmp(name, second, len)) {
1252+
/*
1253+
* Does len bytes starting at "name" and "second"
1254+
* (that are separated by one HT or SP we just
1255+
* found) exactly match?
1256+
*/
1257+
if (second[len] == '\n' && !strncmp(name, second, len))
12391258
return xmemdupz(name, len);
1240-
}
12411259
}
12421260
}
12431261
}

t/t4103-apply-binary.sh

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,28 @@ test_description='git apply handling binary patches
88
'
99
. ./test-lib.sh
1010

11-
# setup
12-
13-
cat >file1 <<EOF
14-
A quick brown fox jumps over the lazy dog.
15-
A tiny little penguin runs around in circles.
16-
There is a flag with Linux written on it.
17-
A slow black-and-white panda just sits there,
18-
munching on his bamboo.
19-
EOF
20-
cat file1 >file2
21-
cat file1 >file4
22-
23-
test_expect_success 'setup' "
11+
test_expect_success 'setup' '
12+
cat >file1 <<-\EOF &&
13+
A quick brown fox jumps over the lazy dog.
14+
A tiny little penguin runs around in circles.
15+
There is a flag with Linux written on it.
16+
A slow black-and-white panda just sits there,
17+
munching on his bamboo.
18+
EOF
19+
cat file1 >file2 &&
20+
cat file1 >file4 &&
21+
2422
git update-index --add --remove file1 file2 file4 &&
25-
git commit -m 'Initial Version' 2>/dev/null &&
23+
git commit -m "Initial Version" 2>/dev/null &&
2624
2725
git checkout -b binary &&
28-
"$PERL_PATH" -pe 'y/x/\000/' <file1 >file3 &&
26+
"$PERL_PATH" -pe "y/x/\000/" <file1 >file3 &&
2927
cat file3 >file4 &&
3028
git add file2 &&
31-
"$PERL_PATH" -pe 'y/\000/v/' <file3 >file1 &&
29+
"$PERL_PATH" -pe "y/\000/v/" <file3 >file1 &&
3230
rm -f file2 &&
3331
git update-index --add --remove file1 file2 file3 file4 &&
34-
git commit -m 'Second Version' &&
32+
git commit -m "Second Version" &&
3533
3634
git diff-tree -p master binary >B.diff &&
3735
git diff-tree -p -C master binary >C.diff &&
@@ -42,17 +40,25 @@ test_expect_success 'setup' "
4240
git diff-tree -p --full-index master binary >B-index.diff &&
4341
git diff-tree -p -C --full-index master binary >C-index.diff &&
4442
43+
git diff-tree -p --binary --no-prefix master binary -- file3 >B0.diff &&
44+
4545
git init other-repo &&
46-
(cd other-repo &&
47-
git fetch .. master &&
48-
git reset --hard FETCH_HEAD
46+
(
47+
cd other-repo &&
48+
git fetch .. master &&
49+
git reset --hard FETCH_HEAD
4950
)
50-
"
51+
'
5152

5253
test_expect_success 'stat binary diff -- should not fail.' \
5354
'git checkout master &&
5455
git apply --stat --summary B.diff'
5556

57+
test_expect_success 'stat binary -p0 diff -- should not fail.' '
58+
git checkout master &&
59+
git apply --stat -p0 B0.diff
60+
'
61+
5662
test_expect_success 'stat binary diff (copy) -- should not fail.' \
5763
'git checkout master &&
5864
git apply --stat --summary C.diff'
@@ -143,4 +149,10 @@ test_expect_success 'apply binary diff (copy).' \
143149
git apply --allow-binary-replacement --index CF.diff &&
144150
test -z "$(git diff --name-status binary)"'
145151

152+
test_expect_success 'apply binary -p0 diff' '
153+
do_reset &&
154+
git apply -p0 --index B0.diff &&
155+
test -z "$(git diff --name-status binary -- file3)"
156+
'
157+
146158
test_done

0 commit comments

Comments
 (0)