Browse Source

git-apply --binary: clean up and prepare for --reverse

This cleans up the implementation of "git-apply --binary", and
implements reverse application of binary patches (when git-diff
is converted to emit reversible binary patches).

Earlier, the types of encoding (either deflated literal or
deflated delta) were stored in is_binary field in struct patch,
which meant that we cannot store more than one fragment that
differ in the encoding for a patch.  This moves the information
to a field in struct fragment that is otherwise unused for
binary patches, and makes it possible to hang two (or more, but
two is enough) hunks for a binary patch.

The original "binary patch" output from git-diff is internally
parsed into an "is_binary" patch with one fragment.  Upcoming
reversible binary patch output will have two fragments, the
first one being the forward patch and the second one the reverse
patch.

Signed-off-by: Junio C Hamano <junkio@cox.net>
maint
Junio C Hamano 19 years ago
parent
commit
3cd4f5e8eb
  1. 200
      builtin-apply.c

200
builtin-apply.c

@ -109,6 +109,13 @@ static int max_change, max_len;
*/ */
static int linenr = 1; static int linenr = 1;


/*
* This represents one "hunk" from a patch, starting with
* "@@ -oldpos,oldlines +newpos,newlines @@" marker. The
* patch text is pointed at by patch, and its byte length
* is stored in size. leading and trailing are the number
* of context lines.
*/
struct fragment { struct fragment {
unsigned long leading, trailing; unsigned long leading, trailing;
unsigned long oldpos, oldlines; unsigned long oldpos, oldlines;
@ -118,12 +125,19 @@ struct fragment {
struct fragment *next; struct fragment *next;
}; };


/*
* When dealing with a binary patch, we reuse "leading" field
* to store the type of the binary hunk, either deflated "delta"
* or deflated "literal".
*/
#define binary_patch_method leading
#define BINARY_DELTA_DEFLATED 1
#define BINARY_LITERAL_DEFLATED 2

struct patch { struct patch {
char *new_name, *old_name, *def_name; char *new_name, *old_name, *def_name;
unsigned int old_mode, new_mode; unsigned int old_mode, new_mode;
int is_rename, is_copy, is_new, is_delete, is_binary; int is_rename, is_copy, is_new, is_delete, is_binary;
#define BINARY_DELTA_DEFLATED 1
#define BINARY_LITERAL_DEFLATED 2
unsigned long deflate_origlen; unsigned long deflate_origlen;
int lines_added, lines_deleted; int lines_added, lines_deleted;
int score; int score;
@ -979,43 +993,70 @@ static inline int metadata_changes(struct patch *patch)
patch->old_mode != patch->new_mode); patch->old_mode != patch->new_mode);
} }


static int parse_binary(char *buffer, unsigned long size, struct patch *patch) static char *inflate_it(const void *data, unsigned long size,
unsigned long inflated_size)
{ {
/* We have read "GIT binary patch\n"; what follows is a line z_stream stream;
* that says the patch method (currently, either "deflated void *out;
* literal" or "deflated delta") and the length of data before int st;
* deflating; a sequence of 'length-byte' followed by base-85
* encoded data follows. memset(&stream, 0, sizeof(stream));

stream.next_in = (unsigned char *)data;
stream.avail_in = size;
stream.next_out = out = xmalloc(inflated_size);
stream.avail_out = inflated_size;
inflateInit(&stream);
st = inflate(&stream, Z_FINISH);
if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
free(out);
return NULL;
}
return out;
}

static struct fragment *parse_binary_hunk(char **buf_p,
unsigned long *sz_p,
int *status_p,
int *used_p)
{
/* Expect a line that begins with binary patch method ("literal"
* or "delta"), followed by the length of data before deflating.
* a sequence of 'length-byte' followed by base-85 encoded data
* should follow, terminated by a newline.
* *
* Each 5-byte sequence of base-85 encodes up to 4 bytes, * Each 5-byte sequence of base-85 encodes up to 4 bytes,
* and we would limit the patch line to 66 characters, * and we would limit the patch line to 66 characters,
* so one line can fit up to 13 groups that would decode * so one line can fit up to 13 groups that would decode
* to 52 bytes max. The length byte 'A'-'Z' corresponds * to 52 bytes max. The length byte 'A'-'Z' corresponds
* to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes. * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes.
* The end of binary is signaled with an empty line.
*/ */
int llen, used; int llen, used;
struct fragment *fragment; unsigned long size = *sz_p;
char *buffer = *buf_p;
int patch_method;
unsigned long origlen;
char *data = NULL; char *data = NULL;
int hunk_size = 0;
struct fragment *frag;


patch->fragments = fragment = xcalloc(1, sizeof(*fragment));

/* Grab the type of patch */
llen = linelen(buffer, size); llen = linelen(buffer, size);
used = llen; used = llen;
linenr++;
*status_p = 0;


if (!strncmp(buffer, "delta ", 6)) { if (!strncmp(buffer, "delta ", 6)) {
patch->is_binary = BINARY_DELTA_DEFLATED; patch_method = BINARY_DELTA_DEFLATED;
patch->deflate_origlen = strtoul(buffer + 6, NULL, 10); origlen = strtoul(buffer + 6, NULL, 10);
} }
else if (!strncmp(buffer, "literal ", 8)) { else if (!strncmp(buffer, "literal ", 8)) {
patch->is_binary = BINARY_LITERAL_DEFLATED; patch_method = BINARY_LITERAL_DEFLATED;
patch->deflate_origlen = strtoul(buffer + 8, NULL, 10); origlen = strtoul(buffer + 8, NULL, 10);
} }
else else
return error("unrecognized binary patch at line %d: %.*s", return NULL;
linenr-1, llen-1, buffer);
linenr++;
buffer += llen; buffer += llen;
while (1) { while (1) {
int byte_length, max_byte_length, newsize; int byte_length, max_byte_length, newsize;
@ -1044,21 +1085,79 @@ static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
if (max_byte_length < byte_length || if (max_byte_length < byte_length ||
byte_length <= max_byte_length - 4) byte_length <= max_byte_length - 4)
goto corrupt; goto corrupt;
newsize = fragment->size + byte_length; newsize = hunk_size + byte_length;
data = xrealloc(data, newsize); data = xrealloc(data, newsize);
if (decode_85(data + fragment->size, if (decode_85(data + hunk_size, buffer + 1, byte_length))
buffer + 1,
byte_length))
goto corrupt; goto corrupt;
fragment->size = newsize; hunk_size = newsize;
buffer += llen; buffer += llen;
size -= llen; size -= llen;
} }
fragment->patch = data;
return used; frag = xcalloc(1, sizeof(*frag));
frag->patch = inflate_it(data, hunk_size, origlen);
if (!frag->patch)
goto corrupt;
free(data);
frag->size = origlen;
*buf_p = buffer;
*sz_p = size;
*used_p = used;
frag->binary_patch_method = patch_method;
return frag;

corrupt: corrupt:
return error("corrupt binary patch at line %d: %.*s", if (data)
free(data);
*status_p = -1;
error("corrupt binary patch at line %d: %.*s",
linenr-1, llen-1, buffer); linenr-1, llen-1, buffer);
return NULL;
}

static int parse_binary(char *buffer, unsigned long size, struct patch *patch)
{
/* We have read "GIT binary patch\n"; what follows is a line
* that says the patch method (currently, either "literal" or
* "delta") and the length of data before deflating; a
* sequence of 'length-byte' followed by base-85 encoded data
* follows.
*
* When a binary patch is reversible, there is another binary
* hunk in the same format, starting with patch method (either
* "literal" or "delta") with the length of data, and a sequence
* of length-byte + base-85 encoded data, terminated with another
* empty line. This data, when applied to the postimage, produces
* the preimage.
*/
struct fragment *forward;
struct fragment *reverse;
int status;
int used, used_1;

forward = parse_binary_hunk(&buffer, &size, &status, &used);
if (!forward && !status)
/* there has to be one hunk (forward hunk) */
return error("unrecognized binary patch at line %d", linenr-1);
if (status)
/* otherwise we already gave an error message */
return status;

reverse = parse_binary_hunk(&buffer, &size, &status, &used_1);
if (reverse)
used += used_1;
else if (status) {
/* not having reverse hunk is not an error, but having
* a corrupt reverse hunk is.
*/
free((void*) forward->patch);
free(forward);
return status;
}
forward->next = reverse;
patch->fragments = forward;
patch->is_binary = 1;
return used;
} }


static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
@ -1505,28 +1604,6 @@ static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag, i
return offset; return offset;
} }


static char *inflate_it(const void *data, unsigned long size,
unsigned long inflated_size)
{
z_stream stream;
void *out;
int st;

memset(&stream, 0, sizeof(stream));

stream.next_in = (unsigned char *)data;
stream.avail_in = size;
stream.next_out = out = xmalloc(inflated_size);
stream.avail_out = inflated_size;
inflateInit(&stream);
st = inflate(&stream, Z_FINISH);
if ((st != Z_STREAM_END) || stream.total_out != inflated_size) {
free(out);
return NULL;
}
return out;
}

static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch) static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
{ {
unsigned long dst_size; unsigned long dst_size;
@ -1534,30 +1611,29 @@ static int apply_binary_fragment(struct buffer_desc *desc, struct patch *patch)
void *data; void *data;
void *result; void *result;


/* Binary patch is irreversible */ /* Binary patch is irreversible without the optional second hunk */
if (apply_in_reverse) if (apply_in_reverse) {
return error("cannot reverse-apply a binary patch to '%s'", if (!fragment->next)
return error("cannot reverse-apply a binary patch "
"without the reverse hunk to '%s'",
patch->new_name patch->new_name
? patch->new_name : patch->old_name); ? patch->new_name : patch->old_name);

fragment = fragment;
data = inflate_it(fragment->patch, fragment->size, }
patch->deflate_origlen); data = (void*) fragment->patch;
if (!data) switch (fragment->binary_patch_method) {
return error("corrupt patch data");
switch (patch->is_binary) {
case BINARY_DELTA_DEFLATED: case BINARY_DELTA_DEFLATED:
result = patch_delta(desc->buffer, desc->size, result = patch_delta(desc->buffer, desc->size,
data, data,
patch->deflate_origlen, fragment->size,
&dst_size); &dst_size);
free(desc->buffer); free(desc->buffer);
desc->buffer = result; desc->buffer = result;
free(data);
break; break;
case BINARY_LITERAL_DEFLATED: case BINARY_LITERAL_DEFLATED:
free(desc->buffer); free(desc->buffer);
desc->buffer = data; desc->buffer = data;
dst_size = patch->deflate_origlen; dst_size = fragment->size;
break; break;
} }
if (!desc->buffer) if (!desc->buffer)

Loading…
Cancel
Save