Skip to content

Commit 011f427

Browse files
author
Junio C Hamano
committed
apply: allow-binary-replacement.
A new option, --allow-binary-replacement, is introduced. When you feed a diff that records full SHA1 name of pre- and post-image blob on its index line to git-apply with this option, the post-image blob replaces the path if what you have in the working tree matches the pre-image _and_ post-image blob is already available in the object directory. Later we _might_ want to enhance the diff output to also include the full binary data of the post-image, to make this more useful, but this is good enough for local rebasing application. Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent 0c15cc9 commit 011f427

File tree

3 files changed

+101
-7
lines changed

3 files changed

+101
-7
lines changed

Documentation/git-apply.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-apply - Apply patch on a git index file and a work tree
88

99
SYNOPSIS
1010
--------
11-
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] [<patch>...]
11+
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [<patch>...]
1212

1313
DESCRIPTION
1414
-----------
@@ -79,6 +79,17 @@ OPTIONS
7979
the result with this option, which would apply the
8080
deletion part but not addition part.
8181

82+
--allow-binary-replacement::
83+
When applying a patch, which is a git-enhanced patch
84+
that was prepared to record the pre- and post-image object
85+
name in full, and the path being patched exactly matches
86+
the object the patch applies to (i.e. "index" line's
87+
pre-image object name is what is in the working tree),
88+
and the post-image object is available in the object
89+
database, use the post-image object as the patch
90+
result. This allows binary files to be patched in a
91+
very limited way.
92+
8293
Author
8394
------
8495
Written by Linus Torvalds <torvalds@osdl.org>

apply.c

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// --numstat does numeric diffstat, and doesn't actually apply
1717
// --index-info shows the old and new index info for paths if available.
1818
//
19+
static int allow_binary_replacement = 0;
1920
static int check_index = 0;
2021
static int write_index = 0;
2122
static int diffstat = 0;
@@ -27,7 +28,7 @@ static int no_add = 0;
2728
static int show_index_info = 0;
2829
static int line_termination = '\n';
2930
static const char apply_usage[] =
30-
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] <patch>...";
31+
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] <patch>...";
3132

3233
/*
3334
* For "diff-stat" like behaviour, we keep track of the biggest change
@@ -900,11 +901,13 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
900901
patch->is_binary = 1;
901902

902903
/* Empty patch cannot be applied if:
903-
* - it is a binary patch or
904-
* - metadata does not change and is not a binary patch.
904+
* - it is a binary patch and we do not do binary_replace, or
905+
* - text patch without metadata change
905906
*/
906907
if ((apply || check) &&
907-
(patch->is_binary || !metadata_changes(patch)))
908+
(patch->is_binary
909+
? !allow_binary_replacement
910+
: !metadata_changes(patch)))
908911
die("patch with only garbage at line %d", linenr);
909912
}
910913

@@ -1158,10 +1161,77 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
11581161
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
11591162
{
11601163
struct fragment *frag = patch->fragments;
1164+
const char *name = patch->old_name ? patch->old_name : patch->new_name;
1165+
1166+
if (patch->is_binary) {
1167+
unsigned char sha1[20];
1168+
1169+
if (!allow_binary_replacement)
1170+
return error("cannot apply binary patch to '%s' "
1171+
"without --allow-binary-replacement",
1172+
name);
1173+
1174+
/* For safety, we require patch index line to contain
1175+
* full 40-byte textual SHA1 for old and new, at least for now.
1176+
*/
1177+
if (strlen(patch->old_sha1_prefix) != 40 ||
1178+
strlen(patch->new_sha1_prefix) != 40 ||
1179+
get_sha1_hex(patch->old_sha1_prefix, sha1) ||
1180+
get_sha1_hex(patch->new_sha1_prefix, sha1))
1181+
return error("cannot apply binary patch to '%s' "
1182+
"without full index line", name);
1183+
1184+
if (patch->old_name) {
1185+
unsigned char hdr[50];
1186+
int hdrlen;
1187+
1188+
/* See if the old one matches what the patch
1189+
* applies to.
1190+
*/
1191+
write_sha1_file_prepare(desc->buffer, desc->size,
1192+
"blob", sha1, hdr, &hdrlen);
1193+
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
1194+
return error("the patch applies to '%s' (%s), "
1195+
"which does not match the "
1196+
"current contents.",
1197+
name, sha1_to_hex(sha1));
1198+
}
1199+
else {
1200+
/* Otherwise, the old one must be empty. */
1201+
if (desc->size)
1202+
return error("the patch applies to an empty "
1203+
"'%s' but it is not empty", name);
1204+
}
1205+
1206+
/* For now, we do not record post-image data in the patch,
1207+
* and require the object already present in the recipient's
1208+
* object database.
1209+
*/
1210+
if (desc->buffer) {
1211+
free(desc->buffer);
1212+
desc->alloc = desc->size = 0;
1213+
}
1214+
get_sha1_hex(patch->new_sha1_prefix, sha1);
1215+
1216+
if (memcmp(sha1, null_sha1, 20)) {
1217+
char type[10];
1218+
unsigned long size;
1219+
1220+
desc->buffer = read_sha1_file(sha1, type, &size);
1221+
if (!desc->buffer)
1222+
return error("the necessary postimage %s for "
1223+
"'%s' does not exist",
1224+
patch->new_sha1_prefix, name);
1225+
desc->alloc = desc->size = size;
1226+
}
1227+
1228+
return 0;
1229+
}
11611230

11621231
while (frag) {
11631232
if (apply_one_fragment(desc, frag) < 0)
1164-
return error("patch failed: %s:%ld", patch->old_name, frag->oldpos);
1233+
return error("patch failed: %s:%ld",
1234+
name, frag->oldpos);
11651235
frag = frag->next;
11661236
}
11671237
return 0;
@@ -1203,6 +1273,7 @@ static int check_patch(struct patch *patch)
12031273
struct stat st;
12041274
const char *old_name = patch->old_name;
12051275
const char *new_name = patch->new_name;
1276+
const char *name = old_name ? old_name : new_name;
12061277

12071278
if (old_name) {
12081279
int changed;
@@ -1277,7 +1348,7 @@ static int check_patch(struct patch *patch)
12771348
}
12781349

12791350
if (apply_data(patch, &st) < 0)
1280-
return error("%s: patch does not apply", old_name);
1351+
return error("%s: patch does not apply", name);
12811352
return 0;
12821353
}
12831354

@@ -1726,6 +1797,10 @@ int main(int argc, char **argv)
17261797
diffstat = 1;
17271798
continue;
17281799
}
1800+
if (!strcmp(arg, "--allow-binary-replacement")) {
1801+
allow_binary_replacement = 1;
1802+
continue;
1803+
}
17291804
if (!strcmp(arg, "--numstat")) {
17301805
apply = 0;
17311806
numstat = 1;

t/t4103-apply-binary.sh

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ test_expect_failure 'check binary diff (copy) -- should fail.' \
5151
'git-checkout master
5252
git-apply --check C.diff'
5353

54+
test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
55+
'git-checkout master
56+
git-apply --check --allow-binary-replacement B.diff'
57+
58+
test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
59+
'git-checkout master
60+
git-apply --check --allow-binary-replacement C.diff'
61+
5462
# Now we start applying them.
5563

5664
test_expect_failure 'apply binary diff -- should fail.' \

0 commit comments

Comments
 (0)