Merge branch 'jk/diff-follow-must-take-one-pathspec'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 17:07:09 +0000 (10:07 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jun 2014 17:07:09 +0000 (10:07 -0700)
* jk/diff-follow-must-take-one-pathspec:
move "--follow needs one pathspec" rule to diff_setup_done

1  2 
builtin/log.c
diff.c
diff --combined builtin/log.c
index 39e883635279ad21f3ddd7b224f484b9b04700b0,00a4ce602cc3f6784bb51784766c9ef2f655f0e9..3b6a6bbaddc8795c148763bb6d71136900a8fbb4
@@@ -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 f66716fab4b477c58434126b4aaa4b4372ce163a,a99b925ac74bc4f513bd9d860e757f385cca871b..de25819e4edede0a24ee30b202dee478165b4fed
--- 1/diff.c
--- 2/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;
                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)
        return temp;
  }
  
 +static void add_external_diff_name(struct argv_array *argv,
 +                                 const char *name,
 +                                 struct diff_filespec *df)
 +{
 +      struct diff_tempfile *temp = prepare_temp_file(name, df);
 +      argv_array_push(argv, temp->name);
 +      argv_array_push(argv, temp->hex);
 +      argv_array_push(argv, temp->mode);
 +}
 +
  /* An external diff command takes:
   *
   * diff-cmd name infile1 infile1-sha1 infile1-mode \
@@@ -2902,36 -2899,41 +2902,36 @@@ 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];
 -      int retval;
 -      const char **arg = &spawn_arg[0];
 +      struct argv_array argv = ARGV_ARRAY_INIT;
 +      struct argv_array env = ARGV_ARRAY_INIT;
 +      struct diff_queue_struct *q = &diff_queued_diff;
 +
 +      argv_array_push(&argv, pgm);
 +      argv_array_push(&argv, name);
  
        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;
 -              if (other) {
 -                      *arg++ = other;
 -                      *arg++ = xfrm_msg;
 +              add_external_diff_name(&argv, name, one);
 +              if (!other)
 +                      add_external_diff_name(&argv, name, two);
 +              else {
 +                      add_external_diff_name(&argv, other, two);
 +                      argv_array_push(&argv, other);
 +                      argv_array_push(&argv, xfrm_msg);
                }
 -      } else {
 -              *arg++ = pgm;
 -              *arg++ = name;
        }
 -      *arg = NULL;
 -      fflush(NULL);
 -      retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
 +
 +      argv_array_pushf(&env, "GIT_DIFF_PATH_COUNTER=%d", ++o->diff_path_counter);
 +      argv_array_pushf(&env, "GIT_DIFF_PATH_TOTAL=%d", q->nr);
 +
 +      if (run_command_v_opt_cd_env(argv.argv, RUN_USING_SHELL, NULL, env.argv))
 +              die(_("external diff died, stopping at %s"), name);
 +
        remove_tempfile();
 -      if (retval) {
 -              fprintf(stderr, "external diff died, stopping at %s.\n", name);
 -              exit(1);
 -      }
 +      argv_array_clear(&argv);
 +      argv_array_clear(&env);
  }
  
  static int similarity_index(struct diff_filepair *p)
@@@ -3040,7 -3042,7 +3040,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)
@@@ -3199,15 -3201,12 +3199,15 @@@ void diff_setup(struct diff_options *op
        options->context = diff_context_default;
        DIFF_OPT_SET(options, RENAME_EMPTY);
  
 +      /* pathchange left =NULL by default */
        options->change = diff_change;
        options->add_remove = diff_addremove;
        options->use_color = diff_use_color_default;
        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) {
@@@ -3319,7 -3318,8 +3319,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)
        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))
@@@ -3391,10 -3394,10 +3394,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;
        }
@@@ -3422,7 -3425,7 +3425,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);
                                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);
                                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);
                                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);
@@@ -3580,6 -3583,14 +3583,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;
@@@ -3606,15 -3617,15 +3609,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);
        }
                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);
        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);
                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;
        }
                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\"");
                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;
                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;
        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 */
        }
        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;
@@@ -3899,22 -3910,22 +3902,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, -Cor -B option */
  
        opt1 = parse_rename_score(&opt);
        if (cmd != 'B')
@@@ -3938,7 -3949,11 +3941,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;
  }
  
@@@ -4108,9 -4123,9 +4111,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)
@@@ -4313,7 -4328,7 +4316,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);
@@@ -4666,38 -4681,6 +4669,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;
        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 {
                        /*
@@@ -4744,7 -4747,6 +4747,7 @@@ void diffcore_fix_diff_index(struct dif
  
  void diffcore_std(struct diff_options *options)
  {
 +      /* NOTE please keep the following in sync with diff_tree_combined() */
        if (options->skip_stat_unmatch)
                diffcore_skip_stat_unmatch(options);
        if (!options->found_follow) {
@@@ -4872,7 -4874,6 +4875,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))
        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)