merge: add merge.renames config setting

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>

Ben Peart committed May 2, 2018 at 16:01 UTC 85b460305ce7ed399c83499851f46c4f870ce280
7 files changed +64 -12
Documentation/merge-config.txt
+5
@@ -38,6 +38,11 @@ merge.renameLimit::
38 diff.renameLimit. This setting has no effect if rename detection
39 is turned off.
40
41 +merge.renames::
42 + Whether and how Git detects renames. If set to "false",
43 + rename detection is disabled. If set to "true", basic rename
44 + detection is enabled. Defaults to the value of diff.renames.
45 +
46 merge.renormalize::
47 Tell Git that canonical representation of files in the
48 repository has changed over time (e.g. earlier commits record
Documentation/merge-strategies.txt
+7 -4
@@ -23,8 +23,9 @@ recursive::
23 causing mismerges by tests done on actual merge commits
24 taken from Linux 2.6 kernel development history.
25 Additionally this can detect and handle merges involving
26 - renames. This is the default merge strategy when
27 - pulling or merging one branch.
26 + renames, but currently cannot make use of detected
27 + copies. This is the default merge strategy when pulling
28 + or merging one branch.
29 +
30 The 'recursive' strategy can take the following options:
31
@@ -84,12 +85,14 @@ no-renormalize;;
85 `merge.renormalize` configuration variable.
86
87 no-renames;;
87 - Turn off rename detection.
88 + Turn off rename detection. This overrides the `merge.renames`
89 + configuration variable.
90 See also linkgit:git-diff[1] `--no-renames`.
91
92 find-renames[=<n>];;
93 Turn on rename detection, optionally setting the similarity
92 - threshold. This is the default.
94 + threshold. This is the default. This overrides the
95 + 'merge.renames' configuration variable.
96 See also linkgit:git-diff[1] `--find-renames`.
97
98 rename-threshold=<n>;;
diff.c
+1 -1
@@ -177,7 +177,7 @@ static int parse_submodule_params(struct diff_options *options, const char *valu
177 return 0;
178 }
179
180 -static int git_config_rename(const char *var, const char *value)
180 +int git_config_rename(const char *var, const char *value)
181 {
182 if (!value)
183 return DIFF_DETECT_RENAME;
diff.h
+1
@@ -324,6 +324,7 @@ extern int git_diff_ui_config(const char *var, const char *value, void *cb);
324 extern void diff_setup(struct diff_options *);
325 extern int diff_opt_parse(struct diff_options *, const char **, int, const char *);
326 extern void diff_setup_done(struct diff_options *);
327 +extern int git_config_rename(const char *var, const char *value);
328
329 #define DIFF_DETECT_RENAME 1
330 #define DIFF_DETECT_COPY 2
merge-recursive.c
+24 -6
@@ -1603,7 +1603,15 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
1603 diff_setup(&opts);
1604 opts.flags.recursive = 1;
1605 opts.flags.rename_empty = 0;
1606 - opts.detect_rename = DIFF_DETECT_RENAME;
1606 + opts.detect_rename = merge_detect_rename(o);
1607 + /*
1608 + * We do not have logic to handle the detection of copies. In
1609 + * fact, it may not even make sense to add such logic: would we
1610 + * really want a change to a base file to be propagated through
1611 + * multiple other files by a merge?
1612 + */
1613 + if (opts.detect_rename > DIFF_DETECT_RENAME)
1614 + opts.detect_rename = DIFF_DETECT_RENAME;
1615 opts.rename_limit = o->merge_rename_limit >= 0 ? o->merge_rename_limit :
1616 o->diff_rename_limit >= 0 ? o->diff_rename_limit :
1617 1000;
@@ -2643,7 +2651,7 @@ static int handle_renames(struct merge_options *o,
2651 ri->head_renames = NULL;
2652 ri->merge_renames = NULL;
2653
2646 - if (!o->detect_rename)
2654 + if (!merge_detect_rename(o))
2655 return 1;
2656
2657 head_pairs = get_diffpairs(o, common, head);
@@ -3324,9 +3332,18 @@ int merge_recursive_generic(struct merge_options *o,
3332
3333 static void merge_recursive_config(struct merge_options *o)
3334 {
3335 + char *value = NULL;
3336 git_config_get_int("merge.verbosity", &o->verbosity);
3337 git_config_get_int("diff.renamelimit", &o->diff_rename_limit);
3338 git_config_get_int("merge.renamelimit", &o->merge_rename_limit);
3339 + if (!git_config_get_string("diff.renames", &value)) {
3340 + o->diff_detect_rename = git_config_rename("diff.renames", value);
3341 + free(value);
3342 + }
3343 + if (!git_config_get_string("merge.renames", &value)) {
3344 + o->merge_detect_rename = git_config_rename("merge.renames", value);
3345 + free(value);
3346 + }
3347 git_config(git_xmerge_config, NULL);
3348 }
3349
@@ -3339,7 +3356,8 @@ void init_merge_options(struct merge_options *o)
3356 o->diff_rename_limit = -1;
3357 o->merge_rename_limit = -1;
3358 o->renormalize = 0;
3342 - o->detect_rename = 1;
3359 + o->diff_detect_rename = -1;
3360 + o->merge_detect_rename = -1;
3361 merge_recursive_config(o);
3362 merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
3363 if (merge_verbosity)
@@ -3390,16 +3408,16 @@ int parse_merge_opt(struct merge_options *o, const char *s)
3408 else if (!strcmp(s, "no-renormalize"))
3409 o->renormalize = 0;
3410 else if (!strcmp(s, "no-renames"))
3393 - o->detect_rename = 0;
3411 + o->merge_detect_rename = 0;
3412 else if (!strcmp(s, "find-renames")) {
3395 - o->detect_rename = 1;
3413 + o->merge_detect_rename = 1;
3414 o->rename_score = 0;
3415 }
3416 else if (skip_prefix(s, "find-renames=", &arg) ||
3417 skip_prefix(s, "rename-threshold=", &arg)) {
3418 if ((o->rename_score = parse_rename_score(&arg)) == -1 || *arg != 0)
3419 return -1;
3402 - o->detect_rename = 1;
3420 + o->merge_detect_rename = 1;
3421 }
3422 else
3423 return -1;
merge-recursive.h
+8 -1
@@ -18,7 +18,8 @@ struct merge_options {
18 unsigned renormalize : 1;
19 long xdl_opts;
20 int verbosity;
21 - int detect_rename;
21 + int diff_detect_rename;
22 + int merge_detect_rename;
23 int diff_rename_limit;
24 int merge_rename_limit;
25 int rename_score;
@@ -57,6 +58,12 @@ struct collision_entry {
58 unsigned reported_already:1;
59 };
60
61 +static inline int merge_detect_rename(struct merge_options *o)
62 +{
63 + return o->merge_detect_rename >= 0 ? o->merge_detect_rename :
64 + o->diff_detect_rename >= 0 ? o->diff_detect_rename : 1;
65 +}
66 +
67 /* merge_trees() but with recursive ancestor consolidation */
68 int merge_recursive(struct merge_options *o,
69 struct commit *h1,
t/t3034-merge-recursive-rename-options.sh
+18
@@ -309,4 +309,22 @@ test_expect_success 'last wins in --find-renames=<m> --rename-threshold=<n>' '
309 check_threshold_0
310 '
311
312 +test_expect_success 'merge.renames disables rename detection' '
313 + git read-tree --reset -u HEAD &&
314 + git -c merge.renames=false merge-recursive $tail &&
315 + check_no_renames
316 +'
317 +
318 +test_expect_success 'merge.renames defaults to diff.renames' '
319 + git read-tree --reset -u HEAD &&
320 + git -c diff.renames=false merge-recursive $tail &&
321 + check_no_renames
322 +'
323 +
324 +test_expect_success 'merge.renames overrides diff.renames' '
325 + git read-tree --reset -u HEAD &&
326 + test_must_fail git -c diff.renames=false -c merge.renames=true merge-recursive $tail &&
327 + $check_50
328 +'
329 +
330 test_done