From: Junio C Hamano Date: Wed, 25 Jun 2014 18:47:23 +0000 (-0700) Subject: Merge branch 'jk/diff-follow-must-take-one-pathspec' into maint X-Git-Tag: v2.0.1~16 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/cb4575fb181c418a85231ec962c01ef314ec9dfc?ds=inline;hp=-c Merge branch 'jk/diff-follow-must-take-one-pathspec' into maint "git format-patch" did not enforce the rule that the "--follow" option from the log/diff family of commands must be used with exactly one pathspec. * jk/diff-follow-must-take-one-pathspec: move "--follow needs one pathspec" rule to diff_setup_done --- cb4575fb181c418a85231ec962c01ef314ec9dfc diff --combined builtin/log.c index 39e8836352,00a4ce602c..3b6a6bbadd --- a/builtin/log.c +++ b/builtin/log.c @@@ -158,13 -158,9 +158,9 @@@ static void cmd_log_init_finish(int arg if (rev->show_notes) init_display_notes(&rev->notes_opt); - if (rev->diffopt.pickaxe || rev->diffopt.filter) + if (rev->diffopt.pickaxe || rev->diffopt.filter || + DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) rev->always_show_header = 0; - if (DIFF_OPT_TST(&rev->diffopt, FOLLOW_RENAMES)) { - rev->always_show_header = 0; - if (rev->diffopt.pathspec.nr != 1) - usage("git logs can only follow renames on one pathname at a time"); - } if (source) rev->show_source = 1; @@@ -391,7 -387,7 +387,7 @@@ static int git_log_config(const char *v default_show_root = git_config_bool(var, value); return 0; } - if (!prefixcmp(var, "color.decorate.")) + if (starts_with(var, "color.decorate.")) return parse_decorate_color_config(var, 15, value); if (!strcmp(var, "log.mailmap")) { use_mailmap_config = git_config_bool(var, value); @@@ -477,7 -473,7 +473,7 @@@ static int show_tag_object(const unsign int new_offset = offset + 1; while (new_offset < size && buf[new_offset++] != '\n') ; /* do nothing */ - if (!prefixcmp(buf + offset, "tagger ")) + if (starts_with(buf + offset, "tagger ")) show_tagger(buf + offset + 7, new_offset - offset - 7, rev); offset = new_offset; @@@ -503,7 -499,7 +499,7 @@@ static void show_rev_tweak_rev(struct r /* There was no "-m" on the command line */ rev->ignore_merges = 0; if (!rev->first_parent_only && !rev->combine_merges) { - /* No "--first-parent", "-c", nor "--cc" */ + /* No "--first-parent", "-c", or "--cc" */ rev->combine_merges = 1; rev->dense_combined_merges = 1; } @@@ -882,7 -878,7 +878,7 @@@ static char *find_branch_name(struct re ref = rev->cmdline.rev[positive].name; tip_sha1 = rev->cmdline.rev[positive].item->sha1; if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) && - !prefixcmp(full_ref, "refs/heads/") && + starts_with(full_ref, "refs/heads/") && !hashcmp(tip_sha1, branch_sha1)) branch = xstrdup(full_ref + strlen("refs/heads/")); free(full_ref); @@@ -1388,7 -1384,7 +1384,7 @@@ int cmd_format_patch(int argc, const ch unsigned char sha1[20]; const char *ref; ref = resolve_ref_unsafe("HEAD", sha1, 1, NULL); - if (ref && !prefixcmp(ref, "refs/heads/")) + if (ref && starts_with(ref, "refs/heads/")) branch_name = xstrdup(ref + strlen("refs/heads/")); else branch_name = xstrdup(""); /* no branch */ diff --combined diff.c index f72769a1c4,a99b925ac7..68bb8c5a84 --- a/diff.c +++ b/diff.c @@@ -16,7 -16,6 +16,7 @@@ #include "submodule.h" #include "ll-merge.h" #include "string-list.h" +#include "argv-array.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@@ -31,7 -30,6 +31,7 @@@ static int diff_use_color_default = -1 static int diff_context_default = 3; static const char *diff_word_regex_cfg; static const char *external_diff_cmd_cfg; +static const char *diff_order_file_cfg; int diff_auto_refresh_index = 1; static int diff_mnemonic_prefix; static int diff_no_prefix; @@@ -203,8 -201,6 +203,8 @@@ int git_diff_ui_config(const char *var return git_config_string(&external_diff_cmd_cfg, var, value); if (!strcmp(var, "diff.wordregex")) return git_config_string(&diff_word_regex_cfg, var, value); + if (!strcmp(var, "diff.orderfile")) + return git_config_pathname(&diff_order_file_cfg, var, value); if (!strcmp(var, "diff.ignoresubmodules")) handle_ignore_submodules_arg(&default_diff_options, value); @@@ -239,7 -235,7 +239,7 @@@ int git_diff_basic_config(const char *v if (userdiff_config(var, value) < 0) return -1; - if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) { + if (starts_with(var, "diff.color.") || starts_with(var, "color.diff.")) { int slot = parse_diff_color_slot(var, 11); if (slot < 0) return 0; @@@ -268,7 -264,7 +268,7 @@@ return 0; } - if (!prefixcmp(var, "submodule.")) + if (starts_with(var, "submodule.")) return parse_submodule_config_option(var, value); return git_default_config(var, value, cb); @@@ -1219,7 -1215,7 +1219,7 @@@ static void fn_out_consume(void *priv, diff_words_append(line, len, &ecbdata->diff_words->plus); return; - } else if (!prefixcmp(line, "\\ ")) { + } else if (starts_with(line, "\\ ")) { /* * Eat the "no newline at eof" marker as if we * saw a "+" or "-" line with nothing on it, @@@ -1362,7 -1358,11 +1362,7 @@@ static struct diffstat_file *diffstat_a { struct diffstat_file *x; x = xcalloc(sizeof (*x), 1); - if (diffstat->nr == diffstat->alloc) { - diffstat->alloc = alloc_nr(diffstat->alloc); - diffstat->files = xrealloc(diffstat->files, - diffstat->alloc * sizeof(x)); - } + ALLOC_GROW(diffstat->files, diffstat->nr + 1, diffstat->alloc); diffstat->files[diffstat->nr++] = x; if (name_b) { x->from_name = xstrdup(name_a); @@@ -1462,12 -1462,20 +1462,12 @@@ int print_stat_summary(FILE *fp, int fi * but nothing about added/removed lines? Is this a bug in Git?"). */ if (insertions || deletions == 0) { - /* - * TRANSLATORS: "+" in (+) is a line addition marker; - * do not translate it. - */ strbuf_addf(&sb, (insertions == 1) ? ", %d insertion(+)" : ", %d insertions(+)", insertions); } if (deletions || insertions == 0) { - /* - * TRANSLATORS: "-" in (-) is a line removal marker; - * do not translate it. - */ strbuf_addf(&sb, (deletions == 1) ? ", %d deletion(-)" : ", %d deletions(-)", deletions); @@@ -2379,9 -2387,9 +2379,9 @@@ static void builtin_diff(const char *na xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); if (!diffopts) ; - else if (!prefixcmp(diffopts, "--unified=")) + else if (starts_with(diffopts, "--unified=")) xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10); - else if (!prefixcmp(diffopts, "-u")) + else if (starts_with(diffopts, "-u")) xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10); if (o->word_diff) init_diff_words_data(&ecbdata, o, one, two); @@@ -2834,9 -2842,8 +2834,9 @@@ static struct diff_tempfile *prepare_te remove_tempfile_installed = 1; } - if (!one->sha1_valid || - reuse_worktree_file(name, one->sha1, 1)) { + if (!S_ISGITLINK(one->mode) && + (!one->sha1_valid || + reuse_worktree_file(name, one->sha1, 1))) { struct stat st; if (lstat(name, &st) < 0) { if (errno == ENOENT) @@@ -2892,48 -2899,37 +2892,48 @@@ static void run_external_diff(const cha struct diff_filespec *one, struct diff_filespec *two, const char *xfrm_msg, - int complete_rewrite) + int complete_rewrite, + struct diff_options *o) { - const char *spawn_arg[10]; + struct argv_array argv = ARGV_ARRAY_INIT; int retval; - const char **arg = &spawn_arg[0]; + struct diff_queue_struct *q = &diff_queued_diff; + const char *env[3] = { NULL }; + char env_counter[50]; + char env_total[50]; if (one && two) { struct diff_tempfile *temp_one, *temp_two; const char *othername = (other ? other : name); temp_one = prepare_temp_file(name, one); temp_two = prepare_temp_file(othername, two); - *arg++ = pgm; - *arg++ = name; - *arg++ = temp_one->name; - *arg++ = temp_one->hex; - *arg++ = temp_one->mode; - *arg++ = temp_two->name; - *arg++ = temp_two->hex; - *arg++ = temp_two->mode; + argv_array_push(&argv, pgm); + argv_array_push(&argv, name); + argv_array_push(&argv, temp_one->name); + argv_array_push(&argv, temp_one->hex); + argv_array_push(&argv, temp_one->mode); + argv_array_push(&argv, temp_two->name); + argv_array_push(&argv, temp_two->hex); + argv_array_push(&argv, temp_two->mode); if (other) { - *arg++ = other; - *arg++ = xfrm_msg; + argv_array_push(&argv, other); + argv_array_push(&argv, xfrm_msg); } } else { - *arg++ = pgm; - *arg++ = name; + argv_array_push(&argv, pgm); + argv_array_push(&argv, name); } - *arg = NULL; fflush(NULL); - retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL); + + env[0] = env_counter; + snprintf(env_counter, sizeof(env_counter), "GIT_DIFF_PATH_COUNTER=%d", + ++o->diff_path_counter); + env[1] = env_total; + snprintf(env_total, sizeof(env_total), "GIT_DIFF_PATH_TOTAL=%d", q->nr); + + retval = run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env); remove_tempfile(); + argv_array_clear(&argv); if (retval) { fprintf(stderr, "external diff died, stopping at %s.\n", name); exit(1); @@@ -3046,7 -3042,7 +3046,7 @@@ static void run_diff_cmd(const char *pg if (pgm) { run_external_diff(pgm, name, other, one, two, xfrm_msg, - complete_rewrite); + complete_rewrite, o); return; } if (one && two) @@@ -3211,8 -3207,6 +3211,8 @@@ void diff_setup(struct diff_options *op options->detect_rename = diff_detect_rename_default; options->xdl_opts |= diff_algorithm; + options->orderfile = diff_order_file_cfg; + if (diff_no_prefix) { options->a_prefix = options->b_prefix = ""; } else if (!diff_mnemonic_prefix) { @@@ -3324,7 -3318,8 +3324,10 @@@ void diff_setup_done(struct diff_option DIFF_OPT_SET(options, EXIT_WITH_STATUS); } + options->diff_path_counter = 0; ++ + if (DIFF_OPT_TST(options, FOLLOW_RENAMES) && options->pathspec.nr != 1) + die(_("--follow requires exactly one pathspec")); } static int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val) @@@ -3354,11 -3349,14 +3357,11 @@@ if (c != '-') return 0; arg++; - eq = strchr(arg, '='); - if (eq) - len = eq - arg; - else - len = strlen(arg); + eq = strchrnul(arg, '='); + len = eq - arg; if (!len || strncmp(arg, arg_long, len)) return 0; - if (eq) { + if (*eq) { int n; char *end; if (!isdigit(*++eq)) @@@ -3396,10 -3394,10 +3399,10 @@@ int parse_long_opt(const char *opt, con if (arg[0] != '-' || arg[1] != '-') return 0; arg += strlen("--"); - if (prefixcmp(arg, opt)) + if (!starts_with(arg, opt)) return 0; arg += strlen(opt); - if (*arg == '=') { /* sticked form: --option=value */ + if (*arg == '=') { /* stuck form: --option=value */ *optarg = arg + 1; return 1; } @@@ -3427,7 -3425,7 +3430,7 @@@ static int stat_opt(struct diff_option switch (*arg) { case '-': - if (!prefixcmp(arg, "-width")) { + if (starts_with(arg, "-width")) { arg += strlen("-width"); if (*arg == '=') width = strtoul(arg + 1, &end, 10); @@@ -3437,7 -3435,7 +3440,7 @@@ width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (!prefixcmp(arg, "-name-width")) { + } else if (starts_with(arg, "-name-width")) { arg += strlen("-name-width"); if (*arg == '=') name_width = strtoul(arg + 1, &end, 10); @@@ -3447,7 -3445,7 +3450,7 @@@ name_width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (!prefixcmp(arg, "-graph-width")) { + } else if (starts_with(arg, "-graph-width")) { arg += strlen("-graph-width"); if (*arg == '=') graph_width = strtoul(arg + 1, &end, 10); @@@ -3457,7 -3455,7 +3460,7 @@@ graph_width = strtoul(av[1], &end, 10); argcount = 2; } - } else if (!prefixcmp(arg, "-count")) { + } else if (starts_with(arg, "-count")) { arg += strlen("-count"); if (*arg == '=') count = strtoul(arg + 1, &end, 10); @@@ -3585,6 -3583,14 +3588,6 @@@ static int parse_diff_filter_opt(const return 0; } -/* Used only by "diff-files" and "diff --no-index" */ -void handle_deprecated_show_diff_q(struct diff_options *opt) -{ - warning("'diff -q' and 'diff-files -q' are deprecated."); - warning("Use 'diff --diff-filter=d' instead to ignore deleted filepairs."); - parse_diff_filter_opt("d", opt); -} - static void enable_patch_output(int *fmt) { *fmt &= ~DIFF_FORMAT_NO_OUTPUT; *fmt |= DIFF_FORMAT_PATCH; @@@ -3611,15 -3617,15 +3614,15 @@@ int diff_opt_parse(struct diff_options options->output_format |= DIFF_FORMAT_SHORTSTAT; else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat")) return parse_dirstat_opt(options, ""); - else if (!prefixcmp(arg, "-X")) + else if (starts_with(arg, "-X")) return parse_dirstat_opt(options, arg + 2); - else if (!prefixcmp(arg, "--dirstat=")) + else if (starts_with(arg, "--dirstat=")) return parse_dirstat_opt(options, arg + 10); else if (!strcmp(arg, "--cumulative")) return parse_dirstat_opt(options, "cumulative"); else if (!strcmp(arg, "--dirstat-by-file")) return parse_dirstat_opt(options, "files"); - else if (!prefixcmp(arg, "--dirstat-by-file=")) { + else if (starts_with(arg, "--dirstat-by-file=")) { parse_dirstat_opt(options, "files"); return parse_dirstat_opt(options, arg + 18); } @@@ -3636,17 -3642,17 +3639,17 @@@ options->output_format |= DIFF_FORMAT_NAME_STATUS; else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch")) options->output_format |= DIFF_FORMAT_NO_OUTPUT; - else if (!prefixcmp(arg, "--stat")) + else if (starts_with(arg, "--stat")) /* --stat, --stat-width, --stat-name-width, or --stat-count */ return stat_opt(options, av); /* renames options */ - else if (!prefixcmp(arg, "-B") || !prefixcmp(arg, "--break-rewrites=") || + else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") || !strcmp(arg, "--break-rewrites")) { if ((options->break_opt = diff_scoreopt_parse(arg)) == -1) return error("invalid argument to -B: %s", arg+2); } - else if (!prefixcmp(arg, "-M") || !prefixcmp(arg, "--find-renames=") || + else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") || !strcmp(arg, "--find-renames")) { if ((options->rename_score = diff_scoreopt_parse(arg)) == -1) return error("invalid argument to -M: %s", arg+2); @@@ -3655,7 -3661,7 +3658,7 @@@ else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) { options->irreversible_delete = 1; } - else if (!prefixcmp(arg, "-C") || !prefixcmp(arg, "--find-copies=") || + else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") || !strcmp(arg, "--find-copies")) { if (options->detect_rename == DIFF_DETECT_COPY) DIFF_OPT_SET(options, FIND_COPIES_HARDER); @@@ -3671,7 -3677,7 +3674,7 @@@ DIFF_OPT_CLR(options, RENAME_EMPTY); else if (!strcmp(arg, "--relative")) DIFF_OPT_SET(options, RELATIVE_NAME); - else if (!prefixcmp(arg, "--relative=")) { + else if (starts_with(arg, "--relative=")) { DIFF_OPT_SET(options, RELATIVE_NAME); options->prefix = arg + 11; } @@@ -3724,7 -3730,7 +3727,7 @@@ DIFF_OPT_CLR(options, FOLLOW_RENAMES); else if (!strcmp(arg, "--color")) options->use_color = 1; - else if (!prefixcmp(arg, "--color=")) { + else if (starts_with(arg, "--color=")) { int value = git_config_colorbool(NULL, arg+8); if (value < 0) return error("option `color' expects \"always\", \"auto\", or \"never\""); @@@ -3736,7 -3742,7 +3739,7 @@@ options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; } - else if (!prefixcmp(arg, "--color-words=")) { + else if (starts_with(arg, "--color-words=")) { options->use_color = 1; options->word_diff = DIFF_WORDS_COLOR; options->word_regex = arg + 14; @@@ -3745,7 -3751,7 +3748,7 @@@ if (options->word_diff == DIFF_WORDS_NONE) options->word_diff = DIFF_WORDS_PLAIN; } - else if (!prefixcmp(arg, "--word-diff=")) { + else if (starts_with(arg, "--word-diff=")) { const char *type = arg + 12; if (!strcmp(type, "plain")) options->word_diff = DIFF_WORDS_PLAIN; @@@ -3781,12 -3787,12 +3784,12 @@@ else if (!strcmp(arg, "--ignore-submodules")) { DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(options, "all"); - } else if (!prefixcmp(arg, "--ignore-submodules=")) { + } else if (starts_with(arg, "--ignore-submodules=")) { DIFF_OPT_SET(options, OVERRIDE_SUBMODULE_CONFIG); handle_ignore_submodules_arg(options, arg + 20); } else if (!strcmp(arg, "--submodule")) DIFF_OPT_SET(options, SUBMODULE_LOG); - else if (!prefixcmp(arg, "--submodule=")) + else if (starts_with(arg, "--submodule=")) return parse_submodule_opt(options, arg + 12); /* misc options */ @@@ -3822,7 -3828,7 +3825,7 @@@ } else if (!strcmp(arg, "--abbrev")) options->abbrev = DEFAULT_ABBREV; - else if (!prefixcmp(arg, "--abbrev=")) { + else if (starts_with(arg, "--abbrev=")) { options->abbrev = strtoul(arg + 9, NULL, 10); if (options->abbrev < MINIMUM_ABBREV) options->abbrev = MINIMUM_ABBREV; @@@ -3904,22 -3910,22 +3907,22 @@@ static int diff_scoreopt_parse(const ch cmd = *opt++; if (cmd == '-') { /* convert the long-form arguments into short-form versions */ - if (!prefixcmp(opt, "break-rewrites")) { + if (starts_with(opt, "break-rewrites")) { opt += strlen("break-rewrites"); if (*opt == 0 || *opt++ == '=') cmd = 'B'; - } else if (!prefixcmp(opt, "find-copies")) { + } else if (starts_with(opt, "find-copies")) { opt += strlen("find-copies"); if (*opt == 0 || *opt++ == '=') cmd = 'C'; - } else if (!prefixcmp(opt, "find-renames")) { + } else if (starts_with(opt, "find-renames")) { opt += strlen("find-renames"); if (*opt == 0 || *opt++ == '=') cmd = 'M'; } } if (cmd != 'M' && cmd != 'C' && cmd != 'B') - return -1; /* that is not a -M, -C nor -B option */ + return -1; /* that is not a -M, -C, or -B option */ opt1 = parse_rename_score(&opt); if (cmd != 'B') @@@ -3943,7 -3949,11 +3946,7 @@@ struct diff_queue_struct diff_queued_di void diff_q(struct diff_queue_struct *queue, struct diff_filepair *dp) { - if (queue->alloc <= queue->nr) { - queue->alloc = alloc_nr(queue->alloc); - queue->queue = xrealloc(queue->queue, - sizeof(dp) * queue->alloc); - } + ALLOC_GROW(queue->queue, queue->nr + 1, queue->alloc); queue->queue[queue->nr++] = dp; } @@@ -4113,9 -4123,9 +4116,9 @@@ void diff_debug_filespec(struct diff_fi DIFF_FILE_VALID(s) ? "valid" : "invalid", s->mode, s->sha1_valid ? sha1_to_hex(s->sha1) : ""); - fprintf(stderr, "queue[%d] %s size %lu flags %d\n", + fprintf(stderr, "queue[%d] %s size %lu\n", x, one ? one : "", - s->size, s->xfrm_flags); + s->size); } void diff_debug_filepair(const struct diff_filepair *p, int i) @@@ -4318,7 -4328,7 +4321,7 @@@ static void patch_id_consume(void *priv int new_len; /* Ignore line numbers when computing the SHA1 of the patch */ - if (!prefixcmp(line, "@@ -")) + if (starts_with(line, "@@ -")) return; new_len = remove_space(line, len); @@@ -4671,38 -4681,6 +4674,38 @@@ static int diff_filespec_is_identical(s return !memcmp(one->data, two->data, one->size); } +static int diff_filespec_check_stat_unmatch(struct diff_filepair *p) +{ + if (p->done_skip_stat_unmatch) + return p->skip_stat_unmatch_result; + + p->done_skip_stat_unmatch = 1; + p->skip_stat_unmatch_result = 0; + /* + * 1. Entries that come from stat info dirtiness + * always have both sides (iow, not create/delete), + * one side of the object name is unknown, with + * the same mode and size. Keep the ones that + * do not match these criteria. They have real + * differences. + * + * 2. At this point, the file is known to be modified, + * with the same mode and size, and the object + * name of one side is unknown. Need to inspect + * the identical contents. + */ + if (!DIFF_FILE_VALID(p->one) || /* (1) */ + !DIFF_FILE_VALID(p->two) || + (p->one->sha1_valid && p->two->sha1_valid) || + (p->one->mode != p->two->mode) || + diff_populate_filespec(p->one, 1) || + diff_populate_filespec(p->two, 1) || + (p->one->size != p->two->size) || + !diff_filespec_is_identical(p->one, p->two)) /* (2) */ + p->skip_stat_unmatch_result = 1; + return p->skip_stat_unmatch_result; +} + static void diffcore_skip_stat_unmatch(struct diff_options *diffopt) { int i; @@@ -4713,7 -4691,27 +4716,7 @@@ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; - /* - * 1. Entries that come from stat info dirtiness - * always have both sides (iow, not create/delete), - * one side of the object name is unknown, with - * the same mode and size. Keep the ones that - * do not match these criteria. They have real - * differences. - * - * 2. At this point, the file is known to be modified, - * with the same mode and size, and the object - * name of one side is unknown. Need to inspect - * the identical contents. - */ - if (!DIFF_FILE_VALID(p->one) || /* (1) */ - !DIFF_FILE_VALID(p->two) || - (p->one->sha1_valid && p->two->sha1_valid) || - (p->one->mode != p->two->mode) || - diff_populate_filespec(p->one, 1) || - diff_populate_filespec(p->two, 1) || - (p->one->size != p->two->size) || - !diff_filespec_is_identical(p->one, p->two)) /* (2) */ + if (diff_filespec_check_stat_unmatch(p)) diff_q(&outq, p); else { /* @@@ -4876,7 -4874,6 +4879,7 @@@ void diff_change(struct diff_options *o unsigned old_dirty_submodule, unsigned new_dirty_submodule) { struct diff_filespec *one, *two; + struct diff_filepair *p; if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) && is_submodule_ignored(concatpath, options)) @@@ -4903,16 -4900,10 +4906,16 @@@ fill_filespec(two, new_sha1, new_sha1_valid, new_mode); one->dirty_submodule = old_dirty_submodule; two->dirty_submodule = new_dirty_submodule; + p = diff_queue(&diff_queued_diff, one, two); - diff_queue(&diff_queued_diff, one, two); - if (!DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) - DIFF_OPT_SET(options, HAS_CHANGES); + if (DIFF_OPT_TST(options, DIFF_FROM_CONTENTS)) + return; + + if (DIFF_OPT_TST(options, QUICK) && options->skip_stat_unmatch && + !diff_filespec_check_stat_unmatch(p)) + return; + + DIFF_OPT_SET(options, HAS_CHANGES); } struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path)