mirror of
https://github.com/git/git
synced 2024-08-27 11:39:22 +00:00
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>
This commit is contained in:
parent
0c15cc921a
commit
011f4274bb
|
@ -8,7 +8,7 @@ git-apply - Apply patch on a git index file and a work tree
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
--------
|
--------
|
||||||
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] [<patch>...]
|
'git-apply' [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] [<patch>...]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
|
@ -79,6 +79,17 @@ OPTIONS
|
||||||
the result with this option, which would apply the
|
the result with this option, which would apply the
|
||||||
deletion part but not addition part.
|
deletion part but not addition part.
|
||||||
|
|
||||||
|
--allow-binary-replacement::
|
||||||
|
When applying a patch, which is a git-enhanced patch
|
||||||
|
that was prepared to record the pre- and post-image object
|
||||||
|
name in full, and the path being patched exactly matches
|
||||||
|
the object the patch applies to (i.e. "index" line's
|
||||||
|
pre-image object name is what is in the working tree),
|
||||||
|
and the post-image object is available in the object
|
||||||
|
database, use the post-image object as the patch
|
||||||
|
result. This allows binary files to be patched in a
|
||||||
|
very limited way.
|
||||||
|
|
||||||
Author
|
Author
|
||||||
------
|
------
|
||||||
Written by Linus Torvalds <torvalds@osdl.org>
|
Written by Linus Torvalds <torvalds@osdl.org>
|
||||||
|
|
87
apply.c
87
apply.c
|
@ -16,6 +16,7 @@
|
||||||
// --numstat does numeric diffstat, and doesn't actually apply
|
// --numstat does numeric diffstat, and doesn't actually apply
|
||||||
// --index-info shows the old and new index info for paths if available.
|
// --index-info shows the old and new index info for paths if available.
|
||||||
//
|
//
|
||||||
|
static int allow_binary_replacement = 0;
|
||||||
static int check_index = 0;
|
static int check_index = 0;
|
||||||
static int write_index = 0;
|
static int write_index = 0;
|
||||||
static int diffstat = 0;
|
static int diffstat = 0;
|
||||||
|
@ -27,7 +28,7 @@ static int no_add = 0;
|
||||||
static int show_index_info = 0;
|
static int show_index_info = 0;
|
||||||
static int line_termination = '\n';
|
static int line_termination = '\n';
|
||||||
static const char apply_usage[] =
|
static const char apply_usage[] =
|
||||||
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [-z] <patch>...";
|
"git-apply [--stat] [--numstat] [--summary] [--check] [--index] [--apply] [--no-add] [--index-info] [--allow-binary-replacement] [-z] <patch>...";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For "diff-stat" like behaviour, we keep track of the biggest change
|
* 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)
|
||||||
patch->is_binary = 1;
|
patch->is_binary = 1;
|
||||||
|
|
||||||
/* Empty patch cannot be applied if:
|
/* Empty patch cannot be applied if:
|
||||||
* - it is a binary patch or
|
* - it is a binary patch and we do not do binary_replace, or
|
||||||
* - metadata does not change and is not a binary patch.
|
* - text patch without metadata change
|
||||||
*/
|
*/
|
||||||
if ((apply || check) &&
|
if ((apply || check) &&
|
||||||
(patch->is_binary || !metadata_changes(patch)))
|
(patch->is_binary
|
||||||
|
? !allow_binary_replacement
|
||||||
|
: !metadata_changes(patch)))
|
||||||
die("patch with only garbage at line %d", linenr);
|
die("patch with only garbage at line %d", linenr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1158,10 +1161,77 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
|
||||||
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
|
||||||
{
|
{
|
||||||
struct fragment *frag = patch->fragments;
|
struct fragment *frag = patch->fragments;
|
||||||
|
const char *name = patch->old_name ? patch->old_name : patch->new_name;
|
||||||
|
|
||||||
|
if (patch->is_binary) {
|
||||||
|
unsigned char sha1[20];
|
||||||
|
|
||||||
|
if (!allow_binary_replacement)
|
||||||
|
return error("cannot apply binary patch to '%s' "
|
||||||
|
"without --allow-binary-replacement",
|
||||||
|
name);
|
||||||
|
|
||||||
|
/* For safety, we require patch index line to contain
|
||||||
|
* full 40-byte textual SHA1 for old and new, at least for now.
|
||||||
|
*/
|
||||||
|
if (strlen(patch->old_sha1_prefix) != 40 ||
|
||||||
|
strlen(patch->new_sha1_prefix) != 40 ||
|
||||||
|
get_sha1_hex(patch->old_sha1_prefix, sha1) ||
|
||||||
|
get_sha1_hex(patch->new_sha1_prefix, sha1))
|
||||||
|
return error("cannot apply binary patch to '%s' "
|
||||||
|
"without full index line", name);
|
||||||
|
|
||||||
|
if (patch->old_name) {
|
||||||
|
unsigned char hdr[50];
|
||||||
|
int hdrlen;
|
||||||
|
|
||||||
|
/* See if the old one matches what the patch
|
||||||
|
* applies to.
|
||||||
|
*/
|
||||||
|
write_sha1_file_prepare(desc->buffer, desc->size,
|
||||||
|
"blob", sha1, hdr, &hdrlen);
|
||||||
|
if (strcmp(sha1_to_hex(sha1), patch->old_sha1_prefix))
|
||||||
|
return error("the patch applies to '%s' (%s), "
|
||||||
|
"which does not match the "
|
||||||
|
"current contents.",
|
||||||
|
name, sha1_to_hex(sha1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Otherwise, the old one must be empty. */
|
||||||
|
if (desc->size)
|
||||||
|
return error("the patch applies to an empty "
|
||||||
|
"'%s' but it is not empty", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For now, we do not record post-image data in the patch,
|
||||||
|
* and require the object already present in the recipient's
|
||||||
|
* object database.
|
||||||
|
*/
|
||||||
|
if (desc->buffer) {
|
||||||
|
free(desc->buffer);
|
||||||
|
desc->alloc = desc->size = 0;
|
||||||
|
}
|
||||||
|
get_sha1_hex(patch->new_sha1_prefix, sha1);
|
||||||
|
|
||||||
|
if (memcmp(sha1, null_sha1, 20)) {
|
||||||
|
char type[10];
|
||||||
|
unsigned long size;
|
||||||
|
|
||||||
|
desc->buffer = read_sha1_file(sha1, type, &size);
|
||||||
|
if (!desc->buffer)
|
||||||
|
return error("the necessary postimage %s for "
|
||||||
|
"'%s' does not exist",
|
||||||
|
patch->new_sha1_prefix, name);
|
||||||
|
desc->alloc = desc->size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
while (frag) {
|
while (frag) {
|
||||||
if (apply_one_fragment(desc, frag) < 0)
|
if (apply_one_fragment(desc, frag) < 0)
|
||||||
return error("patch failed: %s:%ld", patch->old_name, frag->oldpos);
|
return error("patch failed: %s:%ld",
|
||||||
|
name, frag->oldpos);
|
||||||
frag = frag->next;
|
frag = frag->next;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1203,6 +1273,7 @@ static int check_patch(struct patch *patch)
|
||||||
struct stat st;
|
struct stat st;
|
||||||
const char *old_name = patch->old_name;
|
const char *old_name = patch->old_name;
|
||||||
const char *new_name = patch->new_name;
|
const char *new_name = patch->new_name;
|
||||||
|
const char *name = old_name ? old_name : new_name;
|
||||||
|
|
||||||
if (old_name) {
|
if (old_name) {
|
||||||
int changed;
|
int changed;
|
||||||
|
@ -1277,7 +1348,7 @@ static int check_patch(struct patch *patch)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (apply_data(patch, &st) < 0)
|
if (apply_data(patch, &st) < 0)
|
||||||
return error("%s: patch does not apply", old_name);
|
return error("%s: patch does not apply", name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1726,6 +1797,10 @@ int main(int argc, char **argv)
|
||||||
diffstat = 1;
|
diffstat = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (!strcmp(arg, "--allow-binary-replacement")) {
|
||||||
|
allow_binary_replacement = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!strcmp(arg, "--numstat")) {
|
if (!strcmp(arg, "--numstat")) {
|
||||||
apply = 0;
|
apply = 0;
|
||||||
numstat = 1;
|
numstat = 1;
|
||||||
|
|
|
@ -51,6 +51,14 @@ test_expect_failure 'check binary diff (copy) -- should fail.' \
|
||||||
'git-checkout master
|
'git-checkout master
|
||||||
git-apply --check C.diff'
|
git-apply --check C.diff'
|
||||||
|
|
||||||
|
test_expect_failure 'check incomplete binary diff with replacement -- should fail.' \
|
||||||
|
'git-checkout master
|
||||||
|
git-apply --check --allow-binary-replacement B.diff'
|
||||||
|
|
||||||
|
test_expect_failure 'check incomplete binary diff with replacement (copy) -- should fail.' \
|
||||||
|
'git-checkout master
|
||||||
|
git-apply --check --allow-binary-replacement C.diff'
|
||||||
|
|
||||||
# Now we start applying them.
|
# Now we start applying them.
|
||||||
|
|
||||||
test_expect_failure 'apply binary diff -- should fail.' \
|
test_expect_failure 'apply binary diff -- should fail.' \
|
||||||
|
|
Loading…
Reference in a new issue