merge: add merge.renames config setting
authorBen Peart <Ben.Peart@microsoft.com>
Wed, 2 May 2018 16:01:14 +0000 (16:01 +0000)
committerJunio C Hamano <gitster@pobox.com>
Tue, 8 May 2018 07:19:41 +0000 (16:19 +0900)
Add the ability to control rename detection for merge via a config setting.
This setting behaves the same and defaults to the value of diff.renames but only
applies to merge.

Reviewed-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Helped-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Ben Peart <benpeart@microsoft.com>
Reviewed-by: Elijah Newren <newren@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
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 48ee3bce77c28ed5137f9345fd9376f5f43b1bf1..662c2713cab4b9762dded702897d9e92ee0a0549 100644 (file)
@@ -38,6 +38,11 @@ merge.renameLimit::
        diff.renameLimit. This setting has no effect if rename detection
        is turned off.
 
        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
        repository has changed over time (e.g. earlier commits record
 merge.renormalize::
        Tell Git that canonical representation of files in the
        repository has changed over time (e.g. earlier commits record
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
        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:
 
 +
 The 'recursive' strategy can take the following options:
 
@@ -84,12 +85,14 @@ no-renormalize;;
        `merge.renormalize` configuration variable.
 
 no-renames;;
        `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
        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>;;
        See also linkgit:git-diff[1] `--find-renames`.
 
 rename-threshold=<n>;;
diff --git a/diff.c b/diff.c
index 1289df4b1f9f395010e475073c2c5e5ce43976a7..5dfc24aa6dfd1222e350069eb7da862f86aac0bc 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;
 }
 
        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;
 {
        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 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
 
 #define DIFF_DETECT_RENAME     1
 #define DIFF_DETECT_COPY       2
index 13b47629719022c0e5d1311a1c7c3cd905936700..e98404964da7af9496fadf78b349bcf3c7a97832 100644 (file)
@@ -1603,7 +1603,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
        diff_setup(&opts);
        opts.flags.recursive = 1;
        opts.flags.rename_empty = 0;
        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;
        opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
                            o->diff_rename_limit >= 0 ? o->diff_rename_limit :
                            1000;
@@ -2643,7 +2651,7 @@ static int handle_renames(struct merge_options *o,
        ri->head_renames = NULL;
        ri->merge_renames = NULL;
 
        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);
                return 1;
 
        head_pairs = get_diffpairs(o, common, head);
@@ -3324,9 +3332,18 @@ int merge_recursive_generic(struct merge_options *o,
 
 static void merge_recursive_config(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);
        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);
 }
 
        git_config(git_xmerge_config, NULL);
 }
 
@@ -3339,7 +3356,8 @@ void init_merge_options(struct merge_options *o)
        o->diff_rename_limit = -1;
        o->merge_rename_limit = -1;
        o->renormalize = 0;
        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)
        merge_recursive_config(o);
        merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
        if (merge_verbosity)
@@ -3390,16 +3408,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"))
        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")) {
        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->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;
        }
        else
                return -1;
index 248093e407c7744b1e3e9a5bd5780375a1c6c07f..fa7bc6b6836f99ab9c417e2865cd5a544488d025 100644 (file)
@@ -18,7 +18,8 @@ struct merge_options {
        unsigned renormalize : 1;
        long xdl_opts;
        int verbosity;
        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;
        int diff_rename_limit;
        int merge_rename_limit;
        int rename_score;
@@ -57,6 +58,12 @@ struct collision_entry {
        unsigned reported_already:1;
 };
 
        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,
 /* 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
 '
 
        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
 test_done