From: Junio C Hamano Date: Fri, 5 May 2006 22:36:04 +0000 (-0700) Subject: Merge branch 'jc/bindiff' into next X-Git-Tag: v1.4.1-rc1~132 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/2749fa7771f2a4323b48b78fa194a90d611bc2d8?hp=-c Merge branch 'jc/bindiff' into next * jc/bindiff: binary diff: further updates. binary patch. pack-object: squelch eye-candy on non-tty core.prefersymlinkrefs: use symlinks for .git/HEAD repo-config: trim white-space before comment Fix for config file section parsing. Clarify git-cherry documentation. Update git-unpack-objects documentation. Fix up docs where "--" isn't displayed correctly. Several trivial documentation touch ups. git-svn 1.0.0 git-svn: documentation updates delta: stricter constness Makefile: do not link rev-list any specially. builtin-push: --all and --tags _are_ explicit refspecs --- 2749fa7771f2a4323b48b78fa194a90d611bc2d8 diff --combined Makefile index e9d9e101d3,814010d7b4..070c478bf2 --- a/Makefile +++ b/Makefile @@@ -205,7 -205,7 +205,7 @@@ DIFF_OBJS = diffcore-delta.o log-tree.o LIB_OBJS = \ - blob.o commit.o connect.o csum-file.o cache-tree.o \ - blob.o commit.o connect.o csum-file.o base85.o \ ++ blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \ date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o \ quote.o read-cache.o refs.o run-command.o \ @@@ -215,8 -215,7 +215,8 @@@ $(DIFF_OBJS) BUILTIN_OBJS = \ - builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o + builtin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \ + builtin-grep.o GITLIBS = $(LIB_FILE) $(XDIFF_LIB) LIBS = $(GITLIBS) -lz @@@ -565,10 -564,6 +565,6 @@@ git-http-push$X: revision.o http.o http $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT) - git-rev-list$X: rev-list.o $(LIB_FILE) - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \ - $(LIBS) $(OPENSSL_LIBSSL) - init-db.o: init-db.c $(CC) -c $(ALL_CFLAGS) \ -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $*.c @@@ -612,10 -607,7 +608,10 @@@ test-date$X: test-date.c date.o ctype. $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) test-date.c date.o ctype.o test-delta$X: test-delta.c diff-delta.o patch-delta.o - $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ -lz + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $^ + +test-dump-cache-tree$X: dump-cache-tree.o $(GITLIBS) + $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) check: for i in *.c; do sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; done diff --combined apply.c index acecf8de54,1b93aab8af..88d2a3206b --- a/apply.c +++ b/apply.c @@@ -8,9 -8,9 +8,10 @@@ */ #include #include "cache.h" +#include "cache-tree.h" #include "quote.h" #include "blob.h" + #include "delta.h" // --check turns on checking that the working tree matches the // files that are being modified, but doesn't apply the patch @@@ -114,6 -114,9 +115,9 @@@ struct patch char *new_name, *old_name, *def_name; unsigned int old_mode, new_mode; 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; int lines_added, lines_deleted; int score; struct fragment *fragments; @@@ -967,6 -970,88 +971,88 @@@ static inline int metadata_changes(stru patch->old_mode != patch->new_mode); } + 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 "deflated + * literal" or "deflated delta") and the length of data before + * deflating; a sequence of 'length-byte' followed by base-85 + * encoded data follows. + * + * Each 5-byte sequence of base-85 encodes up to 4 bytes, + * and we would limit the patch line to 66 characters, + * so one line can fit up to 13 groups that would decode + * to 52 bytes max. The length byte 'A'-'Z' corresponds + * to 1-26 bytes, and 'a'-'z' corresponds to 27-52 bytes. + * The end of binary is signalled with an empty line. + */ + int llen, used; + struct fragment *fragment; + char *data = NULL; + + patch->fragments = fragment = xcalloc(1, sizeof(*fragment)); + + /* Grab the type of patch */ + llen = linelen(buffer, size); + used = llen; + linenr++; + + if (!strncmp(buffer, "delta ", 6)) { + patch->is_binary = BINARY_DELTA_DEFLATED; + patch->deflate_origlen = strtoul(buffer + 6, NULL, 10); + } + else if (!strncmp(buffer, "literal ", 8)) { + patch->is_binary = BINARY_LITERAL_DEFLATED; + patch->deflate_origlen = strtoul(buffer + 8, NULL, 10); + } + else + return error("unrecognized binary patch at line %d: %.*s", + linenr-1, llen-1, buffer); + buffer += llen; + while (1) { + int byte_length, max_byte_length, newsize; + llen = linelen(buffer, size); + used += llen; + linenr++; + if (llen == 1) + break; + /* Minimum line is "A00000\n" which is 7-byte long, + * and the line length must be multiple of 5 plus 2. + */ + if ((llen < 7) || (llen-2) % 5) + goto corrupt; + max_byte_length = (llen - 2) / 5 * 4; + byte_length = *buffer; + if ('A' <= byte_length && byte_length <= 'Z') + byte_length = byte_length - 'A' + 1; + else if ('a' <= byte_length && byte_length <= 'z') + byte_length = byte_length - 'a' + 27; + else + goto corrupt; + /* if the input length was not multiple of 4, we would + * have filler at the end but the filler should never + * exceed 3 bytes + */ + if (max_byte_length < byte_length || + byte_length <= max_byte_length - 4) + goto corrupt; + newsize = fragment->size + byte_length; + data = xrealloc(data, newsize); + if (decode_85(data + fragment->size, + buffer + 1, + byte_length)) + goto corrupt; + fragment->size = newsize; + buffer += llen; + size -= llen; + } + fragment->patch = data; + return used; + corrupt: + return error("corrupt binary patch at line %d: %.*s", + linenr-1, llen-1, buffer); + } + static int parse_chunk(char *buffer, unsigned long size, struct patch *patch) { int hdrsize, patchsize; @@@ -983,19 -1068,34 +1069,34 @@@ "Files ", NULL, }; + static const char git_binary[] = "GIT binary patch\n"; int i; int hd = hdrsize + offset; unsigned long llen = linelen(buffer + hd, size - hd); - if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) + if (llen == sizeof(git_binary) - 1 && + !memcmp(git_binary, buffer + hd, llen)) { + int used; + linenr++; + used = parse_binary(buffer + hd + llen, + size - hd - llen, patch); + if (used) + patchsize = used + llen; + else + patchsize = 0; + } + else if (!memcmp(" differ\n", buffer + hd + llen - 8, 8)) { for (i = 0; binhdr[i]; i++) { int len = strlen(binhdr[i]); if (len < size - hd && !memcmp(binhdr[i], buffer + hd, len)) { + linenr++; patch->is_binary = 1; + patchsize = llen; break; } } + } /* Empty patch cannot be applied if: * - it is a binary patch and we do not do binary_replace, or @@@ -1346,76 -1446,150 +1447,150 @@@ static int apply_one_fragment(struct bu return offset; } - static int apply_fragments(struct buffer_desc *desc, struct patch *patch) + 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) + { + unsigned long dst_size; + struct fragment *fragment = patch->fragments; + void *data; + void *result; + + data = inflate_it(fragment->patch, fragment->size, + patch->deflate_origlen); + if (!data) + return error("corrupt patch data"); + switch (patch->is_binary) { + case BINARY_DELTA_DEFLATED: + result = patch_delta(desc->buffer, desc->size, + data, + patch->deflate_origlen, + &dst_size); + free(desc->buffer); + desc->buffer = result; + free(data); + break; + case BINARY_LITERAL_DEFLATED: + free(desc->buffer); + desc->buffer = data; + dst_size = patch->deflate_origlen; + break; + } + if (!desc->buffer) + return -1; + desc->size = desc->alloc = dst_size; + return 0; + } + + static int apply_binary(struct buffer_desc *desc, struct patch *patch) { - struct fragment *frag = patch->fragments; const char *name = patch->old_name ? patch->old_name : patch->new_name; + unsigned char sha1[20]; + unsigned char hdr[50]; + int hdrlen; - 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); - 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); - /* For safety, we require patch index line to contain - * full 40-byte textual SHA1 for old and new, at least for now. + if (patch->old_name) { + /* See if the old one matches what the patch + * applies to. */ - 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_type, 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); - } + write_sha1_file_prepare(desc->buffer, desc->size, + blob_type, 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); + } + + get_sha1_hex(patch->new_sha1_prefix, sha1); + if (!memcmp(sha1, null_sha1, 20)) { + free(desc->buffer); + desc->alloc = desc->size = 0; + desc->buffer = NULL; + return 0; /* deletion patch */ + } - /* 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 (has_sha1_file(sha1)) { + /* We already have the postimage */ + char type[10]; + unsigned long size; + + free(desc->buffer); + desc->buffer = read_sha1_file(sha1, type, &size); + if (!desc->buffer) + return error("the necessary postimage %s for " + "'%s' cannot be read", + patch->new_sha1_prefix, name); + desc->alloc = desc->size = size; + } + else { + /* We have verified desc matches the preimage; + * apply the patch data to it, which is stored + * in the patch->fragments->{patch,size}. */ - 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; - } + if (apply_binary_fragment(desc, patch)) + return error("binary patch does not apply to '%s'", + name); - return 0; + /* verify that the result matches */ + write_sha1_file_prepare(desc->buffer, desc->size, blob_type, + sha1, hdr, &hdrlen); + if (strcmp(sha1_to_hex(sha1), patch->new_sha1_prefix)) + return error("binary patch to '%s' creates incorrect result", name); } + return 0; + } + + 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) + return apply_binary(desc, patch); + while (frag) { if (apply_one_fragment(desc, frag) < 0) return error("patch failed: %s:%ld", @@@ -1718,7 -1892,6 +1893,7 @@@ static void remove_file(struct patch *p if (write_index) { if (remove_file_from_cache(patch->old_name) < 0) die("unable to remove %s from index", patch->old_name); + cache_tree_invalidate_path(active_cache_tree, patch->old_name); } unlink(patch->old_name); } @@@ -1815,9 -1988,8 +1990,9 @@@ static void create_file(struct patch *p if (!mode) mode = S_IFREG | 0644; - create_one_file(path, mode, buf, size); + create_one_file(path, mode, buf, size); add_index_file(path, mode, buf, size); + cache_tree_invalidate_path(active_cache_tree, path); } static void write_out_one_result(struct patch *patch) @@@ -1993,7 -2165,8 +2168,8 @@@ int main(int argc, char **argv diffstat = 1; continue; } - if (!strcmp(arg, "--allow-binary-replacement")) { + if (!strcmp(arg, "--allow-binary-replacement") || + !strcmp(arg, "--binary")) { allow_binary_replacement = 1; continue; } diff --combined cache.h index d186b44a51,4b7a439253..b1300cd989 --- a/cache.h +++ b/cache.h @@@ -114,7 -114,6 +114,7 @@@ static inline unsigned int create_ce_mo extern struct cache_entry **active_cache; extern unsigned int active_nr, active_alloc, active_cache_changed; +extern struct cache_tree *active_cache_tree; #define GIT_DIR_ENVIRONMENT "GIT_DIR" #define DEFAULT_GIT_DIR_ENVIRONMENT ".git" @@@ -252,7 -251,6 +252,7 @@@ extern void *read_object_with_reference unsigned char *sha1_ret); const char *show_date(unsigned long time, int timezone); +const char *show_rfc2822_date(unsigned long time, int timezone); int parse_date(const char *date, char *buf, int bufsize); void datestamp(char *buf, int bufsize); unsigned long approxidate(const char *); @@@ -365,4 -363,8 +365,8 @@@ extern int receive_keep_pack(int fd[2] /* pager.c */ extern void setup_pager(void); + /* base85 */ + int decode_85(char *dst, char *line, int linelen); + void encode_85(char *buf, unsigned char *data, int bytes); + #endif /* CACHE_H */ diff --combined pack-objects.c index 5b2ef9a513,53caed42dd..523a1c7da8 --- a/pack-objects.c +++ b/pack-objects.c @@@ -994,7 -994,6 +994,7 @@@ static int type_size_sort(const struct struct unpacked { struct object_entry *entry; void *data; + struct delta_index *index; }; /* @@@ -1005,56 -1004,61 +1005,56 @@@ * more importantly, the bigger file is likely the more recent * one. */ -static int try_delta(struct unpacked *cur, struct unpacked *old, unsigned max_depth) +static int try_delta(struct unpacked *trg, struct unpacked *src, + struct delta_index *src_index, unsigned max_depth) { - struct object_entry *cur_entry = cur->entry; - struct object_entry *old_entry = old->entry; - unsigned long size, oldsize, delta_size, sizediff; - long max_size; + struct object_entry *trg_entry = trg->entry; + struct object_entry *src_entry = src->entry; + unsigned long size, src_size, delta_size, sizediff, max_size; void *delta_buf; /* Don't bother doing diffs between different types */ - if (cur_entry->type != old_entry->type) + if (trg_entry->type != src_entry->type) return -1; /* We do not compute delta to *create* objects we are not * going to pack. */ - if (cur_entry->preferred_base) + if (trg_entry->preferred_base) return -1; - /* If the current object is at pack edge, take the depth the + /* + * If the current object is at pack edge, take the depth the * objects that depend on the current object into account -- * otherwise they would become too deep. */ - if (cur_entry->delta_child) { - if (max_depth <= cur_entry->delta_limit) + if (trg_entry->delta_child) { + if (max_depth <= trg_entry->delta_limit) return 0; - max_depth -= cur_entry->delta_limit; + max_depth -= trg_entry->delta_limit; } - - if (old_entry->depth >= max_depth) + if (src_entry->depth >= max_depth) return 0; - /* - * NOTE! - * - * We always delta from the bigger to the smaller, since that's - * more space-efficient (deletes don't have to say _what_ they - * delete). - */ - size = cur_entry->size; + /* Now some size filtering euristics. */ + size = trg_entry->size; max_size = size / 2 - 20; - if (cur_entry->delta) - max_size = cur_entry->delta_size-1; - oldsize = old_entry->size; - sizediff = oldsize < size ? size - oldsize : 0; + if (trg_entry->delta) + max_size = trg_entry->delta_size-1; + src_size = src_entry->size; + sizediff = src_size < size ? size - src_size : 0; if (sizediff >= max_size) return 0; - delta_buf = diff_delta(old->data, oldsize, - cur->data, size, &delta_size, max_size); + + delta_buf = create_delta(src_index, trg->data, size, &delta_size, max_size); if (!delta_buf) return 0; - cur_entry->delta = old_entry; - cur_entry->delta_size = delta_size; - cur_entry->depth = old_entry->depth + 1; + + trg_entry->delta = src_entry; + trg_entry->delta_size = delta_size; + trg_entry->depth = src_entry->depth + 1; free(delta_buf); - return 0; + return 1; } static void progress_interval(int signum) @@@ -1104,17 -1108,12 +1104,17 @@@ static void find_deltas(struct object_e if (entry->size < 50) continue; - + if (n->index) + free_delta_index(n->index); free(n->data); n->entry = entry; n->data = read_sha1_file(entry->sha1, type, &size); if (size != entry->size) - die("object %s inconsistent object length (%lu vs %lu)", sha1_to_hex(entry->sha1), size, entry->size); + die("object %s inconsistent object length (%lu vs %lu)", + sha1_to_hex(entry->sha1), size, entry->size); + n->index = create_delta_index(n->data, size); + if (!n->index) + die("out of memory"); j = window; while (--j > 0) { @@@ -1125,7 -1124,7 +1125,7 @@@ m = array + other_idx; if (!m->entry) break; - if (try_delta(n, m, depth) < 0) + if (try_delta(n, m, m->index, depth) < 0) break; } #if 0 @@@ -1145,11 -1144,8 +1145,11 @@@ if (progress) fputc('\n', stderr); - for (i = 0; i < window; ++i) + for (i = 0; i < window; ++i) { + if (array[i].index) + free_delta_index(array[i].index); free(array[i].data); + } free(array); } @@@ -1243,6 -1239,7 +1243,7 @@@ int main(int argc, char **argv setup_git_directory(); + progress = isatty(2); for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@@ -1273,6 -1270,10 +1274,10 @@@ usage(pack_usage); continue; } + if (!strcmp("--progress", arg)) { + progress = 1; + continue; + } if (!strcmp("-q", arg)) { progress = 0; continue;