Skip to content

Commit

Permalink
merge-recursive: honor diff.algorithm
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
wetneb authored and gitster committed Jul 14, 2024
1 parent 557ae14 commit 9c93ba4
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 15 deletions.
2 changes: 1 addition & 1 deletion builtin/am.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion builtin/checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion builtin/merge-recursive.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "";

Expand Down
2 changes: 1 addition & 1 deletion builtin/merge-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,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] */
Expand Down
2 changes: 1 addition & 1 deletion builtin/merge.c
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,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 = "";

Expand Down
2 changes: 1 addition & 1 deletion builtin/replay.c
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,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;
Expand Down
2 changes: 1 addition & 1 deletion builtin/stash.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion log-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
29 changes: 25 additions & 4 deletions merge-recursive.c
Original file line number Diff line number Diff line change
Expand Up @@ -3921,7 +3921,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;
Expand Down Expand Up @@ -3950,11 +3950,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));
Expand All @@ -3973,14 +3982,26 @@ 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);
if (opt->verbosity >= 5)
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
Expand Down
5 changes: 4 additions & 1 deletion merge-recursive.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions sequencer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)";
Expand Down Expand Up @@ -4309,7 +4309,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;
Expand Down
60 changes: 60 additions & 0 deletions t/t7615-diff-algo-with-mergy-operations.sh
Original file line number Diff line number Diff line change
@@ -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
17 changes: 17 additions & 0 deletions t/t7615/base.c
Original file line number Diff line number Diff line change
@@ -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;
}
17 changes: 17 additions & 0 deletions t/t7615/ours.c
Original file line number Diff line number Diff line change
@@ -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;
}
17 changes: 17 additions & 0 deletions t/t7615/theirs.c
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 9c93ba4

Please sign in to comment.