From 57fe64a40d95dd99d798c6d7c81c1a76d24a8e7c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 19 May 2005 19:00:36 -0700 Subject: [PATCH] [PATCH] diff overhaul This cleans up the way calls are made into the diff core from diff-tree family and diff-helper. Earlier, these programs had "if (generating_patch)" sprinkled all over the place, but those ugliness are gone and handled uniformly from the diff core, even when not generating patch format. This also allowed diff-cache and diff-files to acquire -R (reverse) option to generate diff in reverse. Users of diff-tree can swap two trees easily so I did not add -R there. [ Linus' note: I'll add -R to "diff-tree" too, since a "commit diff" doesn't have another tree to switch around: the other tree is always the parent(s) of the commit ] Also -M suggestion made by Linus has been implemented. Documentation updates are also included. Signed-off-by: Junio C Hamano Signed-off-by: Linus Torvalds --- Documentation/git-diff-cache.txt | 5 +- Documentation/git-diff-files.txt | 5 +- diff-cache.c | 42 ++++----- diff-files.c | 44 ++++------ diff-helper.c | 19 +++-- diff-tree.c | 46 ++++------ diff.c | 142 ++++++++++++++++++++++++------- diff.h | 4 +- t/t0000-basic.sh | 2 +- t/t2002-checkout-cache-u.sh | 2 + t/t4001-diff-rename.sh | 6 ++ 11 files changed, 192 insertions(+), 125 deletions(-) diff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt index 9cd610333..bd8a6e5c5 100644 --- a/Documentation/git-diff-cache.txt +++ b/Documentation/git-diff-cache.txt @@ -9,7 +9,7 @@ git-diff-cache - Compares content and mode of blobs between the cache and reposi SYNOPSIS -------- -'git-diff-cache' [-p] [-r] [-z] [-m] [-M] [--cached] +'git-diff-cache' [-p] [-r] [-z] [-m] [-M] [-R] [--cached] DESCRIPTION ----------- @@ -36,6 +36,9 @@ OPTIONS -M:: Detect renames; implies -p. +-R:: + Output diff in reverse. + --cached:: do not consider the on-disk file at all diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt index 166238f5c..f79820a65 100644 --- a/Documentation/git-diff-files.txt +++ b/Documentation/git-diff-files.txt @@ -9,7 +9,7 @@ git-diff-files - Compares files in the working tree and the cache SYNOPSIS -------- -'git-diff-files' [-p] [-q] [-r] [-z] [-M] [...] +'git-diff-files' [-p] [-q] [-r] [-z] [-M] [-R] [...] DESCRIPTION ----------- @@ -26,6 +26,9 @@ OPTIONS -q:: Remain silent even on nonexisting files +-R:: + Output diff in reverse. + -M:: Detect renames; implies -p. diff --git a/diff-cache.c b/diff-cache.c index 8f32beed1..40fd6b496 100644 --- a/diff-cache.c +++ b/diff-cache.c @@ -6,15 +6,13 @@ static int generate_patch = 0; static int match_nonexisting = 0; static int line_termination = '\n'; static int detect_rename = 0; +static int reverse_diff = 0; +static int diff_score_opt = 0; /* A file entry went away or appeared */ static void show_file(const char *prefix, struct cache_entry *ce, unsigned char *sha1, unsigned int mode) { - if (generate_patch) - diff_addremove(prefix[0], ntohl(mode), sha1, ce->name, NULL); - else - printf("%s%06o\tblob\t%s\t%s%c", prefix, ntohl(mode), - sha1_to_hex(sha1), ce->name, line_termination); + diff_addremove(prefix[0], ntohl(mode), sha1, ce->name, NULL); } static int get_stat_data(struct cache_entry *ce, unsigned char **sha1p, unsigned int *modep) @@ -64,7 +62,6 @@ static int show_modified(struct cache_entry *old, { unsigned int mode, oldmode; unsigned char *sha1; - char old_sha1_hex[60]; if (get_stat_data(new, &sha1, &mode) < 0) { if (report_missing) @@ -79,15 +76,8 @@ static int show_modified(struct cache_entry *old, mode = ntohl(mode); oldmode = ntohl(oldmode); - if (generate_patch) - diff_change(oldmode, mode, - old->sha1, sha1, old->name, NULL); - else { - strcpy(old_sha1_hex, sha1_to_hex(old->sha1)); - printf("*%06o->%06o\tblob\t%s->%s\t%s%c", oldmode, mode, - old_sha1_hex, sha1_to_hex(sha1), - old->name, line_termination); - } + diff_change(oldmode, mode, + old->sha1, sha1, old->name, NULL); return 0; } @@ -127,10 +117,7 @@ static int diff_cache(struct cache_entry **ac, int entries) break; /* fallthru */ case 3: - if (generate_patch) - diff_unmerge(ce->name); - else - printf("U %s%c", ce->name, line_termination); + diff_unmerge(ce->name); break; default: @@ -166,7 +153,7 @@ static void mark_merge_entries(void) } static char *diff_cache_usage = -"git-diff-cache [-p] [-r] [-z] [-m] [-M] [--cached] "; +"git-diff-cache [-p] [-r] [-z] [-m] [-M] [-R] [--cached] "; int main(int argc, char **argv) { @@ -188,14 +175,19 @@ int main(int argc, char **argv) generate_patch = 1; continue; } - if (!strcmp(arg, "-M")) { + if (!strncmp(arg, "-M", 2)) { generate_patch = detect_rename = 1; + diff_score_opt = diff_scoreopt_parse(arg); continue; } if (!strcmp(arg, "-z")) { line_termination = '\0'; continue; } + if (!strcmp(arg, "-R")) { + reverse_diff = 1; + continue; + } if (!strcmp(arg, "-m")) { match_nonexisting = 1; continue; @@ -210,8 +202,9 @@ int main(int argc, char **argv) if (argc != 2 || get_sha1(argv[1], tree_sha1)) usage(diff_cache_usage); - if (generate_patch) - diff_setup(detect_rename, 0, 0, 0, 0); + diff_setup(detect_rename, diff_score_opt, reverse_diff, + (generate_patch ? -1 : line_termination), + 0, 0); mark_merge_entries(); @@ -222,7 +215,6 @@ int main(int argc, char **argv) die("unable to read tree object %s", argv[1]); ret = diff_cache(active_cache, active_nr); - if (generate_patch) - diff_flush(); + diff_flush(); return ret; } diff --git a/diff-files.c b/diff-files.c index fb345a516..414b36ec9 100644 --- a/diff-files.c +++ b/diff-files.c @@ -7,11 +7,13 @@ #include "diff.h" static const char *diff_files_usage = -"git-diff-files [-p] [-q] [-r] [-z] [-M] [paths...]"; +"git-diff-files [-p] [-q] [-r] [-z] [-M] [-R] [paths...]"; static int generate_patch = 0; static int line_termination = '\n'; static int detect_rename = 0; +static int reverse_diff = 0; +static int diff_score_opt = 0; static int silent = 0; static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt) @@ -31,36 +33,19 @@ static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt) static void show_unmerge(const char *path) { - if (generate_patch) - diff_unmerge(path); - else - printf("U %s%c", path, line_termination); + diff_unmerge(path); } static void show_file(int pfx, struct cache_entry *ce) { - if (generate_patch) - diff_addremove(pfx, ntohl(ce->ce_mode), ce->sha1, - ce->name, NULL); - else - printf("%c%06o\t%s\t%s\t%s%c", - pfx, ntohl(ce->ce_mode), "blob", - sha1_to_hex(ce->sha1), ce->name, line_termination); + diff_addremove(pfx, ntohl(ce->ce_mode), ce->sha1, ce->name, NULL); } static void show_modified(int oldmode, int mode, const unsigned char *old_sha1, const unsigned char *sha1, char *path) { - char old_sha1_hex[41]; - strcpy(old_sha1_hex, sha1_to_hex(old_sha1)); - - if (generate_patch) - diff_change(oldmode, mode, old_sha1, sha1, path, NULL); - else - printf("*%06o->%06o\tblob\t%s->%s\t%s%c", - oldmode, mode, old_sha1_hex, sha1_to_hex(sha1), path, - line_termination); + diff_change(oldmode, mode, old_sha1, sha1, path, NULL); } int main(int argc, char **argv) @@ -80,7 +65,10 @@ int main(int argc, char **argv) ; /* no-op */ else if (!strcmp(argv[1], "-z")) line_termination = 0; - else if (!strcmp(argv[1], "-M")) { + else if (!strcmp(argv[1], "-R")) + reverse_diff = 1; + else if (!strncmp(argv[1], "-M", 2)) { + diff_score_opt = diff_scoreopt_parse(argv[1]); detect_rename = generate_patch = 1; } else @@ -96,8 +84,9 @@ int main(int argc, char **argv) exit(1); } - if (generate_patch) - diff_setup(detect_rename, 0, 0, 0, 0); + diff_setup(detect_rename, diff_score_opt, reverse_diff, + (generate_patch ? -1 : line_termination), + 0, 0); for (i = 0; i < entries; i++) { struct stat st; @@ -117,12 +106,12 @@ int main(int argc, char **argv) i--; /* compensate for loop control increments */ continue; } - + if (lstat(ce->name, &st) < 0) { if (errno != ENOENT) { perror(ce->name); continue; - } + } if (silent) continue; show_file('-', ce); @@ -139,7 +128,6 @@ int main(int argc, char **argv) show_modified(oldmode, mode, ce->sha1, null_sha1, ce->name); } - if (generate_patch) - diff_flush(); + diff_flush(); return 0; } diff --git a/diff-helper.c b/diff-helper.c index 5b0feb1d8..8d896f856 100644 --- a/diff-helper.c +++ b/diff-helper.c @@ -7,6 +7,7 @@ #include "diff.h" static int detect_rename = 0; +static int diff_score_opt = 0; static int parse_oneside_change(const char *cp, int *mode, unsigned char *sha1, char *path) @@ -19,15 +20,15 @@ static int parse_oneside_change(const char *cp, int *mode, cp++; } *mode = m; - if (strncmp(cp, "\tblob\t", 6)) + if (strncmp(cp, "\tblob\t", 6) && strncmp(cp, " blob ", 6)) return -1; cp += 6; if (get_sha1_hex(cp, sha1)) return -1; cp += 40; - if (*cp++ != '\t') + if ((*cp != '\t') && *cp != ' ') return -1; - strcpy(path, cp); + strcpy(path, ++cp); return 0; } @@ -63,7 +64,7 @@ static int parse_diff_raw_output(const char *buf) new_mode = (new_mode << 3) | (ch - '0'); cp++; } - if (strncmp(cp, "\tblob\t", 6)) + if (strncmp(cp, "\tblob\t", 6) && strncmp(cp, " blob ", 6)) return -1; cp += 6; if (get_sha1_hex(cp, old_sha1)) @@ -75,9 +76,9 @@ static int parse_diff_raw_output(const char *buf) if (get_sha1_hex(cp, new_sha1)) return -1; cp += 40; - if (*cp++ != '\t') + if ((*cp != '\t') && *cp != ' ') return -1; - strcpy(path, cp); + strcpy(path, ++cp); diff_change(old_mode, new_mode, old_sha1, new_sha1, path, 0); break; default: @@ -101,15 +102,17 @@ int main(int ac, const char **av) { reverse = 1; else if (av[1][1] == 'z') line_termination = 0; - else if (av[1][1] == 'M') + else if (av[1][1] == 'M') { detect_rename = 1; + diff_score_opt = diff_scoreopt_parse(av[1]); + } else usage(diff_helper_usage); ac--; av++; } /* the remaining parameters are paths patterns */ - diff_setup(detect_rename, 0, reverse, av+1, ac-1); + diff_setup(detect_rename, diff_score_opt, reverse, -1, av+1, ac-1); while (1) { int status; diff --git a/diff-tree.c b/diff-tree.c index 9d94e0cc5..b43df18ef 100644 --- a/diff-tree.c +++ b/diff-tree.c @@ -11,6 +11,7 @@ static int read_stdin = 0; static int line_termination = '\n'; static int generate_patch = 0; static int detect_rename = 0; +static int diff_score_opt = 0; static const char *header = NULL; static const char *header_prefix = ""; @@ -84,21 +85,13 @@ static void show_file(const char *prefix, void *tree, unsigned long size, const die("corrupt tree sha %s", sha1_to_hex(sha1)); show_tree(prefix, tree, size, newbase); - + free(tree); free(newbase); return; } - if (generate_patch) { - if (!S_ISDIR(mode)) - diff_addremove(prefix[0], mode, sha1, base, path); - } - else - printf("%s%06o\t%s\t%s\t%s%s%c", prefix, mode, - S_ISDIR(mode) ? "tree" : "blob", - sha1_to_hex(sha1), base, path, - line_termination); + diff_addremove(prefix[0], mode, sha1, base, path); } static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base) @@ -107,7 +100,6 @@ static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, uns const char *path1, *path2; const unsigned char *sha1, *sha2; int cmp, pathlen1, pathlen2; - char old_sha1_hex[50]; sha1 = extract(tree1, size1, &path1, &mode1); sha2 = extract(tree2, size2, &path2, &mode2); @@ -151,17 +143,7 @@ static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, uns if (silent) return 0; - if (generate_patch) { - if (!S_ISDIR(mode1)) - diff_change(mode1, mode2, sha1, sha2, base, path1); - } - else { - strcpy(old_sha1_hex, sha1_to_hex(sha1)); - printf("*%06o->%06o\t%s\t%s->%s\t%s%s%c", mode1, mode2, - S_ISDIR(mode1) ? "tree" : "blob", - old_sha1_hex, sha1_to_hex(sha2), base, path1, - line_termination); - } + diff_change(mode1, mode2, sha1, sha2, base, path1); return 0; } @@ -287,11 +269,12 @@ static int diff_tree_sha1_top(const unsigned char *old, const unsigned char *new, const char *base) { int ret; - if (generate_patch) - diff_setup(detect_rename, 0, 0, 0, 0); + + diff_setup(detect_rename, diff_score_opt, 0, + (generate_patch ? -1 : line_termination), + 0, 0); ret = diff_tree_sha1(old, new, base); - if (generate_patch) - diff_flush(); + diff_flush(); return ret; } @@ -301,15 +284,15 @@ static int diff_root_tree(const unsigned char *new, const char *base) void *tree; unsigned long size; - if (generate_patch) - diff_setup(detect_rename, 0, 0, 0, 0); + diff_setup(detect_rename, diff_score_opt, 0, + (generate_patch ? -1 : line_termination), + 0, 0); tree = read_object_with_reference(new, "tree", &size, 0); if (!tree) die("unable to read root tree (%s)", sha1_to_hex(new)); retval = diff_tree("", 0, tree, size, base); free(tree); - if (generate_patch) - diff_flush(); + diff_flush(); return retval; } @@ -485,8 +468,9 @@ int main(int argc, char **argv) recursive = generate_patch = 1; continue; } - if (!strcmp(arg, "-M")) { + if (!strncmp(arg, "-M", 2)) { detect_rename = recursive = generate_patch = 1; + diff_score_opt = diff_scoreopt_parse(arg); continue; } if (!strcmp(arg, "-z")) { diff --git a/diff.c b/diff.c index 92fcc677e..c4b6b76f5 100644 --- a/diff.c +++ b/diff.c @@ -11,6 +11,8 @@ static const char *diff_opts = "-pu"; static unsigned char null_sha1[20] = { 0, }; +#define MAX_SCORE 10000 +#define DEFAULT_MINIMUM_SCORE 5000 static const char *external_diff(void) { @@ -55,7 +57,7 @@ static char *sq_expand(const char *src) const char *cp; char *bp; - /* count bytes needed to store the quoted string. */ + /* count bytes needed to store the quoted string. */ for (cnt = 1, cp = src; *cp; cnt++, cp++) if (*cp == '\'') cnt += 3; @@ -93,7 +95,8 @@ struct diff_spec { static void builtin_diff(const char *name_a, const char *name_b, - struct diff_tempfile *temp) + struct diff_tempfile *temp, + int rename_score) { int i, next_at, cmd_size; const char *diff_cmd = "diff -L'%s%s' -L'%s%s'"; @@ -149,6 +152,10 @@ static void builtin_diff(const char *name_a, printf("new mode %s\n", temp[1].mode); } if (strcmp(name_a, name_b)) { + if (0 < rename_score) + printf("rename similarity index %d%%\n", + (int)(0.5+ + rename_score*100.0/MAX_SCORE)); printf("rename old %s\n", name_a); printf("rename new %s\n", name_b); } @@ -184,7 +191,7 @@ static int work_tree_matches(const char *name, const unsigned char *sha1) * file. Practically, this code only helps when we are used * by diff-cache --cached, which does read the cache before * calling us. - */ + */ if (!active_cache) return 0; @@ -302,9 +309,10 @@ static void remove_tempfile_on_signal(int signo) static int detect_rename; static int reverse_diff; +static int diff_raw_output = -1; static const char **pathspec; static int speccnt; -static int diff_rename_minimum_score; +static int minimum_score; static int matches_pathspec(const char *name) { @@ -334,7 +342,8 @@ static int matches_pathspec(const char *name) static void run_external_diff(const char *name, const char *other, struct diff_spec *one, - struct diff_spec *two) + struct diff_spec *two, + int rename_score) { struct diff_tempfile *temp = diff_temp; pid_t pid; @@ -395,7 +404,7 @@ static void run_external_diff(const char *name, * otherwise we use the built-in one. */ if (one && two) - builtin_diff(name, other ? : name, temp); + builtin_diff(name, other ? : name, temp, rename_score); else printf("* Unmerged path %s\n", name); exit(0); @@ -446,7 +455,7 @@ static void hold_diff(const char *name, die("internal error"); if (!detect_rename) { - run_external_diff(name, NULL, one, two); + run_external_diff(name, NULL, one, two, -1); return; } elem = xmalloc(sizeof(*elem) + strlen(name)); @@ -519,10 +528,10 @@ static void flush_remaining_diff(struct diff_spec_hold *elem, continue; if (on_created_list) run_external_diff(elem->path, NULL, - &null_file_spec, &elem->it); + &null_file_spec, &elem->it, -1); else run_external_diff(elem->path, NULL, - &elem->it, &null_file_spec); + &elem->it, &null_file_spec, -1); } } @@ -541,7 +550,6 @@ static int is_exact_match(struct diff_spec_hold *src, return 0; } -#define MINIMUM_SCORE 5000 int estimate_similarity(struct diff_spec_hold *src, struct diff_spec_hold *dst) { /* src points at a deleted file and dst points at a created @@ -549,20 +557,24 @@ int estimate_similarity(struct diff_spec_hold *src, struct diff_spec_hold *dst) * say src is renamed to dst. * * Compare them and return how similar they are, representing - * the score as an integer between 0 and 10000. 10000 is - * reserved for the case where they match exactly. + * the score as an integer between 0 and 10000, except + * where they match exactly it is considered better than anything + * else. */ void *delta; unsigned long delta_size; + int score; delta_size = ((src->size < dst->size) ? (dst->size - src->size) : (src->size - dst->size)); /* We would not consider rename followed by more than - * 20% edits; that is, delta_size must be smaller than - * (src->size + dst->size)/2 * 0.2, which means... + * minimum_score/MAX_SCORE edits; that is, delta_size must be smaller + * than (src->size + dst->size)/2 * minimum_score/MAX_SCORE, + * which means... */ - if ((src->size + dst->size) < delta_size * 10) + + if ((src->size+dst->size)*minimum_score < delta_size*MAX_SCORE*2) return 0; delta = diff_delta(src->data, src->size, @@ -573,14 +585,17 @@ int estimate_similarity(struct diff_spec_hold *src, struct diff_spec_hold *dst) /* This "delta" is really xdiff with adler32 and all the * overheads but it is a quick and dirty approximation. * - * Now we will give some score to it. Let's say 20% edit gets - * 5000 points and 0% edit gets 9000 points. That is, every - * 1/20000 edit gets 1 point penalty. The amount of penalty is: + * Now we will give some score to it. 100% edit gets + * 0 points and 0% edit gets MAX_SCORE points. That is, every + * 1/MAX_SCORE edit gets 1 point penalty. The amount of penalty is: * - * (delta_size * 2 / (src->size + dst->size)) * 20000 + * (delta_size * 2 / (src->size + dst->size)) * MAX_SCORE * */ - return 9000 - (40000 * delta_size / (src->size+dst->size)); + score = MAX_SCORE-(MAX_SCORE*2*delta_size/(src->size+dst->size)); + if (score < 0) return 0; + if (MAX_SCORE < score) return MAX_SCORE; + return score; } struct diff_score { @@ -596,14 +611,15 @@ static int score_compare(const void *a_, const void *b_) } static void flush_rename_pair(struct diff_spec_hold *src, - struct diff_spec_hold *dst) + struct diff_spec_hold *dst, + int rename_score) { src->flags |= MATCHED; dst->flags |= MATCHED; free_data(src); free_data(dst); run_external_diff(src->path, dst->path, - &src->it, &dst->it); + &src->it, &dst->it, rename_score); } static void free_held_diff(struct diff_spec_hold *list) @@ -637,7 +653,7 @@ void diff_flush(void) continue; if (! is_exact_match(src, dst)) continue; - flush_rename_pair(src, dst); + flush_rename_pair(src, dst, MAX_SCORE); break; } } @@ -670,7 +686,7 @@ void diff_flush(void) } c++; } - qsort(mx, num_create*num_delete, sizeof(*mx), score_compare); + qsort(mx, num_create*num_delete, sizeof(*mx), score_compare); #if 0 for (c = 0; c < num_create * num_delete; c++) { @@ -689,9 +705,9 @@ void diff_flush(void) dst = mx[c].dst; if ((src->flags & MATCHED) || (dst->flags & MATCHED)) continue; - if (mx[c].score < diff_rename_minimum_score) + if (mx[c].score < minimum_score) break; - flush_rename_pair(src, dst); + flush_rename_pair(src, dst, mx[c].score); } free(mx); @@ -703,7 +719,26 @@ void diff_flush(void) createdfile = deletedfile = NULL; } +int diff_scoreopt_parse(const char *opt) +{ + int diglen, num, scale, i; + if (opt[0] != '-' || opt[1] != 'M') + return -1; /* that is not -M option */ + diglen = strspn(opt+2, "0123456789"); + if (diglen == 0 || strlen(opt+2) != diglen) + return 0; /* use default */ + sscanf(opt+2, "%d", &num); + for (i = 0, scale = 1; i < diglen; i++) + scale *= 10; + + /* user says num divided by scale and we say internally that + * is MAX_SCORE * num / scale. + */ + return MAX_SCORE * num / scale; +} + void diff_setup(int detect_rename_, int minimum_score_, int reverse_diff_, + int diff_raw_output_, const char **pathspec_, int speccnt_) { free_held_diff(createdfile); @@ -713,8 +748,14 @@ void diff_setup(int detect_rename_, int minimum_score_, int reverse_diff_, detect_rename = detect_rename_; reverse_diff = reverse_diff_; pathspec = pathspec_; + diff_raw_output = diff_raw_output_; speccnt = speccnt_; - diff_rename_minimum_score = minimum_score_ ? : MINIMUM_SCORE; + minimum_score = minimum_score_ ? : DEFAULT_MINIMUM_SCORE; +} + +static const char *git_object_type(unsigned mode) +{ + return S_ISDIR(mode) ? "tree" : "blob"; } void diff_addremove(int addremove, unsigned mode, @@ -724,6 +765,21 @@ void diff_addremove(int addremove, unsigned mode, char concatpath[PATH_MAX]; struct diff_spec spec[2], *one, *two; + if (0 <= diff_raw_output) { + if (!path) + path = ""; + if (reverse_diff) + addremove = (addremove == '+' ? '-' : '+'); + printf("%c%06o %s %s %s%s%c", + addremove, + mode, + git_object_type(mode), sha1_to_hex(sha1), + base, path, diff_raw_output); + return; + } + if (S_ISDIR(mode)) + return; + memcpy(spec[0].blob_sha1, sha1, 20); spec[0].mode = mode; spec[0].sha1_valid = !!memcmp(sha1, null_sha1, 20); @@ -750,6 +806,29 @@ void diff_change(unsigned old_mode, unsigned new_mode, char concatpath[PATH_MAX]; struct diff_spec spec[2]; + if (0 <= diff_raw_output) { + char old_hex[41]; + strcpy(old_hex, sha1_to_hex(old_sha1)); + + if (!path) + path = ""; + if (reverse_diff) + printf("*%06o->%06o %s %s->%s %s%s%c", + new_mode, old_mode, + git_object_type(new_mode), + sha1_to_hex(new_sha1), old_hex, + base, path, diff_raw_output); + else + printf("*%06o->%06o %s %s->%s %s%s%c", + old_mode, new_mode, + git_object_type(new_mode), + old_hex, sha1_to_hex(new_sha1), + base, path, diff_raw_output); + return; + } + if (S_ISDIR(new_mode)) + return; + if (path) { strcpy(concatpath, base); strcat(concatpath, path); @@ -766,10 +845,15 @@ void diff_change(unsigned old_mode, unsigned new_mode, /* We do not look at changed files as candidate for * rename detection ever. */ - run_external_diff(path ? concatpath : base, NULL, &spec[0], &spec[1]); + run_external_diff(path ? concatpath : base, NULL, + &spec[0], &spec[1], -1); } void diff_unmerge(const char *path) { - run_external_diff(path, NULL, NULL, NULL); + if (0 <= diff_raw_output) { + printf("U %s%c", path, diff_raw_output); + return; + } + run_external_diff(path, NULL, NULL, NULL, -1); } diff --git a/diff.h b/diff.h index cce46491b..86a645afe 100644 --- a/diff.h +++ b/diff.h @@ -17,8 +17,10 @@ extern void diff_change(unsigned mode1, unsigned mode2, extern void diff_unmerge(const char *path); +extern int diff_scoreopt_parse(const char *opt); + extern void diff_setup(int detect_rename, int minimum_score, - int reverse, + int reverse, int raw_output, const char **spec, int cnt); extern void diff_flush(void); diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh index 7ae9d658c..9a557129d 100755 --- a/t/t0000-basic.sh +++ b/t/t0000-basic.sh @@ -166,7 +166,7 @@ cat >expected <<\EOF EOF test_expect_success \ 'validate git-diff-files output for a know cache/work tree state.' \ - 'git-diff-files >current && cmp -s current expected' + 'git-diff-files >current && diff >/dev/null -b current expected' test_expect_success \ 'git-update-cache --refresh should succeed.' \ diff --git a/t/t2002-checkout-cache-u.sh b/t/t2002-checkout-cache-u.sh index 8c789d824..69146acc3 100644 --- a/t/t2002-checkout-cache-u.sh +++ b/t/t2002-checkout-cache-u.sh @@ -29,3 +29,5 @@ rm -f path0 && git-read-tree $t && git-checkout-cache -u -f -a && git-diff-files | diff - /dev/null' + +test_done diff --git a/t/t4001-diff-rename.sh b/t/t4001-diff-rename.sh index 44225f7a7..065ddb71a 100644 --- a/t/t4001-diff-rename.sh +++ b/t/t4001-diff-rename.sh @@ -58,3 +58,9 @@ rename new path1 Line 13 Line 14 EOF + +test_expect_success \ + 'validate the output.' \ + 'diff -I "rename similarity.*" >/dev/null current expected' + +test_done -- 2.26.2