summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <junkio@cox.net>2005-11-14 17:37:05 -0800
committerLibravatar Junio C Hamano <junkio@cox.net>2005-11-16 16:20:40 -0800
commit011f4274bbb14e44035586f2ede695d584b8cfd2 (patch)
tree539f12ed014b1d0beaab44280436eb8d5ba7102f
parentgit-am: --resolved. (diff)
downloadtgif-011f4274bbb14e44035586f2ede695d584b8cfd2.tar.xz
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>
-rw-r--r--Documentation/git-apply.txt13
-rw-r--r--apply.c87
-rw-r--r--t/t4103-apply-binary.sh8
3 files changed, 101 insertions, 7 deletions
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 6702a18c7e..626e281596 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -8,7 +8,7 @@ git-apply - Apply patch on a git index file and a work tree
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
-----------
@@ -79,6 +79,17 @@ OPTIONS
the result with this option, which would apply the
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
------
Written by Linus Torvalds <torvalds@osdl.org>
diff --git a/apply.c b/apply.c
index a002e156c7..129edb1889 100644
--- a/apply.c
+++ b/apply.c
@@ -16,6 +16,7 @@
// --numstat does numeric diffstat, and doesn't actually apply
// --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 write_index = 0;
static int diffstat = 0;
@@ -27,7 +28,7 @@ static int no_add = 0;
static int show_index_info = 0;
static int line_termination = '\n';
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
@@ -900,11 +901,13 @@ static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
patch->is_binary = 1;
/* Empty patch cannot be applied if:
- * - it is a binary patch or
- * - metadata does not change and is not a binary patch.
+ * - it is a binary patch and we do not do binary_replace, or
+ * - text patch without metadata change
*/
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);
}
@@ -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)
{
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) {
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;
}
return 0;
@@ -1203,6 +1273,7 @@ static int check_patch(struct patch *patch)
struct stat st;
const char *old_name = patch->old_name;
const char *new_name = patch->new_name;
+ const char *name = old_name ? old_name : new_name;
if (old_name) {
int changed;
@@ -1277,7 +1348,7 @@ static int check_patch(struct patch *patch)
}
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;
}
@@ -1726,6 +1797,10 @@ int main(int argc, char **argv)
diffstat = 1;
continue;
}
+ if (!strcmp(arg, "--allow-binary-replacement")) {
+ allow_binary_replacement = 1;
+ continue;
+ }
if (!strcmp(arg, "--numstat")) {
apply = 0;
numstat = 1;
diff --git a/t/t4103-apply-binary.sh b/t/t4103-apply-binary.sh
index 948d5b5a7a..9057f6d04f 100644
--- a/t/t4103-apply-binary.sh
+++ b/t/t4103-apply-binary.sh
@@ -51,6 +51,14 @@ test_expect_failure 'check binary diff (copy) -- should fail.' \
'git-checkout master
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.
test_expect_failure 'apply binary diff -- should fail.' \