From: "Antonin Delpeuch via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: "Junio C Hamano [ ]" <gitster@pobox.com>,
Antonin Delpeuch <antonin@delpeuch.eu>,
Antonin Delpeuch <antonin@delpeuch.eu>
Subject: [PATCH v3] merge-recursive: honor diff.algorithm
Date: Sat, 13 Jul 2024 16:51:46 +0000 [thread overview]
Message-ID: <pull.1743.v3.git.git.1720889507066.gitgitgadget@gmail.com> (raw)
In-Reply-To: <pull.1743.v2.git.git.1720551701648.gitgitgadget@gmail.com>
From: Antonin Delpeuch <antonin@delpeuch.eu>
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>
---
merge-recursive: honor diff.algorithm
Changes since v2:
* merge test cases of "merge" and "cherry-pick" together to keep things
simple
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1743%2Fwetneb%2Frecursive_respects_diff.algorithm-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1743/wetneb/recursive_respects_diff.algorithm-v3
Pull-Request: https://github.com/git/git/pull/1743
Range-diff vs v2:
1: 9c1907fad43 ! 1: abba5c3c0cf merge-recursive: honor diff.algorithm
@@ sequencer.c: static int do_merge(struct repository *r,
o.branch2 = ref_name.buf;
o.buffer_output = 2;
- ## t/t3515-cherry-pick-diff.sh (new) ##
+ ## t/t7615-diff-algo-with-mergy-operations.sh (new) ##
@@
+#!/bin/sh
+
-+test_description='git cherry-pick
++test_description='git merge and other operations that rely on merge
+
+Testing the influence of the diff algorithm on the merge output.'
+
@@ t/t3515-cherry-pick-diff.sh (new)
+. ./test-lib.sh
+
+test_expect_success 'setup' '
-+ cp "$TEST_DIRECTORY"/t3515/base.c file.c &&
++ cp "$TEST_DIRECTORY"/t7615/base.c file.c &&
+ git add file.c &&
+ git commit -m c0 &&
+ git tag c0 &&
-+ cp "$TEST_DIRECTORY"/t3515/ours.c file.c &&
++ cp "$TEST_DIRECTORY"/t7615/ours.c file.c &&
+ git add file.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
-+ cp "$TEST_DIRECTORY"/t3515/theirs.c file.c &&
++ cp "$TEST_DIRECTORY"/t7615/theirs.c file.c &&
+ git add file.c &&
+ git commit -m c2 &&
+ git tag c2
+'
+
++GIT_TEST_MERGE_ALGORITHM=recursive
++
++test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
++ git reset --hard c1 &&
++ test_must_fail git merge -s recursive c2
++'
++
++test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' '
++ git reset --hard c1 &&
++ git merge --strategy recursive -Xdiff-algorithm=histogram c2
++'
++
++test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' '
++ git reset --hard c1 &&
++ git config diff.algorithm histogram &&
++ git merge --strategy recursive c2
++'
++
+test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
+ git reset --hard c1 &&
+ test_must_fail git cherry-pick -s recursive c2
@@ t/t3515-cherry-pick-diff.sh (new)
+ git config diff.algorithm histogram &&
+ git cherry-pick --strategy recursive c2
+'
++
+test_done
- ## t/t3515/base.c (new) ##
+ ## t/t7615/base.c (new) ##
@@
+int f(int x, int y)
+{
@@ t/t3515/base.c (new)
+ return u;
+}
- ## t/t3515/ours.c (new) ##
+ ## t/t7615/ours.c (new) ##
@@
+int g(size_t u)
+{
@@ t/t3515/ours.c (new)
+ return y;
+}
- ## t/t3515/theirs.c (new) ##
+ ## t/t7615/theirs.c (new) ##
@@
+int f(int x, int y)
+{
@@ t/t3515/theirs.c (new)
+ }
+ return u;
+}
-
- ## t/t7615-merge-diff.sh (new) ##
-@@
-+#!/bin/sh
-+
-+test_description='git merge
-+
-+Testing the influence of the diff algorithm on the merge output.'
-+
-+TEST_PASSES_SANITIZE_LEAK=true
-+. ./test-lib.sh
-+
-+test_expect_success 'setup' '
-+ cp "$TEST_DIRECTORY"/t3515/base.c file.c &&
-+ git add file.c &&
-+ git commit -m c0 &&
-+ git tag c0 &&
-+ cp "$TEST_DIRECTORY"/t3515/ours.c file.c &&
-+ git add file.c &&
-+ git commit -m c1 &&
-+ git tag c1 &&
-+ git reset --hard c0 &&
-+ cp "$TEST_DIRECTORY"/t3515/theirs.c file.c &&
-+ git add file.c &&
-+ git commit -m c2 &&
-+ git tag c2
-+'
-+
-+GIT_TEST_MERGE_ALGORITHM=recursive
-+
-+test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
-+ git reset --hard c1 &&
-+ test_must_fail git merge -s recursive c2
-+'
-+
-+test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' '
-+ git reset --hard c1 &&
-+ git merge --strategy recursive -Xdiff-algorithm=histogram c2
-+'
-+
-+test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' '
-+ git reset --hard c1 &&
-+ git config diff.algorithm histogram &&
-+ git merge --strategy recursive c2
-+'
-+test_done
builtin/am.c | 2 +-
builtin/checkout.c | 2 +-
builtin/merge-recursive.c | 2 +-
builtin/merge-tree.c | 2 +-
builtin/merge.c | 2 +-
builtin/replay.c | 2 +-
builtin/stash.c | 2 +-
log-tree.c | 2 +-
merge-recursive.c | 29 +++++++++--
merge-recursive.h | 5 +-
sequencer.c | 4 +-
t/t7615-diff-algo-with-mergy-operations.sh | 60 ++++++++++++++++++++++
t/t7615/base.c | 17 ++++++
t/t7615/ours.c | 17 ++++++
t/t7615/theirs.c | 17 ++++++
15 files changed, 150 insertions(+), 15 deletions(-)
create mode 100755 t/t7615-diff-algo-with-mergy-operations.sh
create mode 100644 t/t7615/base.c
create mode 100644 t/t7615/ours.c
create mode 100644 t/t7615/theirs.c
diff --git a/builtin/am.c b/builtin/am.c
index 8f9619ea3a3..b821561b15a 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1630,7 +1630,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
* changes.
*/
- init_merge_options(&o, the_repository);
+ init_ui_merge_options(&o, the_repository);
o.branch1 = "HEAD";
their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3cf44b4683a..5769efaca00 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -884,7 +884,7 @@ static int merge_working_tree(const struct checkout_opts *opts,
add_files_to_cache(the_repository, NULL, NULL, NULL, 0,
0);
- init_merge_options(&o, the_repository);
+ init_ui_merge_options(&o, the_repository);
o.verbosity = 0;
work = write_in_core_index_as_tree(the_repository);
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index c2ce044a201..9e9d0b57158 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -31,7 +31,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix UNUSED)
char *better1, *better2;
struct commit *result;
- init_merge_options(&o, the_repository);
+ init_basic_merge_options(&o, the_repository);
if (argv[0] && ends_with(argv[0], "-subtree"))
o.subtree_shift = "";
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 1082d919fd1..aab0843ff5a 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -570,7 +570,7 @@ int cmd_merge_tree(int argc, const char **argv, const char *prefix)
};
/* Init merge options */
- init_merge_options(&o.merge_options, the_repository);
+ init_ui_merge_options(&o.merge_options, the_repository);
/* Parse arguments */
original_argc = argc - 1; /* ignoring argv[0] */
diff --git a/builtin/merge.c b/builtin/merge.c
index 66a4fa72e1c..686326bc1d3 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -720,7 +720,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
return 2;
}
- init_merge_options(&o, the_repository);
+ init_ui_merge_options(&o, the_repository);
if (!strcmp(strategy, "subtree"))
o.subtree_shift = "";
diff --git a/builtin/replay.c b/builtin/replay.c
index 6bf0691f15d..d90ddd0837d 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -373,7 +373,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
goto cleanup;
}
- init_merge_options(&merge_opt, the_repository);
+ init_basic_merge_options(&merge_opt, the_repository);
memset(&result, 0, sizeof(result));
merge_opt.show_rename_progress = 0;
last_commit = onto;
diff --git a/builtin/stash.c b/builtin/stash.c
index 7859bc0866a..86803755f03 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -574,7 +574,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
}
}
- init_merge_options(&o, the_repository);
+ init_ui_merge_options(&o, the_repository);
o.branch1 = "Updated upstream";
o.branch2 = "Stashed changes";
diff --git a/log-tree.c b/log-tree.c
index 101079e8200..5d8fb6ff8df 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -1025,7 +1025,7 @@ static int do_remerge_diff(struct rev_info *opt,
struct strbuf parent2_desc = STRBUF_INIT;
/* Setup merge options */
- init_merge_options(&o, the_repository);
+ init_ui_merge_options(&o, the_repository);
o.show_rename_progress = 0;
o.record_conflict_msgs_as_headers = 1;
o.msg_header_prefix = "remerge";
diff --git a/merge-recursive.c b/merge-recursive.c
index 46ee364af73..cd9bd3c03ef 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3901,7 +3901,7 @@ int merge_recursive_generic(struct merge_options *opt,
return clean ? 0 : 1;
}
-static void merge_recursive_config(struct merge_options *opt)
+static void merge_recursive_config(struct merge_options *opt, int ui)
{
char *value = NULL;
int renormalize = 0;
@@ -3930,11 +3930,20 @@ static void merge_recursive_config(struct merge_options *opt)
} /* avoid erroring on values from future versions of git */
free(value);
}
+ if (ui) {
+ if (!git_config_get_string("diff.algorithm", &value)) {
+ long diff_algorithm = parse_algorithm_value(value);
+ if (diff_algorithm < 0)
+ die(_("unknown value for config '%s': %s"), "diff.algorithm", value);
+ opt->xdl_opts = (opt->xdl_opts & ~XDF_DIFF_ALGORITHM_MASK) | diff_algorithm;
+ free(value);
+ }
+ }
git_config(git_xmerge_config, NULL);
}
-void init_merge_options(struct merge_options *opt,
- struct repository *repo)
+static void init_merge_options(struct merge_options *opt,
+ struct repository *repo, int ui)
{
const char *merge_verbosity;
memset(opt, 0, sizeof(struct merge_options));
@@ -3953,7 +3962,7 @@ void init_merge_options(struct merge_options *opt,
opt->conflict_style = -1;
- merge_recursive_config(opt);
+ merge_recursive_config(opt, ui);
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
if (merge_verbosity)
opt->verbosity = strtol(merge_verbosity, NULL, 10);
@@ -3961,6 +3970,18 @@ void init_merge_options(struct merge_options *opt,
opt->buffer_output = 0;
}
+void init_ui_merge_options(struct merge_options *opt,
+ struct repository *repo)
+{
+ init_merge_options(opt, repo, 1);
+}
+
+void init_basic_merge_options(struct merge_options *opt,
+ struct repository *repo)
+{
+ init_merge_options(opt, repo, 0);
+}
+
/*
* For now, members of merge_options do not need deep copying, but
* it may change in the future, in which case we would need to update
diff --git a/merge-recursive.h b/merge-recursive.h
index e67d38c3030..85a5c332bbb 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -54,7 +54,10 @@ struct merge_options {
struct merge_options_internal *priv;
};
-void init_merge_options(struct merge_options *opt, struct repository *repo);
+/* for use by porcelain commands */
+void init_ui_merge_options(struct merge_options *opt, struct repository *repo);
+/* for use by plumbing commands */
+void init_basic_merge_options(struct merge_options *opt, struct repository *repo);
void copy_merge_options(struct merge_options *dst, struct merge_options *src);
void clear_merge_options(struct merge_options *opt);
diff --git a/sequencer.c b/sequencer.c
index b4f055e5a85..3608374166a 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -762,7 +762,7 @@ static int do_recursive_merge(struct repository *r,
repo_read_index(r);
- init_merge_options(&o, r);
+ init_ui_merge_options(&o, r);
o.ancestor = base ? base_label : "(empty tree)";
o.branch1 = "HEAD";
o.branch2 = next ? next_label : "(empty tree)";
@@ -4308,7 +4308,7 @@ static int do_merge(struct repository *r,
bases = reverse_commit_list(bases);
repo_read_index(r);
- init_merge_options(&o, r);
+ init_ui_merge_options(&o, r);
o.branch1 = "HEAD";
o.branch2 = ref_name.buf;
o.buffer_output = 2;
diff --git a/t/t7615-diff-algo-with-mergy-operations.sh b/t/t7615-diff-algo-with-mergy-operations.sh
new file mode 100755
index 00000000000..9a83be518cb
--- /dev/null
+++ b/t/t7615-diff-algo-with-mergy-operations.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='git merge and other operations that rely on merge
+
+Testing the influence of the diff algorithm on the merge output.'
+
+TEST_PASSES_SANITIZE_LEAK=true
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ cp "$TEST_DIRECTORY"/t7615/base.c file.c &&
+ git add file.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ cp "$TEST_DIRECTORY"/t7615/ours.c file.c &&
+ git add file.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ cp "$TEST_DIRECTORY"/t7615/theirs.c file.c &&
+ git add file.c &&
+ git commit -m c2 &&
+ git tag c2
+'
+
+GIT_TEST_MERGE_ALGORITHM=recursive
+
+test_expect_success 'merge c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
+ git reset --hard c1 &&
+ test_must_fail git merge -s recursive c2
+'
+
+test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' '
+ git reset --hard c1 &&
+ git merge --strategy recursive -Xdiff-algorithm=histogram c2
+'
+
+test_expect_success 'merge c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' '
+ git reset --hard c1 &&
+ git config diff.algorithm histogram &&
+ git merge --strategy recursive c2
+'
+
+test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy fails with the current default myers diff algorithm' '
+ git reset --hard c1 &&
+ test_must_fail git cherry-pick -s recursive c2
+'
+
+test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with -Xdiff-algorithm=histogram' '
+ git reset --hard c1 &&
+ git cherry-pick --strategy recursive -Xdiff-algorithm=histogram c2
+'
+
+test_expect_success 'cherry-pick c2 to c1 with recursive merge strategy succeeds with diff.algorithm = histogram' '
+ git reset --hard c1 &&
+ git config diff.algorithm histogram &&
+ git cherry-pick --strategy recursive c2
+'
+
+test_done
diff --git a/t/t7615/base.c b/t/t7615/base.c
new file mode 100644
index 00000000000..c64abc59366
--- /dev/null
+++ b/t/t7615/base.c
@@ -0,0 +1,17 @@
+int f(int x, int y)
+{
+ if (x == 0)
+ {
+ return y;
+ }
+ return x;
+}
+
+int g(size_t u)
+{
+ while (u < 30)
+ {
+ u++;
+ }
+ return u;
+}
diff --git a/t/t7615/ours.c b/t/t7615/ours.c
new file mode 100644
index 00000000000..44d82513970
--- /dev/null
+++ b/t/t7615/ours.c
@@ -0,0 +1,17 @@
+int g(size_t u)
+{
+ while (u < 30)
+ {
+ u++;
+ }
+ return u;
+}
+
+int h(int x, int y, int z)
+{
+ if (z == 0)
+ {
+ return x;
+ }
+ return y;
+}
diff --git a/t/t7615/theirs.c b/t/t7615/theirs.c
new file mode 100644
index 00000000000..85f02146fee
--- /dev/null
+++ b/t/t7615/theirs.c
@@ -0,0 +1,17 @@
+int f(int x, int y)
+{
+ if (x == 0)
+ {
+ return y;
+ }
+ return x;
+}
+
+int g(size_t u)
+{
+ while (u > 34)
+ {
+ u--;
+ }
+ return u;
+}
base-commit: 06e570c0dfb2a2deb64d217db78e2ec21672f558
--
gitgitgadget
prev parent reply other threads:[~2024-07-13 16:51 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-07-08 9:34 [PATCH] merge-recursive: honor diff.algorithm Antonin Delpeuch via GitGitGadget
2024-07-09 17:16 ` Antonin Delpeuch
2024-07-09 18:51 ` Junio C Hamano
2024-07-09 19:01 ` [PATCH v2] " Antonin Delpeuch via GitGitGadget
2024-07-10 17:58 ` Junio C Hamano
2024-07-13 16:50 ` Antonin Delpeuch
2024-07-13 16:51 ` Antonin Delpeuch via GitGitGadget [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=pull.1743.v3.git.git.1720889507066.gitgitgadget@gmail.com \
--to=gitgitgadget@gmail.com \
--cc=antonin@delpeuch.eu \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.