Merge branch 'bp/merge-rename-config'
authorJunio C Hamano <gitster@pobox.com>
Wed, 30 May 2018 05:04:04 +0000 (14:04 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 30 May 2018 05:04:04 +0000 (14:04 +0900)
With merge.renames configuration set to false, the recursive merge
strategy can be told not to spend cycles trying to find renamed
paths and merge them accordingly.

* bp/merge-rename-config:
merge: pass aggressive when rename detection is turned off
merge: add merge.renames config setting
merge: update documentation for {merge,diff}.renameLimit

Documentation/diff-config.txt
Documentation/merge-config.txt
Documentation/merge-strategies.txt
diff.c
diff.h
merge-recursive.c
merge-recursive.h
t/t3034-merge-recursive-rename-options.sh
index 5ca942ab5e2d5dfd67ceb06c5991f446b207de90..77caa66c2f768b6e43322c4662f45b4191a163c0 100644 (file)
@@ -112,7 +112,8 @@ diff.orderFile::
 
 diff.renameLimit::
        The number of files to consider when performing the copy/rename
-       detection; equivalent to the 'git diff' option `-l`.
+       detection; equivalent to the 'git diff' option `-l`. This setting
+       has no effect if rename detection is turned off.
 
 diff.renames::
        Whether and how Git detects renames.  If set to "false",
index 12b6bbf591ee801e0723f59db0e0abe7dc88ec73..662c2713cab4b9762dded702897d9e92ee0a0549 100644 (file)
@@ -35,7 +35,13 @@ include::fmt-merge-msg-config.txt[]
 merge.renameLimit::
        The number of files to consider when performing rename detection
        during a merge; if not specified, defaults to the value of
-       diff.renameLimit.
+       diff.renameLimit. This setting has no effect if rename detection
+       is turned off.
+
+merge.renames::
+       Whether and how Git detects renames.  If set to "false",
+       rename detection is disabled. If set to "true", basic rename
+       detection is enabled.  Defaults to the value of diff.renames.
 
 merge.renormalize::
        Tell Git that canonical representation of files in the
index 4a58aad4b83b9e365b57a8afa71f1ab939c47aee..aa66cbe41eaf070a74d98801ba973daeb8d15106 100644 (file)
@@ -23,8 +23,9 @@ recursive::
        causing mismerges by tests done on actual merge commits
        taken from Linux 2.6 kernel development history.
        Additionally this can detect and handle merges involving
-       renames.  This is the default merge strategy when
-       pulling or merging one branch.
+       renames, but currently cannot make use of detected
+       copies.  This is the default merge strategy when pulling
+       or merging one branch.
 +
 The 'recursive' strategy can take the following options:
 
@@ -84,12 +85,14 @@ no-renormalize;;
        `merge.renormalize` configuration variable.
 
 no-renames;;
-       Turn off rename detection.
+       Turn off rename detection. This overrides the `merge.renames`
+       configuration variable.
        See also linkgit:git-diff[1] `--no-renames`.
 
 find-renames[=<n>];;
        Turn on rename detection, optionally setting the similarity
-       threshold.  This is the default.
+       threshold.  This is the default. This overrides the
+       'merge.renames' configuration variable.
        See also linkgit:git-diff[1] `--find-renames`.
 
 rename-threshold=<n>;;
diff --git a/diff.c b/diff.c
index 4753170fe1296c87579db3376aa5a625b0042f19..b2840231f36e8425bf06438ac4bd236a5ed7dc86 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -177,7 +177,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valu
        return 0;
 }
 
-static int git_config_rename(const char *var, const char *value)
+int git_config_rename(const char *var, const char *value)
 {
        if (!value)
                return DIFF_DETECT_RENAME;
diff --git a/diff.h b/diff.h
index d29560f822ca0ee4738f769e2feb3939851d7ff1..dedac472ca5959bb5ff17888a54042ac75678631 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -324,6 +324,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb);
 extern void diff_setup(struct diff_options *);
 extern int diff_opt_parse(struct diff_options *, const char **, int, const char *);
 extern void diff_setup_done(struct diff_options *);
+extern int git_config_rename(const char *var, const char *value);
 
 #define DIFF_DETECT_RENAME     1
 #define DIFF_DETECT_COPY       2
index 35df695fa420073d04997a3792f1c3a45d278b5d..8d96e94bd5911376bec5df3fecb1ab502691060a 100644 (file)
@@ -356,6 +356,7 @@ static int git_merge_trees(struct merge_options *o,
        o->unpack_opts.fn = threeway_merge;
        o->unpack_opts.src_index = &the_index;
        o->unpack_opts.dst_index = &tmp_index;
+       o->unpack_opts.aggressive = !merge_detect_rename(o);
        setup_unpack_trees_porcelain(&o->unpack_opts, "merge");
 
        init_tree_desc_from_tree(t+0, common);
@@ -1603,7 +1604,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
        diff_setup(&opts);
        opts.flags.recursive = 1;
        opts.flags.rename_empty = 0;
-       opts.detect_rename = DIFF_DETECT_RENAME;
+       opts.detect_rename = merge_detect_rename(o);
+       /*
+        * We do not have logic to handle the detection of copies.  In
+        * fact, it may not even make sense to add such logic: would we
+        * really want a change to a base file to be propagated through
+        * multiple other files by a merge?
+        */
+       if (opts.detect_rename > DIFF_DETECT_RENAME)
+               opts.detect_rename = DIFF_DETECT_RENAME;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
                            1000;
@@ -2643,7 +2652,7 @@ static int handle_renames(struct merge_options *o,
        ri->head_renames = NULL;
        ri->merge_renames = NULL;
 
-       if (!o->detect_rename)
+       if (!merge_detect_rename(o))
                return 1;
 
        head_pairs = get_diffpairs(o, common, head);
@@ -3325,9 +3334,18 @@ int merge_recursive_generic(struct merge_options *o,
 
 static void merge_recursive_config(struct merge_options *o)
 {
+       char *value = NULL;
        git_config_get_int("merge.verbosity", &o->verbosity);
        git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
        git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
+       if (!git_config_get_string("diff.renames", &value)) {
+               o->diff_detect_rename = git_config_rename("diff.renames", value);
+               free(value);
+       }
+       if (!git_config_get_string("merge.renames", &value)) {
+               o->merge_detect_rename = git_config_rename("merge.renames", value);
+               free(value);
+       }
        git_config(git_xmerge_config, NULL);
 }
 
@@ -3340,7 +3358,8 @@ void init_merge_options(struct merge_options *o)
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
        o->renormalize = 0;
-       o->detect_rename = 1;
+       o->diff_detect_rename = -1;
+       o->merge_detect_rename = -1;
        merge_recursive_config(o);
        merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
        if (merge_verbosity)
@@ -3391,16 +3410,16 @@ int parse_merge_opt(struct merge_options *o, const char *s)
        else if (!strcmp(s, "no-renormalize"))
                o->renormalize = 0;
        else if (!strcmp(s, "no-renames"))
-               o->detect_rename = 0;
+               o->merge_detect_rename = 0;
        else if (!strcmp(s, "find-renames")) {
-               o->detect_rename = 1;
+               o->merge_detect_rename = 1;
                o->rename_score = 0;
        }
        else if (skip_prefix(s, "find-renames=", &arg) ||
                 skip_prefix(s, "rename-threshold=", &arg)) {
                if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
                        return -1;
-               o->detect_rename = 1;
+               o->merge_detect_rename = 1;
        }
        else
                return -1;
index 248093e407c7744b1e3e9a5bd5780375a1c6c07f..fa7bc6b6836f99ab9c417e2865cd5a544488d025 100644 (file)
@@ -18,7 +18,8 @@ struct merge_options {
        unsigned renormalize : 1;
        long xdl_opts;
        int verbosity;
-       int detect_rename;
+       int diff_detect_rename;
+       int merge_detect_rename;
        int diff_rename_limit;
        int merge_rename_limit;
        int rename_score;
@@ -57,6 +58,12 @@ struct collision_entry {
        unsigned reported_already:1;
 };
 
+static inline int merge_detect_rename(struct merge_options *o)
+{
+       return o->merge_detect_rename >= 0 ? o->merge_detect_rename :
+               o->diff_detect_rename >= 0 ? o->diff_detect_rename : 1;
+}
+
 /* merge_trees() but with recursive ancestor consolidation */
 int merge_recursive(struct merge_options *o,
                    struct commit *h1,
index b9c4028496f97bc9a620df89768e07c6a99bb07d..3d9fae68c41c85b4393e618cfe3706212d5c2dce 100755 (executable)
@@ -309,4 +309,22 @@ test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' '
        check_threshold_0
 '
 
+test_expect_success 'merge.renames disables rename detection' '
+       git read-tree --reset -u HEAD &&
+       git -c merge.renames=false merge-recursive $tail &&
+       check_no_renames
+'
+
+test_expect_success 'merge.renames defaults to diff.renames' '
+       git read-tree --reset -u HEAD &&
+       git -c diff.renames=false merge-recursive $tail &&
+       check_no_renames
+'
+
+test_expect_success 'merge.renames overrides diff.renames' '
+       git read-tree --reset -u HEAD &&
+       test_must_fail git -c diff.renames=false -c merge.renames=true merge-recursive $tail &&
+       $check_50
+'
+
 test_done