Skip to content

Commit 1403db4

Browse files
committed
Merge branch 'jc/apply-binary-p0' into maint-1.7.11
"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 eaff724 + 6a2abdc commit 1403db4

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
@@ -1086,15 +1086,23 @@ static int gitdiff_unrecognized(const char *line, struct patch *patch)
10861086
return -1;
10871087
}
10881088

1089-
static const char *stop_at_slash(const char *line, int llen)
1089+
/*
1090+
* Skip p_value leading components from "line"; as we do not accept
1091+
* absolute paths, return NULL in that case.
1092+
*/
1093+
static const char *skip_tree_prefix(const char *line, int llen)
10901094
{
1091-
int nslash = p_value;
1095+
int nslash;
10921096
int i;
10931097

1098+
if (!p_value)
1099+
return (llen && line[0] == '/') ? NULL : line;
1100+
1101+
nslash = p_value;
10941102
for (i = 0; i < llen; i++) {
10951103
int ch = line[i];
10961104
if (ch == '/' && --nslash <= 0)
1097-
return &line[i];
1105+
return (i == 0) ? NULL : &line[i + 1];
10981106
}
10991107
return NULL;
11001108
}
@@ -1124,12 +1132,11 @@ static char *git_header_name(const char *line, int llen)
11241132
if (unquote_c_style(&first, line, &second))
11251133
goto free_and_fail1;
11261134

1127-
/* advance to the first slash */
1128-
cp = stop_at_slash(first.buf, first.len);
1129-
/* we do not accept absolute paths */
1130-
if (!cp || cp == first.buf)
1135+
/* strip the a/b prefix including trailing slash */
1136+
cp = skip_tree_prefix(first.buf, first.len);
1137+
if (!cp)
11311138
goto free_and_fail1;
1132-
strbuf_remove(&first, 0, cp + 1 - first.buf);
1139+
strbuf_remove(&first, 0, cp - first.buf);
11331140

11341141
/*
11351142
* second points at one past closing dq of name.
@@ -1143,22 +1150,21 @@ static char *git_header_name(const char *line, int llen)
11431150
if (*second == '"') {
11441151
if (unquote_c_style(&sp, second, NULL))
11451152
goto free_and_fail1;
1146-
cp = stop_at_slash(sp.buf, sp.len);
1147-
if (!cp || cp == sp.buf)
1153+
cp = skip_tree_prefix(sp.buf, sp.len);
1154+
if (!cp)
11481155
goto free_and_fail1;
11491156
/* They must match, otherwise ignore */
1150-
if (strcmp(cp + 1, first.buf))
1157+
if (strcmp(cp, first.buf))
11511158
goto free_and_fail1;
11521159
strbuf_release(&sp);
11531160
return strbuf_detach(&first, NULL);
11541161
}
11551162

11561163
/* unquoted second */
1157-
cp = stop_at_slash(second, line + llen - second);
1158-
if (!cp || cp == second)
1164+
cp = skip_tree_prefix(second, line + llen - second);
1165+
if (!cp)
11591166
goto free_and_fail1;
1160-
cp++;
1161-
if (line + llen - cp != first.len + 1 ||
1167+
if (line + llen - cp != first.len ||
11621168
memcmp(first.buf, cp, first.len))
11631169
goto free_and_fail1;
11641170
return strbuf_detach(&first, NULL);
@@ -1170,10 +1176,9 @@ static char *git_header_name(const char *line, int llen)
11701176
}
11711177

11721178
/* unquoted first name */
1173-
name = stop_at_slash(line, llen);
1174-
if (!name || name == line)
1179+
name = skip_tree_prefix(line, llen);
1180+
if (!name)
11751181
return NULL;
1176-
name++;
11771182

11781183
/*
11791184
* since the first name is unquoted, a dq if exists must be
@@ -1187,10 +1192,9 @@ static char *git_header_name(const char *line, int llen)
11871192
if (unquote_c_style(&sp, second, NULL))
11881193
goto free_and_fail2;
11891194

1190-
np = stop_at_slash(sp.buf, sp.len);
1191-
if (!np || np == sp.buf)
1195+
np = skip_tree_prefix(sp.buf, sp.len);
1196+
if (!np)
11921197
goto free_and_fail2;
1193-
np++;
11941198

11951199
len = sp.buf + sp.len - np;
11961200
if (len < second - name &&
@@ -1222,13 +1226,27 @@ static char *git_header_name(const char *line, int llen)
12221226
case '\n':
12231227
return NULL;
12241228
case '\t': case ' ':
1225-
second = stop_at_slash(name + len, line_len - len);
1229+
/*
1230+
* Is this the separator between the preimage
1231+
* and the postimage pathname? Again, we are
1232+
* only interested in the case where there is
1233+
* no rename, as this is only to set def_name
1234+
* and a rename patch has the names elsewhere
1235+
* in an unambiguous form.
1236+
*/
1237+
if (!name[len + 1])
1238+
return NULL; /* no postimage name */
1239+
second = skip_tree_prefix(name + len + 1,
1240+
line_len - (len + 1));
12261241
if (!second)
12271242
return NULL;
1228-
second++;
1229-
if (second[len] == '\n' && !strncmp(name, second, len)) {
1243+
/*
1244+
* Does len bytes starting at "name" and "second"
1245+
* (that are separated by one HT or SP we just
1246+
* found) exactly match?
1247+
*/
1248+
if (second[len] == '\n' && !strncmp(name, second, len))
12301249
return xmemdupz(name, len);
1231-
}
12321250
}
12331251
}
12341252
}

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)