From 710158e3ca3f45a30e08a50dadf427fe95311b9c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 25 Apr 2006 23:40:09 -0700 Subject: diff --stat: show complete rewrites consistently. The patch format shows complete rewrite as deletion of all old lines followed by addition of all new lines. Count lines consistenly with that when doing diffstat. Signed-off-by: Junio C Hamano --- diff.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 13b216f273..6762fcee5a 100644 --- a/diff.c +++ b/diff.c @@ -485,7 +485,8 @@ static void builtin_diff(const char *name_a, static void builtin_diffstat(const char *name_a, const char *name_b, struct diff_filespec *one, struct diff_filespec *two, - struct diffstat_t *diffstat) + struct diffstat_t *diffstat, + int complete_rewrite) { mmfile_t mf1, mf2; struct diffstat_file *data; @@ -496,7 +497,13 @@ static void builtin_diffstat(const char *name_a, const char *name_b, data->is_unmerged = 1; return; } - + if (complete_rewrite) { + diff_populate_filespec(one, 0); + diff_populate_filespec(two, 0); + data->deleted = count_lines(one->data, one->size); + data->added = count_lines(two->data, two->size); + return; + } if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); @@ -1055,10 +1062,11 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, { const char *name; const char *other; + int complete_rewrite = 0; if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ - builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat); + builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, 0); return; } @@ -1068,7 +1076,9 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o, diff_fill_sha1_info(p->one); diff_fill_sha1_info(p->two); - builtin_diffstat(name, other, p->one, p->two, diffstat); + if (p->status == DIFF_STATUS_MODIFIED && p->score) + complete_rewrite = 1; + builtin_diffstat(name, other, p->one, p->two, diffstat, complete_rewrite); } void diff_setup(struct diff_options *options) -- cgit v1.2.3 From dcb3450fd8785d76dd3d25aa49be66190aa5e7f3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 3 May 2006 17:21:08 -0700 Subject: sha1_to_hex() usage cleanup Somebody on the #git channel complained that the sha1_to_hex() thing uses a static buffer which caused an error message to show the same hex output twice instead of showing two different ones. That's pretty easily rectified by making it uses a simple LRU of a few buffers, which also allows some other users (that were aware of the buffer re-use) to be written in a more straightforward manner. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 6762fcee5a..c845c87113 100644 --- a/diff.c +++ b/diff.c @@ -1018,14 +1018,12 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) } if (memcmp(one->sha1, two->sha1, 20)) { - char one_sha1[41]; int abbrev = o->full_index ? 40 : DEFAULT_ABBREV; - memcpy(one_sha1, sha1_to_hex(one->sha1), 41); len += snprintf(msg + len, sizeof(msg) - len, "index %.*s..%.*s", - abbrev, one_sha1, abbrev, - sha1_to_hex(two->sha1)); + abbrev, sha1_to_hex(one->sha1), + abbrev, sha1_to_hex(two->sha1)); if (one->mode == two->mode) len += snprintf(msg + len, sizeof(msg) - len, " %06o", one->mode); -- cgit v1.2.3 From 051308f6e9cebeb76b8fb4f52b7e9e7ce064445c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 4 May 2006 16:51:44 -0700 Subject: binary patch. This adds "binary patch" to the diff output and teaches apply what to do with them. On the diff generation side, traditionally, we said "Binary files differ\n" without giving anything other than the preimage and postimage object name on the index line. This was good enough for applying a patch generated from your own repository (very useful while rebasing), because the postimage would be available in such a case. However, this was not useful when the recipient of such a patch via e-mail were to apply it, even if the preimage was available. This patch allows the diff to generate "binary" patch when operating under --full-index option. The binary patch follows the usual extended git diff headers, and looks like this: "GIT binary patch\n" "\n" ... "\n" Each line is prefixed with a "length-byte", whose value is upper or lowercase alphabet that encodes number of bytes that the data on the line decodes to (1..52 -- 'A' means 1, 'B' means 2, ..., 'Z' means 26, 'a' means 27, ...). is 1 or more groups of 5-byte sequence, each of which encodes up to 4 bytes in base85 encoding. Because 52 / 4 * 5 = 65 and we have the length byte, an output line is capped to 66 characters. The payload is the same diff-delta as we use in the packfiles. On the consumption side, git-apply now can decode and apply the binary patch when --allow-binary-replacement is given, the diff was generated with --full-index, and the receiving repository has the preimage blob, which is the same condition as it always required when accepting an "Binary files differ\n" patch. Signed-off-by: Junio C Hamano --- diff.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 7 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index c845c87113..b14d897f19 100644 --- a/diff.c +++ b/diff.c @@ -8,6 +8,7 @@ #include "quote.h" #include "diff.h" #include "diffcore.h" +#include "delta.h" #include "xdiff-interface.h" static int use_size_cache; @@ -391,6 +392,90 @@ static void show_stats(struct diffstat_t* data) total_files, adds, dels); } +static void *encode_delta_size(void *data, unsigned long size) +{ + unsigned char *cp = data; + *cp++ = size; + size >>= 7; + while (size) { + cp[-1] |= 0x80; + *cp++ = size; + size >>= 7; + } + return cp; +} + +static void *safe_diff_delta(const unsigned char *src, unsigned long src_size, + const unsigned char *dst, unsigned long dst_size, + unsigned long *delta_size) +{ + unsigned long bufsize; + unsigned char *data; + unsigned char *cp; + + if (src_size && dst_size) + return diff_delta(src, src_size, dst, dst_size, delta_size, 0); + + /* diff-delta does not like to do delta with empty, so + * we do that by hand here. Sigh... + */ + + if (!src_size) + /* literal copy can be done only 127-byte at a time. + */ + bufsize = dst_size + (dst_size / 127) + 40; + else + bufsize = 40; + data = xmalloc(bufsize); + cp = encode_delta_size(data, src_size); + cp = encode_delta_size(cp, dst_size); + + if (dst_size) { + /* copy out literally */ + while (dst_size) { + int sz = (127 < dst_size) ? 127 : dst_size; + *cp++ = sz; + dst_size -= sz; + while (sz) { + *cp++ = *dst++; + sz--; + } + } + } + *delta_size = (cp - data); + return data; +} + +static void emit_binary_diff(mmfile_t *one, mmfile_t *two) +{ + void *delta, *cp; + unsigned long delta_size; + + printf("GIT binary patch\n"); + delta = safe_diff_delta(one->ptr, one->size, + two->ptr, two->size, + &delta_size); + if (!delta) + die("unable to generate binary diff"); + + /* emit delta encoded in base85 */ + cp = delta; + while (delta_size) { + int bytes = (52 < delta_size) ? 52 : delta_size; + char line[70]; + delta_size -= bytes; + if (bytes <= 26) + line[0] = bytes + 'A' - 1; + else + line[0] = bytes - 26 + 'a' - 1; + encode_85(line + 1, cp, bytes); + cp += bytes; + puts(line); + } + printf("\n"); + free(delta); +} + #define FIRST_FEW_BYTES 8000 static int mmfile_is_binary(mmfile_t *mf) { @@ -407,6 +492,7 @@ static void builtin_diff(const char *name_a, struct diff_filespec *one, struct diff_filespec *two, const char *xfrm_msg, + struct diff_options *o, int complete_rewrite) { mmfile_t mf1, mf2; @@ -451,8 +537,13 @@ static void builtin_diff(const char *name_a, if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0) die("unable to read files to diff"); - if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) - printf("Binary files %s and %s differ\n", lbl[0], lbl[1]); + if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) { + if (o->full_index) + emit_binary_diff(&mf1, &mf2); + else + printf("Binary files %s and %s differ\n", + lbl[0], lbl[1]); + } else { /* Crazy xdl interfaces.. */ const char *diffopts = getenv("GIT_DIFF_OPTS"); @@ -928,6 +1019,7 @@ static void run_diff_cmd(const char *pgm, struct diff_filespec *one, struct diff_filespec *two, const char *xfrm_msg, + struct diff_options *o, int complete_rewrite) { if (pgm) { @@ -937,7 +1029,7 @@ static void run_diff_cmd(const char *pgm, } if (one && two) builtin_diff(name, other ? other : name, - one, two, xfrm_msg, complete_rewrite); + one, two, xfrm_msg, o, complete_rewrite); else printf("* Unmerged path %s\n", name); } @@ -971,7 +1063,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) if (DIFF_PAIR_UNMERGED(p)) { /* unmerged */ - run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0); + run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0); return; } @@ -1041,14 +1133,14 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o) * needs to be split into deletion and creation. */ struct diff_filespec *null = alloc_filespec(two->path); - run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0); + run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0); free(null); null = alloc_filespec(one->path); - run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0); + run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0); free(null); } else - run_diff_cmd(pgm, name, other, one, two, xfrm_msg, + run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o, complete_rewrite); free(name_munged); -- cgit v1.2.3 From 0660626caff6ac3745cd7b7908a2ca540141a2ec Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 5 May 2006 02:41:53 -0700 Subject: binary diff: further updates. This updates the user interface and generated diff data format. * "diff --binary" is used to signal that we want an e-mailable binary patch. It implies --full-index and -p. * "apply --allow-binary-replacement" acquired a short synonym "apply --binary". * After the "GIT binary patch\n" header line there is a token to record which binary patch mechanism was used, so that we can extend it later. Currently there are two mechanisms defined: "literal" and "delta". The former records the deflated postimage and the latter records the deflated delta from the preimage to postimage. For purely implementation convenience, I added the deflated length after these "literal/delta" tokens (otherwise the decoding side needs to guess and reallocate the buffer while inflating). Improvement patches are very welcomed. Signed-off-by: Junio C Hamano --- diff.c | 134 ++++++++++++++++++++++++++++++++++------------------------------- 1 file changed, 71 insertions(+), 63 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index b14d897f19..bfe54c3e09 100644 --- a/diff.c +++ b/diff.c @@ -392,78 +392,78 @@ static void show_stats(struct diffstat_t* data) total_files, adds, dels); } -static void *encode_delta_size(void *data, unsigned long size) +static unsigned char *deflate_it(char *data, + unsigned long size, + unsigned long *result_size) { - unsigned char *cp = data; - *cp++ = size; - size >>= 7; - while (size) { - cp[-1] |= 0x80; - *cp++ = size; - size >>= 7; - } - return cp; + int bound; + unsigned char *deflated; + z_stream stream; + + memset(&stream, 0, sizeof(stream)); + deflateInit(&stream, Z_BEST_COMPRESSION); + bound = deflateBound(&stream, size); + deflated = xmalloc(bound); + stream.next_out = deflated; + stream.avail_out = bound; + + stream.next_in = (unsigned char *)data; + stream.avail_in = size; + while (deflate(&stream, Z_FINISH) == Z_OK) + ; /* nothing */ + deflateEnd(&stream); + *result_size = stream.total_out; + return deflated; } -static void *safe_diff_delta(const unsigned char *src, unsigned long src_size, - const unsigned char *dst, unsigned long dst_size, - unsigned long *delta_size) +static void emit_binary_diff(mmfile_t *one, mmfile_t *two) { - unsigned long bufsize; - unsigned char *data; - unsigned char *cp; - - if (src_size && dst_size) - return diff_delta(src, src_size, dst, dst_size, delta_size, 0); + void *cp; + void *delta; + void *deflated; + void *data; + unsigned long orig_size; + unsigned long delta_size; + unsigned long deflate_size; + unsigned long data_size; - /* diff-delta does not like to do delta with empty, so - * we do that by hand here. Sigh... + printf("GIT binary patch\n"); + /* We could do deflated delta, or we could do just deflated two, + * whichever is smaller. */ - - if (!src_size) - /* literal copy can be done only 127-byte at a time. - */ - bufsize = dst_size + (dst_size / 127) + 40; - else - bufsize = 40; - data = xmalloc(bufsize); - cp = encode_delta_size(data, src_size); - cp = encode_delta_size(cp, dst_size); - - if (dst_size) { - /* copy out literally */ - while (dst_size) { - int sz = (127 < dst_size) ? 127 : dst_size; - *cp++ = sz; - dst_size -= sz; - while (sz) { - *cp++ = *dst++; - sz--; - } + delta = NULL; + deflated = deflate_it(two->ptr, two->size, &deflate_size); + if (one->size && two->size) { + delta = diff_delta(one->ptr, one->size, + two->ptr, two->size, + &delta_size, deflate_size); + if (delta) { + void *to_free = delta; + orig_size = delta_size; + delta = deflate_it(delta, delta_size, &delta_size); + free(to_free); } } - *delta_size = (cp - data); - return data; -} -static void emit_binary_diff(mmfile_t *one, mmfile_t *two) -{ - void *delta, *cp; - unsigned long delta_size; + if (delta && delta_size < deflate_size) { + printf("delta %lu\n", orig_size); + free(deflated); + data = delta; + data_size = delta_size; + } + else { + printf("literal %lu\n", two->size); + free(delta); + data = deflated; + data_size = deflate_size; + } - printf("GIT binary patch\n"); - delta = safe_diff_delta(one->ptr, one->size, - two->ptr, two->size, - &delta_size); - if (!delta) - die("unable to generate binary diff"); - - /* emit delta encoded in base85 */ - cp = delta; - while (delta_size) { - int bytes = (52 < delta_size) ? 52 : delta_size; + /* emit data encoded in base85 */ + cp = data; + while (data_size) { + int bytes = (52 < data_size) ? 52 : data_size; char line[70]; - delta_size -= bytes; + data_size -= bytes; if (bytes <= 26) line[0] = bytes + 'A' - 1; else @@ -473,7 +473,7 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two) puts(line); } printf("\n"); - free(delta); + free(data); } #define FIRST_FEW_BYTES 8000 @@ -538,7 +538,11 @@ static void builtin_diff(const char *name_a, die("unable to read files to diff"); if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) { - if (o->full_index) + /* Quite common confusing case */ + if (mf1.size == mf2.size && + !memcmp(mf1.ptr, mf2.ptr, mf1.size)) + goto free_ab_and_return; + if (o->binary) emit_binary_diff(&mf1, &mf2); else printf("Binary files %s and %s differ\n", @@ -1239,6 +1243,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) options->rename_limit = strtoul(arg+2, NULL, 10); else if (!strcmp(arg, "--full-index")) options->full_index = 1; + else if (!strcmp(arg, "--binary")) { + options->output_format = DIFF_FORMAT_PATCH; + options->full_index = options->binary = 1; + } else if (!strcmp(arg, "--name-only")) options->output_format = DIFF_FORMAT_NAME; else if (!strcmp(arg, "--name-status")) -- cgit v1.2.3 From 5d6a9f45e1ba78693cb0fbd5e9724b05d4e30e37 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 8 May 2006 09:46:53 -0700 Subject: Fix "git diff --stat" with long filenames When we cut off the front of a filename to make it fit on the line, we add a "..." in front. However, the way the "git diff" code was written, we will never reset the prefix back to the empty string, so every single filename afterwards will have the "..." prefix, whether appropriate or not. You can see this with "git diff v2.6.16.." on the current kernel tree, since there are filenames with long names that changed there: [ snip snip ] Documentation/filesystems/vfs.txt | 229 .../firmware_class/firmware_sample_driver.c | 3 .../firmware_sample_firmware_class.c | 1 ...Documentation/fujitsu/frv/kernel-ABI.txt | 192 ...Documentation/hwmon/w83627hf | 4 [ snip snip ] notice how the two Documentation/firmware** filenames caused the "..." to be added, but then the later filenames don't want it, and it also screws up the alignment of the line numbering afterwards. Trivially fixed by moving the declaration (and initial setting) of the "prefix" variable into the for-loop where it is used. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 903afa1689..afaa648dbb 100644 --- a/diff.c +++ b/diff.c @@ -239,7 +239,6 @@ static const char minuses[]= "-------------------------------------------------- static void show_stats(struct diffstat_t* data) { - char *prefix = ""; int i, len, add, del, total, adds = 0, dels = 0; int max, max_change = 0, max_len = 0; int total_files = data->nr; @@ -261,6 +260,7 @@ static void show_stats(struct diffstat_t* data) } for (i = 0; i < data->nr; i++) { + char *prefix = ""; char *name = data->files[i]->name; int added = data->files[i]->added; int deleted = data->files[i]->deleted; -- cgit v1.2.3 From ee1e5412a702674d4da95a4aef73a73d2834dcc3 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 13 May 2006 13:23:48 -0700 Subject: git diff: support "-U" and "--unified" options properly We used to parse "-U" and "--unified" as part of the GIT_DIFF_OPTS environment variable, but strangely enough we would _not_ parse them as part of the normal diff command line (where we only accepted "-u"). This adds parsing of -U and --unified, both with an optional numeric argument. So now you can just say git diff --unified=5 to get a unified diff with a five-line context, instead of having to do something silly like GIT_DIFF_OPTS="--unified=5" git diff -u (that silly format does continue to still work, of course). Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- diff.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7a7b839e56..be925a3a39 100644 --- a/diff.c +++ b/diff.c @@ -558,7 +558,7 @@ static void builtin_diff(const char *name_a, ecbdata.label_path = lbl; xpp.flags = XDF_NEED_MINIMAL; - xecfg.ctxlen = 3; + xecfg.ctxlen = o->context; xecfg.flags = XDL_EMIT_FUNCNAMES; if (!diffopts) ; @@ -1182,6 +1182,7 @@ void diff_setup(struct diff_options *options) options->line_termination = '\n'; options->break_opt = -1; options->rename_limit = -1; + options->context = 3; options->change = diff_change; options->add_remove = diff_addremove; @@ -1222,11 +1223,60 @@ int diff_setup_done(struct diff_options *options) return 0; } +int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) +{ + char c, *eq; + int len; + + if (*arg != '-') + return 0; + c = *++arg; + if (!c) + return 0; + if (c == arg_short) { + c = *++arg; + if (!c) + return 1; + if (val && isdigit(c)) { + char *end; + int n = strtoul(arg, &end, 10); + if (*end) + return 0; + *val = n; + return 1; + } + return 0; + } + if (c != '-') + return 0; + arg++; + eq = strchr(arg, '='); + if (eq) + len = eq - arg; + else + len = strlen(arg); + if (!len || strncmp(arg, arg_long, len)) + return 0; + if (eq) { + int n; + char *end; + if (!isdigit(*++eq)) + return 0; + n = strtoul(eq, &end, 10); + if (*end) + return 0; + *val = n; + } + return 1; +} + int diff_opt_parse(struct diff_options *options, const char **av, int ac) { const char *arg = av[0]; if (!strcmp(arg, "-p") || !strcmp(arg, "-u")) options->output_format = DIFF_FORMAT_PATCH; + else if (opt_arg(arg, 'U', "unified", &options->context)) + options->output_format = DIFF_FORMAT_PATCH; else if (!strcmp(arg, "--patch-with-raw")) { options->output_format = DIFF_FORMAT_PATCH; options->with_raw = 1; -- cgit v1.2.3 From 4bbd261bbd941066735e41dff0ade9f50e6fe84d Mon Sep 17 00:00:00 2001 From: Sean Date: Sun, 14 May 2006 08:13:49 -0400 Subject: Add "--summary" option to git diff. Remove the need to pipe git diff through git apply to get the extended headers summary. Signed-off-by: Sean Estabrooks Signed-off-by: Junio C Hamano --- diff.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 87 insertions(+), 1 deletion(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7a7b839e56..00b1044d3a 100644 --- a/diff.c +++ b/diff.c @@ -1233,6 +1233,8 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac) } else if (!strcmp(arg, "--stat")) options->output_format = DIFF_FORMAT_DIFFSTAT; + else if (!strcmp(arg, "--summary")) + options->summary = 1; else if (!strcmp(arg, "--patch-with-stat")) { options->output_format = DIFF_FORMAT_PATCH; options->with_stat = 1; @@ -1703,6 +1705,85 @@ static void flush_one_pair(struct diff_filepair *p, } } +static void show_file_mode_name(const char *newdelete, struct diff_filespec *fs) +{ + if (fs->mode) + printf(" %s mode %06o %s\n", newdelete, fs->mode, fs->path); + else + printf(" %s %s\n", newdelete, fs->path); +} + + +static void show_mode_change(struct diff_filepair *p, int show_name) +{ + if (p->one->mode && p->two->mode && p->one->mode != p->two->mode) { + if (show_name) + printf(" mode change %06o => %06o %s\n", + p->one->mode, p->two->mode, p->two->path); + else + printf(" mode change %06o => %06o\n", + p->one->mode, p->two->mode); + } +} + +static void show_rename_copy(const char *renamecopy, struct diff_filepair *p) +{ + const char *old, *new; + + /* Find common prefix */ + old = p->one->path; + new = p->two->path; + while (1) { + const char *slash_old, *slash_new; + slash_old = strchr(old, '/'); + slash_new = strchr(new, '/'); + if (!slash_old || + !slash_new || + slash_old - old != slash_new - new || + memcmp(old, new, slash_new - new)) + break; + old = slash_old + 1; + new = slash_new + 1; + } + /* p->one->path thru old is the common prefix, and old and new + * through the end of names are renames + */ + if (old != p->one->path) + printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy, + (int)(old - p->one->path), p->one->path, + old, new, (int)(0.5 + p->score * 100.0/MAX_SCORE)); + else + printf(" %s %s => %s (%d%%)\n", renamecopy, + p->one->path, p->two->path, + (int)(0.5 + p->score * 100.0/MAX_SCORE)); + show_mode_change(p, 0); +} + +static void diff_summary(struct diff_filepair *p) +{ + switch(p->status) { + case DIFF_STATUS_DELETED: + show_file_mode_name("delete", p->one); + break; + case DIFF_STATUS_ADDED: + show_file_mode_name("create", p->two); + break; + case DIFF_STATUS_COPIED: + show_rename_copy("copy", p); + break; + case DIFF_STATUS_RENAMED: + show_rename_copy("rename", p); + break; + default: + if (p->score) { + printf(" rewrite %s (%d%%)\n", p->two->path, + (int)(0.5 + p->score * 100.0/MAX_SCORE)); + show_mode_change(p, 0); + } else show_mode_change(p, 1); + break; + } +} + void diff_flush(struct diff_options *options) { struct diff_queue_struct *q = &diff_queued_diff; @@ -1736,7 +1817,6 @@ void diff_flush(struct diff_options *options) for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; flush_one_pair(p, diff_output_format, options, diffstat); - diff_free_filepair(p); } if (diffstat) { @@ -1744,6 +1824,12 @@ void diff_flush(struct diff_options *options) free(diffstat); } + for (i = 0; i < q->nr; i++) { + if (options->summary) + diff_summary(q->queue[i]); + diff_free_filepair(q->queue[i]); + } + free(q->queue); q->queue = NULL; q->nr = q->alloc = 0; -- cgit v1.2.3 From cc908b82a4a09df555fbc4c32cfbe8b8cffae865 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 14 May 2006 22:07:28 -0700 Subject: diffstat rename squashing fix. When renaming leading/a/filename to leading/b/filename (and "filename" is sufficiently long), we tried to squash the rename to "leading/{a => b}/filename". However, when "/a" or "/b" part is empty, we underflowed and tried to print a substring of length -1. Signed-off-by: Junio C Hamano --- diff.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'diff.c') diff --git a/diff.c b/diff.c index 7a7b839e56..5285c03667 100644 --- a/diff.c +++ b/diff.c @@ -232,11 +232,16 @@ static char *pprint_rename(const char *a, const char *b) * name-a => name-b */ if (pfx_length + sfx_length) { + int a_midlen = len_a - pfx_length - sfx_length; + int b_midlen = len_b - pfx_length - sfx_length; + if (a_midlen < 0) a_midlen = 0; + if (b_midlen < 0) b_midlen = 0; + name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7); sprintf(name, "%.*s{%.*s => %.*s}%s", pfx_length, a, - len_a - pfx_length - sfx_length, a + pfx_length, - len_b - pfx_length - sfx_length, b + pfx_length, + a_midlen, a + pfx_length, + b_midlen, b + pfx_length, a + len_a - sfx_length); } else { -- cgit v1.2.3