Merge branch 'jc/rename-degrade-cc-to-c'
authorJunio C Hamano <gitster@pobox.com>
Thu, 28 Apr 2011 21:11:43 +0000 (14:11 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 28 Apr 2011 21:11:43 +0000 (14:11 -0700)
* jc/rename-degrade-cc-to-c:
diffcore-rename: fall back to -C when -C -C busts the rename limit
diffcore-rename: record filepair for rename src
diffcore-rename: refactor "too many candidates" logic
builtin/diff.c: remove duplicated call to diff_result_code()

1  2 
builtin/diff.c
builtin/log.c
diff.c
merge-recursive.c
t/t4001-diff-rename.sh
diff --combined builtin/diff.c
index 717fa1a3414e18cbdd4c6eb1e0785761687688d7,171ac39274ed8f2e7d5a03de2f88a280b9276b2f..14bd14fce0fa6b9c9b047142d23d4a2237b57a36
@@@ -71,9 -71,9 +71,9 @@@ static int builtin_diff_b_f(struct rev_
                usage(builtin_diff_usage);
  
        if (lstat(path, &st))
 -              die_errno("failed to stat '%s'", path);
 +              die_errno(_("failed to stat '%s'"), path);
        if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
 -              die("'%s': not a regular file or symlink", path);
 +              die(_("'%s': not a regular file or symlink"), path);
  
        diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
  
@@@ -197,12 -197,16 +197,11 @@@ static void refresh_index_quietly(void
        discard_cache();
        read_cache();
        refresh_cache(REFRESH_QUIET|REFRESH_UNMERGED);
 -
 -      if (active_cache_changed &&
 -          !write_cache(fd, active_cache, active_nr))
 -              commit_locked_index(lock_file);
 -
 -      rollback_lock_file(lock_file);
 +      update_index_if_able(&the_index, lock_file);
  }
  
  static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv)
  {
-       int result;
        unsigned int options = 0;
  
        while (1 < argc && argv[1][0] == '-') {
                else if (!strcmp(argv[1], "-h"))
                        usage(builtin_diff_usage);
                else
 -                      return error("invalid option: %s", argv[1]);
 +                      return error(_("invalid option: %s"), argv[1]);
                argv++; argc--;
        }
  
                perror("read_cache_preload");
                return -1;
        }
-       result = run_diff_files(revs, options);
-       return diff_result_code(&revs->diffopt, result);
+       return run_diff_files(revs, options);
  }
  
  int cmd_diff(int argc, const char **argv, const char *prefix)
        DIFF_OPT_SET(&rev.diffopt, ALLOW_TEXTCONV);
  
        if (nongit)
 -              die("Not a git repository");
 +              die(_("Not a git repository"));
        argc = setup_revisions(argc, argv, &rev, NULL);
        if (!rev.diffopt.output_format) {
                rev.diffopt.output_format = DIFF_FORMAT_PATCH;
                if (diff_setup_done(&rev.diffopt) < 0)
 -                      die("diff_setup_done failed");
 +                      die(_("diff_setup_done failed"));
        }
  
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
                        obj = parse_object(obj->sha1);
                obj = deref_tag(obj, NULL, 0);
                if (!obj)
 -                      die("invalid object '%s' given.", name);
 +                      die(_("invalid object '%s' given."), name);
                if (obj->type == OBJ_COMMIT)
                        obj = &((struct commit *)obj)->tree->object;
                if (obj->type == OBJ_TREE) {
                        if (ARRAY_SIZE(ent) <= ents)
 -                              die("more than %d trees given: '%s'",
 +                              die(_("more than %d trees given: '%s'"),
                                    (int) ARRAY_SIZE(ent), name);
                        obj->flags |= flags;
                        ent[ents].item = obj;
                }
                if (obj->type == OBJ_BLOB) {
                        if (2 <= blobs)
 -                              die("more than two blobs given: '%s'", name);
 +                              die(_("more than two blobs given: '%s'"), name);
                        hashcpy(blob[blobs].sha1, obj->sha1);
                        blob[blobs].name = name;
                        blob[blobs].mode = list->mode;
                        continue;
  
                }
 -              die("unhandled object '%s' given.", name);
 +              die(_("unhandled object '%s' given."), name);
        }
        if (rev.prune_data.nr) {
                if (!path)
diff --combined builtin/log.c
index c6dce9b8950410b08e13c11aa940404a7fcd2b78,707fd5706c37290bd65fcdf1f482202c54205881..55abe07610bce0a959b1878be128e09906f8fea4
@@@ -49,8 -49,13 +49,8 @@@ static int parse_decoration_style(cons
        return -1;
  }
  
 -static void cmd_log_init(int argc, const char **argv, const char *prefix,
 -                       struct rev_info *rev, struct setup_revision_opt *opt)
 +static void cmd_log_init_defaults(struct rev_info *rev)
  {
 -      int i;
 -      int decoration_given = 0;
 -      struct userformat_want w;
 -
        rev->abbrev = DEFAULT_ABBREV;
        rev->commit_format = CMIT_FMT_DEFAULT;
        if (fmt_pretty)
  
        if (default_date_mode)
                rev->date_mode = parse_date_format(default_date_mode);
 +}
  
 +static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 +                       struct rev_info *rev, struct setup_revision_opt *opt)
 +{
 +      int i;
 +      int decoration_given = 0;
 +      struct userformat_want w;
        /*
         * Check for -h before setup_revisions(), or "git log -h" will
         * fail when run without a git directory.
                        const char *v = skip_prefix(arg, "--decorate=");
                        decoration_style = parse_decoration_style(arg, v);
                        if (decoration_style < 0)
 -                              die("invalid --decorate option: %s", arg);
 +                              die(_("invalid --decorate option: %s"), arg);
                        decoration_given = 1;
                } else if (!strcmp(arg, "--no-decorate")) {
                        decoration_style = 0;
                } else if (!strcmp(arg, "-h")) {
                        usage(builtin_log_usage);
                } else
 -                      die("unrecognized argument: %s", arg);
 +                      die(_("unrecognized argument: %s"), arg);
        }
  
        /*
        setup_pager();
  }
  
 +static void cmd_log_init(int argc, const char **argv, const char *prefix,
 +                       struct rev_info *rev, struct setup_revision_opt *opt)
 +{
 +      cmd_log_init_defaults(rev);
 +      cmd_log_init_finish(argc, argv, prefix, rev, opt);
 +}
 +
  /*
   * This gives a rough estimate for how many commits we
   * will print out in the list.
@@@ -162,7 -153,7 +162,7 @@@ static void show_early_header(struct re
                if (rev->commit_format != CMIT_FMT_ONELINE)
                        putchar(rev->diffopt.line_termination);
        }
 -      printf("Final output: %d %s\n", nr, stage);
 +      printf(_("Final output: %d %s\n"), nr, stage);
  }
  
  static struct itimerval early_output_timer;
@@@ -256,12 -247,14 +256,14 @@@ static void finish_early_output(struct 
  static int cmd_log_walk(struct rev_info *rev)
  {
        struct commit *commit;
+       int saved_nrl = 0;
+       int saved_dcctc = 0;
  
        if (rev->early_output)
                setup_early_output(rev);
  
        if (prepare_revision_walk(rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
  
        if (rev->early_output)
                finish_early_output(rev);
                }
                free_commit_list(commit->parents);
                commit->parents = NULL;
+               if (saved_nrl < rev->diffopt.needed_rename_limit)
+                       saved_nrl = rev->diffopt.needed_rename_limit;
+               if (rev->diffopt.degraded_cc_to_c)
+                       saved_dcctc = 1;
        }
+       rev->diffopt.degraded_cc_to_c = saved_dcctc;
+       rev->diffopt.needed_rename_limit = saved_nrl;
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
            DIFF_OPT_TST(&rev->diffopt, CHECK_FAILED)) {
                return 02;
@@@ -358,7 -358,7 +367,7 @@@ static int show_object(const unsigned c
        int offset = 0;
  
        if (!buf)
 -              return error("Could not read object %s", sha1_to_hex(sha1));
 +              return error(_("Could not read object %s"), sha1_to_hex(sha1));
  
        if (show_tag_object)
                while (offset < size && buf[offset] != '\n') {
@@@ -445,7 -445,7 +454,7 @@@ int cmd_show(int argc, const char **arg
                                break;
                        o = parse_object(t->tagged->sha1);
                        if (!o)
 -                              ret = error("Could not read object %s",
 +                              ret = error(_("Could not read object %s"),
                                            sha1_to_hex(t->tagged->sha1));
                        objects[i].item = o;
                        i--;
                        ret = cmd_log_walk(&rev);
                        break;
                default:
 -                      ret = error("Unknown type: %d", o->type);
 +                      ret = error(_("Unknown type: %d"), o->type);
                }
        }
        free(objects);
@@@ -495,11 -495,16 +504,11 @@@ int cmd_log_reflog(int argc, const cha
        rev.verbose_header = 1;
        memset(&opt, 0, sizeof(opt));
        opt.def = "HEAD";
 -      cmd_log_init(argc, argv, prefix, &rev, &opt);
 -
 -      /*
 -       * This means that we override whatever commit format the user gave
 -       * on the cmd line.  Sad, but cmd_log_init() currently doesn't
 -       * allow us to set a different default.
 -       */
 +      cmd_log_init_defaults(&rev);
        rev.commit_format = CMIT_FMT_ONELINE;
        rev.use_terminator = 1;
        rev.always_show_header = 1;
 +      cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
  
        return cmd_log_walk(&rev);
  }
@@@ -564,7 -569,7 +573,7 @@@ static int git_format_config(const cha
  {
        if (!strcmp(var, "format.headers")) {
                if (!value)
 -                      die("format.headers without value");
 +                      die(_("format.headers without value"));
                add_header(value);
                return 0;
        }
@@@ -627,7 -632,7 +636,7 @@@ static FILE *realstdout = NULL
  static const char *output_directory = NULL;
  static int outdir_offset;
  
 -static int reopen_stdout(struct commit *commit, struct rev_info *rev)
 +static int reopen_stdout(struct commit *commit, struct rev_info *rev, int quiet)
  {
        struct strbuf filename = STRBUF_INIT;
        int suffix_len = strlen(fmt_patch_suffix) + 1;
                strbuf_addstr(&filename, output_directory);
                if (filename.len >=
                    PATH_MAX - FORMAT_PATCH_NAME_MAX - suffix_len)
 -                      return error("name of output directory is too long");
 +                      return error(_("name of output directory is too long"));
                if (filename.buf[filename.len - 1] != '/')
                        strbuf_addch(&filename, '/');
        }
  
        get_patch_filename(commit, rev->nr, fmt_patch_suffix, &filename);
  
 -      if (!DIFF_OPT_TST(&rev->diffopt, QUICK))
 +      if (!quiet)
                fprintf(realstdout, "%s\n", filename.buf + outdir_offset);
  
        if (freopen(filename.buf, "w", stdout) == NULL)
 -              return error("Cannot open patch file %s", filename.buf);
 +              return error(_("Cannot open patch file %s"), filename.buf);
  
        strbuf_release(&filename);
        return 0;
@@@ -661,7 -666,7 +670,7 @@@ static void get_patch_ids(struct rev_in
        unsigned flags1, flags2;
  
        if (rev->pending.nr != 2)
 -              die("Need exactly one range.");
 +              die(_("Need exactly one range."));
  
        o1 = rev->pending.objects[0].item;
        flags1 = o1->flags;
        flags2 = o2->flags;
  
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
 -              die("Not a range.");
 +              die(_("Not a range."));
  
        init_patch_ids(ids);
  
        add_pending_object(&check_rev, o1, "o1");
        add_pending_object(&check_rev, o2, "o2");
        if (prepare_revision_walk(&check_rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
  
        while ((commit = get_revision(&check_rev)) != NULL) {
                /* ignore merges */
@@@ -706,7 -711,7 +715,7 @@@ static void gen_message_id(struct rev_i
        const char *email_end = strrchr(committer, '>');
        struct strbuf buf = STRBUF_INIT;
        if (!email_start || !email_end || email_start > email_end - 1)
 -              die("Could not extract email from committer identity.");
 +              die(_("Could not extract email from committer identity."));
        strbuf_addf(&buf, "%s.%lu.git.%.*s", base,
                    (unsigned long) time(NULL),
                    (int)(email_end - email_start - 1), email_start + 1);
@@@ -722,8 -727,7 +731,8 @@@ static void print_signature(void
  static void make_cover_letter(struct rev_info *rev, int use_stdout,
                              int numbered, int numbered_files,
                              struct commit *origin,
 -                            int nr, struct commit **list, struct commit *head)
 +                            int nr, struct commit **list, struct commit *head,
 +                            int quiet)
  {
        const char *committer;
        const char *subject_start = NULL;
        struct commit *commit = NULL;
  
        if (rev->commit_format != CMIT_FMT_EMAIL)
 -              die("Cover letter needs email format");
 +              die(_("Cover letter needs email format"));
  
        committer = git_committer_info(0);
  
                        sha1_to_hex(head->object.sha1), committer, committer);
        }
  
 -      if (!use_stdout && reopen_stdout(commit, rev))
 +      if (!use_stdout && reopen_stdout(commit, rev, quiet))
                return;
  
        if (commit) {
@@@ -832,7 -836,7 +841,7 @@@ static const char *clean_message_id(con
                m++;
        }
        if (!z)
 -              die("insane in-reply-to: %s", msg_id);
 +              die(_("insane in-reply-to: %s"), msg_id);
        if (++z == m)
                return a;
        return xmemdupz(a, z - a);
@@@ -905,7 -909,7 +914,7 @@@ static int output_directory_callback(co
  {
        const char **dir = (const char **)opt->value;
        if (*dir)
 -              die("Two output directories?");
 +              die(_("Two output directories?"));
        *dir = arg;
        return 0;
  }
@@@ -1000,7 -1004,6 +1009,7 @@@ int cmd_format_patch(int argc, const ch
        char *add_signoff = NULL;
        struct strbuf buf = STRBUF_INIT;
        int use_patch_format = 0;
 +      int quiet = 0;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            "use [PATCH n/m] even with a single patch",
                            PARSE_OPT_OPTARG, thread_callback },
                OPT_STRING(0, "signature", &signature, "signature",
                            "add a signature"),
 +              OPT_BOOLEAN(0, "quiet", &quiet,
 +                          "don't print the patch filenames"),
                OPT_END()
        };
  
        rev.commit_format = CMIT_FMT_EMAIL;
        rev.verbose_header = 1;
        rev.diff = 1;
 -      rev.no_merges = 1;
 +      rev.max_parents = 1;
        DIFF_OPT_SET(&rev.diffopt, RECURSIVE);
        rev.subject_prefix = fmt_patch_subject_prefix;
        memset(&s_r_opt, 0, sizeof(s_r_opt));
                committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
                endpos = strchr(committer, '>');
                if (!endpos)
 -                      die("bogus committer info %s", committer);
 +                      die(_("bogus committer info %s"), committer);
                add_signoff = xmemdupz(committer, endpos - committer + 1);
        }
  
                numbered = 0;
  
        if (numbered && keep_subject)
 -              die ("-n and -k are mutually exclusive.");
 +              die (_("-n and -k are mutually exclusive."));
        if (keep_subject && subject_prefix)
 -              die ("--subject-prefix and -k are mutually exclusive.");
 +              die (_("--subject-prefix and -k are mutually exclusive."));
  
        argc = setup_revisions(argc, argv, &rev, &s_r_opt);
        if (argc > 1)
 -              die ("unrecognized argument: %s", argv[1]);
 +              die (_("unrecognized argument: %s"), argv[1]);
  
        if (rev.diffopt.output_format & DIFF_FORMAT_NAME)
 -              die("--name-only does not make sense");
 +              die(_("--name-only does not make sense"));
        if (rev.diffopt.output_format & DIFF_FORMAT_NAME_STATUS)
 -              die("--name-status does not make sense");
 +              die(_("--name-status does not make sense"));
        if (rev.diffopt.output_format & DIFF_FORMAT_CHECKDIFF)
 -              die("--check does not make sense");
 +              die(_("--check does not make sense"));
  
        if (!use_patch_format &&
                (!rev.diffopt.output_format ||
  
        if (output_directory) {
                if (use_stdout)
 -                      die("standard output, or directory, which one?");
 +                      die(_("standard output, or directory, which one?"));
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
 -                      die_errno("Could not create directory '%s'",
 +                      die_errno(_("Could not create directory '%s'"),
                                  output_directory);
        }
  
                realstdout = xfdopen(xdup(1), "w");
  
        if (prepare_revision_walk(&rev))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
        rev.boundary = 1;
        while ((commit = get_revision(&rev)) != NULL) {
                if (commit->object.flags & BOUNDARY) {
                if (thread)
                        gen_message_id(&rev, "cover");
                make_cover_letter(&rev, use_stdout, numbered, numbered_files,
 -                                origin, nr, list, head);
 +                                origin, nr, list, head, quiet);
                total++;
                start_number--;
        }
                }
  
                if (!use_stdout && reopen_stdout(numbered_files ? NULL : commit,
 -                                               &rev))
 -                      die("Failed to create output files");
 +                                               &rev, quiet))
 +                      die(_("Failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free(commit->buffer);
                commit->buffer = NULL;
@@@ -1418,9 -1419,9 +1427,9 @@@ int cmd_cherry(int argc, const char **a
                if (!current_branch || !current_branch->merge
                                        || !current_branch->merge[0]
                                        || !current_branch->merge[0]->dst) {
 -                      fprintf(stderr, "Could not find a tracked"
 +                      fprintf(stderr, _("Could not find a tracked"
                                        " remote branch, please"
 -                                      " specify <upstream> manually.\n");
 +                                      " specify <upstream> manually.\n"));
                        usage_with_options(cherry_usage, options);
                }
  
        DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
  
        if (add_pending_commit(head, &revs, 0))
 -              die("Unknown commit %s", head);
 +              die(_("Unknown commit %s"), head);
        if (add_pending_commit(upstream, &revs, UNINTERESTING))
 -              die("Unknown commit %s", upstream);
 +              die(_("Unknown commit %s"), upstream);
  
        /* Don't say anything if head and upstream are the same. */
        if (revs.pending.nr == 2) {
        get_patch_ids(&revs, &ids, prefix);
  
        if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
 -              die("Unknown commit %s", limit);
 +              die(_("Unknown commit %s"), limit);
  
        /* reverse the list of commits */
        if (prepare_revision_walk(&revs))
 -              die("revision walk setup failed");
 +              die(_("revision walk setup failed"));
        while ((commit = get_revision(&revs)) != NULL) {
                /* ignore merges */
                if (commit->parents && commit->parents->next)
diff --combined diff.c
index 5376d01e1b26f0297e20f14bd8adcfefd374ab08,13f322bc5f8d12f3a11f62db3744f0648ba8ace6..56758c7dca3e7529cbb050afd792425a45d05517
--- 1/diff.c
--- 2/diff.c
+++ b/diff.c
@@@ -1242,7 -1242,7 +1242,7 @@@ static void show_stats(struct diffstat_
        uintmax_t max_change = 0, max_len = 0;
        int total_files = data->nr;
        int width, name_width;
 -      const char *reset, *set, *add_c, *del_c;
 +      const char *reset, *add_c, *del_c;
        const char *line_prefix = "";
        struct strbuf *msg = NULL;
  
  
        /* Find the longest filename and max number of changes */
        reset = diff_get_color_opt(options, DIFF_RESET);
 -      set   = diff_get_color_opt(options, DIFF_PLAIN);
        add_c = diff_get_color_opt(options, DIFF_FILE_NEW);
        del_c = diff_get_color_opt(options, DIFF_FILE_OLD);
  
@@@ -1538,36 -1539,8 +1538,36 @@@ static void show_dirstat(struct diff_op
                struct diff_filepair *p = q->queue[i];
                const char *name;
                unsigned long copied, added, damage;
 +              int content_changed;
  
 -              name = p->one->path ? p->one->path : p->two->path;
 +              name = p->two->path ? p->two->path : p->one->path;
 +
 +              if (p->one->sha1_valid && p->two->sha1_valid)
 +                      content_changed = hashcmp(p->one->sha1, p->two->sha1);
 +              else
 +                      content_changed = 1;
 +
 +              if (!content_changed) {
 +                      /*
 +                       * The SHA1 has not changed, so pre-/post-content is
 +                       * identical. We can therefore skip looking at the
 +                       * file contents altogether.
 +                       */
 +                      damage = 0;
 +                      goto found_damage;
 +              }
 +
 +              if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
 +                      /*
 +                       * In --dirstat-by-file mode, we don't really need to
 +                       * look at the actual file contents at all.
 +                       * The fact that the SHA1 changed is enough for us to
 +                       * add this file to the list of results
 +                       * (with each file contributing equal damage).
 +                       */
 +                      damage = 1;
 +                      goto found_damage;
 +              }
  
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
                        diff_populate_filespec(p->one, 0);
                /*
                 * Original minus copied is the removed material,
                 * added is the new material.  They are both damages
 -               * made to the preimage. In --dirstat-by-file mode, count
 -               * damaged files, not damaged lines. This is done by
 -               * counting only a single damaged line per file.
 +               * made to the preimage.
 +               * If the resulting damage is zero, we know that
 +               * diffcore_count_changes() considers the two entries to
 +               * be identical, but since content_changed is true, we
 +               * know that there must have been _some_ kind of change,
 +               * so we force all entries to have damage > 0.
                 */
                damage = (p->one->size - copied) + added;
 -              if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
 +              if (!damage)
                        damage = 1;
  
 +found_damage:
                ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
                dir.files[dir.nr].name = name;
                dir.files[dir.nr].changed = damage;
@@@ -3987,6 -3956,28 +3987,28 @@@ static int is_summary_empty(const struc
        return 1;
  }
  
+ static const char rename_limit_warning[] =
+ "inexact rename detection was skipped due to too many files.";
+ static const char degrade_cc_to_c_warning[] =
+ "only found copies from modified paths due to too many files.";
+ static const char rename_limit_advice[] =
+ "you may want to set your %s variable to at least "
+ "%d and retry the command.";
+ void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc)
+ {
+       if (degraded_cc)
+               warning(degrade_cc_to_c_warning);
+       else if (needed)
+               warning(rename_limit_warning);
+       else
+               return;
+       if (0 < needed && needed < 32767)
+               warning(rename_limit_advice, varname, needed);
+ }
  void diff_flush(struct diff_options *options)
  {
        struct diff_queue_struct *q = &diff_queued_diff;
@@@ -4268,6 -4259,10 +4290,10 @@@ void diffcore_std(struct diff_options *
  int diff_result_code(struct diff_options *opt, int status)
  {
        int result = 0;
+       diff_warn_rename_limit("diff.renamelimit",
+                              opt->needed_rename_limit,
+                              opt->degraded_cc_to_c);
        if (!DIFF_OPT_TST(opt, EXIT_WITH_STATUS) &&
            !(opt->output_format & DIFF_FORMAT_CHECKDIFF))
                return status;
diff --combined merge-recursive.c
index 7c126735535f390b54c02920f1df58acaf34532e,3ae4d53cc552fb837cb50edddbcc993c460aa941..fb3c874ff47c949dc8df89edb320accf90bc8ac9
  #include "dir.h"
  #include "submodule.h"
  
- static const char rename_limit_advice[] =
- "inexact rename detection was skipped because there were too many\n"
- "  files. You may want to set your merge.renamelimit variable to at least\n"
- "  %d and retry this merge.";
  static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
  {
@@@ -344,11 -339,10 +339,11 @@@ static void make_room_for_directories_o
         * make room for the corresponding directory.  Such paths will
         * later be processed in process_df_entry() at the end.  If
         * the corresponding directory ends up being removed by the
 -       * merge, then the file will be reinstated at that time;
 -       * otherwise, if the file is not supposed to be removed by the
 -       * merge, the contents of the file will be placed in another
 -       * unique filename.
 +       * merge, then the file will be reinstated at that time
 +       * (albeit with a different timestamp!); otherwise, if the
 +       * file is not supposed to be removed by the merge, the
 +       * contents of the file will be placed in another unique
 +       * filename.
         *
         * NOTE: This function relies on the fact that entries for a
         * D/F conflict will appear adjacent in the index, with the
         */
        const char *last_file = NULL;
        int last_len = 0;
 -      struct stage_data *last_e;
        int i;
  
 +      /*
 +       * Do not do any of this crazyness during the recursive; we don't
 +       * even write anything to the working tree!
 +       */
 +      if (o->call_depth)
 +              return;
 +
        for (i = 0; i < entries->nr; i++) {
                const char *path = entries->items[i].string;
                int len = strlen(path);
                if (S_ISREG(e->stages[2].mode) || S_ISLNK(e->stages[2].mode)) {
                        last_file = path;
                        last_len = len;
 -                      last_e = e;
                } else {
                        last_file = NULL;
                }
@@@ -967,6 -956,7 +962,6 @@@ static int process_renames(struct merge
        }
  
        for (i = 0, j = 0; i < a_renames->nr || j < b_renames->nr;) {
 -              char *src;
                struct string_list *renames1, *renames2Dst;
                struct rename *ren1 = NULL, *ren2 = NULL;
                const char *branch1, *branch2;
                        ren2 = ren1;
                        ren1 = tmp;
                }
 -              src = ren1->pair->one->path;
  
                ren1->dst_entry->processed = 1;
                ren1->src_entry->processed = 1;
@@@ -1268,13 -1259,9 +1263,13 @@@ static int merge_content(struct merge_o
        }
  
        if (mfi.clean && !df_conflict_remains &&
 -          sha_eq(mfi.sha, a_sha) && mfi.mode == a.mode)
 +          sha_eq(mfi.sha, a_sha) && mfi.mode == a.mode &&
 +          !o->call_depth && !lstat(path, &st)) {
                output(o, 3, "Skipped %s (merged same as existing)", path);
 -      else
 +              add_cacheinfo(mfi.mode, mfi.sha, path,
 +                            0 /*stage*/, 1 /*refresh*/, 0 /*options*/);
 +              return mfi.clean;
 +      } else
                output(o, 2, "Auto-merging %s", path);
  
        if (!mfi.clean) {
@@@ -1664,8 -1651,9 +1659,9 @@@ int merge_recursive(struct merge_option
                commit_list_insert(h2, &(*result)->parents->next);
        }
        flush_output(o);
-       if (o->needed_rename_limit)
-               warning(rename_limit_advice, o->needed_rename_limit);
+       if (show(o, 2))
+               diff_warn_rename_limit("merge.renamelimit",
+                                      o->needed_rename_limit, 0);
        return clean;
  }
  
diff --combined t/t4001-diff-rename.sh
index cad85450b78891a98a0b60af1422c62c7288265c,301f3a0e545be02b071fcf43ea690a1a6d6e044a..3dadf9b31636b862b2ab7377ce7100ebebd397e2
@@@ -64,7 -64,7 +64,7 @@@ test_expect_success 
      'validate the output.' \
      'compare_diff_patch current expected'
  
 -test_expect_success 'favour same basenames over different ones' '
 +test_expect_success C_LOCALE_OUTPUT 'favour same basenames over different ones' '
        cp path1 another-path &&
        git add another-path &&
        git commit -m 1 &&
        git mv another-path subdir/path1 &&
        git status | grep "renamed: .*path1 -> subdir/path1"'
  
 -test_expect_success  'favour same basenames even with minor differences' '
 +test_expect_success C_LOCALE_OUTPUT  'favour same basenames even with minor differences' '
        git show HEAD:path1 | sed "s/15/16/" > subdir/path1 &&
        git status | grep "renamed: .*path1 -> subdir/path1"'
  
+ test_expect_success 'setup for many rename source candidates' '
+       git reset --hard &&
+       for i in 0 1 2 3 4 5 6 7 8 9;
+       do
+               for j in 0 1 2 3 4 5 6 7 8 9;
+               do
+                       echo "$i$j" >"path$i$j"
+               done
+       done &&
+       git add "path??" &&
+       test_tick &&
+       git commit -m "hundred" &&
+       (cat path1; echo new) >new-path &&
+       echo old >>path1 &&
+       git add new-path path1 &&
+       git diff -l 4 -C -C --cached --name-status >actual 2>actual.err &&
+       sed -e "s/^\([CM]\)[0-9]*       /\1     /" actual >actual.munged &&
+       cat >expect <<-EOF &&
+       C       path1   new-path
+       M       path1
+       EOF
+       test_cmp expect actual.munged &&
+       grep warning actual.err
+ '
  test_done