Git fork

merge-recursive: honor diff.algorithm

The documentation claims that "recursive defaults to the diff.algorithm
config setting", but this is currently not the case. This fixes it,
ensuring that diff.algorithm is used when -Xdiff-algorithm is not
supplied. This affects the following porcelain commands: "merge",
"rebase", "cherry-pick", "pull", "stash", "log", "am" and "checkout".
It also affects the "merge-tree" ancillary interrogator.

This change refactors the initialization of merge options to introduce
two functions, "init_merge_ui_options" and "init_merge_basic_options"
instead of just one "init_merge_options". This design follows the
approach used in diff.c, providing initialization methods for
porcelain and plumbing commands respectively. Thanks to that, the
"replay" and "merge-recursive" plumbing commands remain unaffected by
diff.algorithm.

Signed-off-by: Antonin Delpeuch <antonin@delpeuch.eu>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Antonin Delpeuch and committed by
Junio C Hamano
9c93ba4d 557ae147

+150 -15
+1 -1
builtin/am.c
··· 1630 1630 * changes. 1631 1631 */ 1632 1632 1633 - init_merge_options(&o, the_repository); 1633 + init_ui_merge_options(&o, the_repository); 1634 1634 1635 1635 o.branch1 = "HEAD"; 1636 1636 their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
+1 -1
builtin/checkout.c
··· 884 884 885 885 add_files_to_cache(the_repository, NULL, NULL, NULL, 0, 886 886 0); 887 - init_merge_options(&o, the_repository); 887 + init_ui_merge_options(&o, the_repository); 888 888 o.verbosity = 0; 889 889 work = write_in_core_index_as_tree(the_repository); 890 890
+1 -1
builtin/merge-recursive.c
··· 31 31 char *better1, *better2; 32 32 struct commit *result; 33 33 34 - init_merge_options(&o, the_repository); 34 + init_basic_merge_options(&o, the_repository); 35 35 if (argv[0] && ends_with(argv[0], "-subtree")) 36 36 o.subtree_shift = ""; 37 37
+1 -1
builtin/merge-tree.c
··· 571 571 }; 572 572 573 573 /* Init merge options */ 574 - init_merge_options(&o.merge_options, the_repository); 574 + init_ui_merge_options(&o.merge_options, the_repository); 575 575 576 576 /* Parse arguments */ 577 577 original_argc = argc - 1; /* ignoring argv[0] */
+1 -1
builtin/merge.c
··· 724 724 return 2; 725 725 } 726 726 727 - init_merge_options(&o, the_repository); 727 + init_ui_merge_options(&o, the_repository); 728 728 if (!strcmp(strategy, "subtree")) 729 729 o.subtree_shift = ""; 730 730
+1 -1
builtin/replay.c
··· 377 377 goto cleanup; 378 378 } 379 379 380 - init_merge_options(&merge_opt, the_repository); 380 + init_basic_merge_options(&merge_opt, the_repository); 381 381 memset(&result, 0, sizeof(result)); 382 382 merge_opt.show_rename_progress = 0; 383 383 last_commit = onto;
+1 -1
builtin/stash.c
··· 574 574 } 575 575 } 576 576 577 - init_merge_options(&o, the_repository); 577 + init_ui_merge_options(&o, the_repository); 578 578 579 579 o.branch1 = "Updated upstream"; 580 580 o.branch2 = "Stashed changes";
+1 -1
log-tree.c
··· 1025 1025 struct strbuf parent2_desc = STRBUF_INIT; 1026 1026 1027 1027 /* Setup merge options */ 1028 - init_merge_options(&o, the_repository); 1028 + init_ui_merge_options(&o, the_repository); 1029 1029 o.show_rename_progress = 0; 1030 1030 o.record_conflict_msgs_as_headers = 1; 1031 1031 o.msg_header_prefix = "remerge";
+25 -4
merge-recursive.c
··· 3921 3921 return clean ? 0 : 1; 3922 3922 } 3923 3923 3924 - static void merge_recursive_config(struct merge_options *opt) 3924 + static void merge_recursive_config(struct merge_options *opt, int ui) 3925 3925 { 3926 3926 char *value = NULL; 3927 3927 int renormalize = 0; ··· 3950 3950 } /* avoid erroring on values from future versions of git */ 3951 3951 free(value); 3952 3952 } 3953 + if (ui) { 3954 + if (!git_config_get_string("diff.algorithm", &value)) { 3955 + long diff_algorithm = parse_algorithm_value(value); 3956 + if (diff_algorithm < 0) 3957 + die(_("unknown value for config '%s': %s"), "diff.algorithm", value); 3958 + opt->xdl_opts = (opt->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm; 3959 + free(value); 3960 + } 3961 + } 3953 3962 git_config(git_xmerge_config, NULL); 3954 3963 } 3955 3964 3956 - void init_merge_options(struct merge_options *opt, 3957 - struct repository *repo) 3965 + static void init_merge_options(struct merge_options *opt, 3966 + struct repository *repo, int ui) 3958 3967 { 3959 3968 const char *merge_verbosity; 3960 3969 memset(opt, 0, sizeof(struct merge_options)); ··· 3973 3982 3974 3983 opt->conflict_style = -1; 3975 3984 3976 - merge_recursive_config(opt); 3985 + merge_recursive_config(opt, ui); 3977 3986 merge_verbosity = getenv("GIT_MERGE_VERBOSITY"); 3978 3987 if (merge_verbosity) 3979 3988 opt->verbosity = strtol(merge_verbosity, NULL, 10); 3980 3989 if (opt->verbosity >= 5) 3981 3990 opt->buffer_output = 0; 3991 + } 3992 + 3993 + void init_ui_merge_options(struct merge_options *opt, 3994 + struct repository *repo) 3995 + { 3996 + init_merge_options(opt, repo, 1); 3997 + } 3998 + 3999 + void init_basic_merge_options(struct merge_options *opt, 4000 + struct repository *repo) 4001 + { 4002 + init_merge_options(opt, repo, 0); 3982 4003 } 3983 4004 3984 4005 /*
+4 -1
merge-recursive.h
··· 54 54 struct merge_options_internal *priv; 55 55 }; 56 56 57 - void init_merge_options(struct merge_options *opt, struct repository *repo); 57 + /* for use by porcelain commands */ 58 + void init_ui_merge_options(struct merge_options *opt, struct repository *repo); 59 + /* for use by plumbing commands */ 60 + void init_basic_merge_options(struct merge_options *opt, struct repository *repo); 58 61 59 62 void copy_merge_options(struct merge_options *dst, struct merge_options *src); 60 63 void clear_merge_options(struct merge_options *opt);
+2 -2
sequencer.c
··· 762 762 763 763 repo_read_index(r); 764 764 765 - init_merge_options(&o, r); 765 + init_ui_merge_options(&o, r); 766 766 o.ancestor = base ? base_label : "(empty tree)"; 767 767 o.branch1 = "HEAD"; 768 768 o.branch2 = next ? next_label : "(empty tree)"; ··· 4309 4309 bases = reverse_commit_list(bases); 4310 4310 4311 4311 repo_read_index(r); 4312 - init_merge_options(&o, r); 4312 + init_ui_merge_options(&o, r); 4313 4313 o.branch1 = "HEAD"; 4314 4314 o.branch2 = ref_name.buf; 4315 4315 o.buffer_output = 2;
+60
t/t7615-diff-algo-with-mergy-operations.sh
··· 1 + #!/bin/sh 2 + 3 + test_description='git merge and other operations that rely on merge 4 + 5 + Testing the influence of the diff algorithm on the merge output.' 6 + 7 + TEST_PASSES_SANITIZE_LEAK=true 8 + . ./test-lib.sh 9 + 10 + test_expect_success 'setup' ' 11 + cp "$TEST_DIRECTORY"/t7615/base.c file.c && 12 + git add file.c && 13 + git commit -m c0 && 14 + git tag c0 && 15 + cp "$TEST_DIRECTORY"/t7615/ours.c file.c && 16 + git add file.c && 17 + git commit -m c1 && 18 + git tag c1 && 19 + git reset --hard c0 && 20 + cp "$TEST_DIRECTORY"/t7615/theirs.c file.c && 21 + git add file.c && 22 + git commit -m c2 && 23 + git tag c2 24 + ' 25 + 26 + GIT_TEST_MERGE_ALGORITHM=recursive 27 + 28 + test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' ' 29 + git reset --hard c1 && 30 + test_must_fail git merge -s recursive c2 31 + ' 32 + 33 + test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' ' 34 + git reset --hard c1 && 35 + git merge --strategy recursive -Xdiff-algorithm=histogram c2 36 + ' 37 + 38 + test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' ' 39 + git reset --hard c1 && 40 + git config diff.algorithm histogram && 41 + git merge --strategy recursive c2 42 + ' 43 + 44 + test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' ' 45 + git reset --hard c1 && 46 + test_must_fail git cherry-pick -s recursive c2 47 + ' 48 + 49 + test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' ' 50 + git reset --hard c1 && 51 + git cherry-pick --strategy recursive -Xdiff-algorithm=histogram c2 52 + ' 53 + 54 + test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' ' 55 + git reset --hard c1 && 56 + git config diff.algorithm histogram && 57 + git cherry-pick --strategy recursive c2 58 + ' 59 + 60 + test_done
+17
t/t7615/base.c
··· 1 + int f(int x, int y) 2 + { 3 + if (x == 0) 4 + { 5 + return y; 6 + } 7 + return x; 8 + } 9 + 10 + int g(size_t u) 11 + { 12 + while (u < 30) 13 + { 14 + u++; 15 + } 16 + return u; 17 + }
+17
t/t7615/ours.c
··· 1 + int g(size_t u) 2 + { 3 + while (u < 30) 4 + { 5 + u++; 6 + } 7 + return u; 8 + } 9 + 10 + int h(int x, int y, int z) 11 + { 12 + if (z == 0) 13 + { 14 + return x; 15 + } 16 + return y; 17 + }
+17
t/t7615/theirs.c
··· 1 + int f(int x, int y) 2 + { 3 + if (x == 0) 4 + { 5 + return y; 6 + } 7 + return x; 8 + } 9 + 10 + int g(size_t u) 11 + { 12 + while (u > 34) 13 + { 14 + u--; 15 + } 16 + return u; 17 + }