Thread (36 messages) 36 messages, 5 authors, 2022-05-12

Re: [PATCH v2 5/7] merge-recursive: add merge function arg to 'merge_recursive_generic'

flat view

From: Victoria Dye <hidden>
Date: 2022-05-09 19:25:01

Elijah Newren wrote:
On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget
[off-list ref] wrote:
quoted
From: Victoria Dye <redacted>

Replace the hardcoded 'merge_recursive()' function used by the
'merge_recursive_generic()' with a caller-specific merge function. This will
allow us to use 'merge_ort_recursive()' (and therefore avoid the index
expansion of 'merge_recursive()') in commands that perform merges with
'merge_recursive_generic()', such as 'git stash pop'.

Note that this patch is strictly a refactor; all callers still use
'merge_recursive()', and any changing to 'merge_ort_recursive()' will be
done in a later commit.
I'm not sure if we can gut merge_recursive_generic(), but I don't
think stash should use it...
quoted
Signed-off-by: Victoria Dye <redacted>
---
 builtin/am.c              | 2 +-
 builtin/merge-recursive.c | 2 +-
 builtin/stash.c           | 2 +-
 merge-ort.c               | 3 ++-
 merge-recursive.c         | 4 ++--
 merge-recursive.h         | 9 ++++++++-
 6 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..6d01185d122 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1614,7 +1614,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
        if (state->quiet)
                o.verbosity = 0;

-       if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
+       if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, merge_recursive, &result)) {
                repo_rerere(the_repository, state->allow_rerere_autoupdate);
                free(their_tree_name);
                return error(_("Failed to merge in the changes."));
diff --git a/builtin/merge-recursive.c b/builtin/merge-recursive.c
index b9acbf5d342..687ed1e527b 100644
--- a/builtin/merge-recursive.c
+++ b/builtin/merge-recursive.c
@@ -81,7 +81,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
        if (o.verbosity >= 3)
                printf(_("Merging %s with %s\n"), o.branch1, o.branch2);

-       failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, &result);
+       failed = merge_recursive_generic(&o, &h1, &h2, bases_count, bases, merge_recursive, &result);

        free(better1);
        free(better2);
diff --git a/builtin/stash.c b/builtin/stash.c
index 1bfba532044..16171eb1dab 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -554,7 +554,7 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
        bases[0] = &info->b_tree;

        ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
-                                     &result);
+                                     merge_recursive, &result);
        if (ret) {
                rerere(0);
diff --git a/merge-ort.c b/merge-ort.c
index 8545354dafd..4bccdfcf355 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -4737,7 +4737,8 @@ void merge_incore_recursive(struct merge_options *opt,
        trace2_region_enter("merge", "incore_recursive", opt->repo);

        /* We set the ancestor label based on the merge_bases */
-       assert(opt->ancestor == NULL);
+       assert(opt->ancestor == NULL ||
+              !strcmp(opt->ancestor, "constructed merge base"));
...and here's one of the reasons why.  The fact that
merge_recursive_generic() uses this string when exactly one merge base
is passed is something that is only correct for git-am; it is wrong
and actively misleading for git-stash since it has a real merge base
that is not internally constructed by the operation using the merge
machinery.  (The merge base it uses is something like $STASH^1, IIRC.)

In fact, this was half the coin around why merge_recursive_generic()
wasn't converted when merge-ort was written; see
https://lore.kernel.org/git/CABPp-BHW61zA+MefvWK47iVZKY97rxc2XZ-NjXzuJxEhgSLqUw@mail.gmail.com/ (local)
and https://lore.kernel.org/git/CABPp-BFr=1iVY739cfh-1Hp82x-Mny-k4y0f3zZ_YuP3PxiGfQ@mail.gmail.com/ (local)
for more details.
All of that makes sense, thanks for the context!
The use of merge_recursive_generic() by stash is also a bit weird;
most of the time, stash is going to have actual commits instead of
just trees.  But stash dereferences those commits to trees, passes
them to merge_recursive_generic(), and then merge_recursive_generic()
has to create fake commits containing those trees, because the merge
machinery wants commits.  It feels a bit like a Rube Goldberg machine.
Also, stash also always calls merge_recursive_generic() with exactly
one merge base, which together with having real commits both kind of
defeat the need for "generic".    I think stash should just use
merge_trees()/merge_incore_nonrecursive() directly (much as
sequencer.c does).  The only special case to worry about with stash is
when c_tree != HEAD^{tree}, i.e. when applying changes on top of
already present changes instead of just on top of HEAD.  But in that
case, I think stash should be the thing to create a fake commit rather
than invoking some wrapper that will create fake commits for all three
trees.
I'm a bit confused about this. The non-recursive merge functions
('merge_trees()' & 'merge_ort_nonrecursive()' or the lower-level
'merge_incore_nonrecursive()') merge trees, not commits, so performing a
non-recursive merge requires dereferencing commits to trees anyway. I think
I agree with your other message [1] that the 'stash' merge doesn't need to
merge recursively, but that would mean it also doesn't use the commits
*directly* (i.e., as arguments in the merge). 

Apologies if I'm missing something obvious, but I want to make sure I
understand your suggestion.

[1] https://lore.kernel.org/git/CABPp-BFANwZn73w8zrVySB7mh0bQQBdGJjBuSJy50UOeyYT6aA@mail.gmail.com/ (local) 

quoted
        trace2_region_enter("merge", "merge_start", opt->repo);
        merge_start(opt, result);
diff --git a/merge-recursive.c b/merge-recursive.c
index 1ee6364e8b1..2088f5c5fb3 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -3806,6 +3806,7 @@ int merge_recursive_generic(struct merge_options *opt,
                            const struct object_id *merge,
                            int num_merge_bases,
                            const struct object_id **merge_bases,
+                           recursive_merge_fn_t merge_fn,
                            struct commit **result)
 {
        int clean;
@@ -3829,8 +3830,7 @@ int merge_recursive_generic(struct merge_options *opt,
        }

        repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
-       clean = merge_recursive(opt, head_commit, next_commit, ca,
-                               result);
+       clean = merge_fn(opt, head_commit, next_commit, ca, result);
        if (clean < 0) {
                rollback_lock_file(&lock);
                return clean;
diff --git a/merge-recursive.h b/merge-recursive.h
index b88000e3c25..6a21f2da538 100644
--- a/merge-recursive.h
+++ b/merge-recursive.h
@@ -53,6 +53,12 @@ struct merge_options {
        struct merge_options_internal *priv;
 };

+typedef int (*recursive_merge_fn_t)(struct merge_options *opt,
+                                   struct commit *h1,
+                                   struct commit *h2,
+                                   struct commit_list *merge_bases,
+                                   struct commit **result);
+
 void init_merge_options(struct merge_options *opt, struct repository *repo);

 /* parse the option in s and update the relevant field of opt */
@@ -105,7 +111,7 @@ int merge_recursive(struct merge_options *opt,

 /*
  * merge_recursive_generic can operate on trees instead of commits, by
- * wrapping the trees into virtual commits, and calling merge_recursive().
+ * wrapping the trees into virtual commits, and calling the provided merge_fn.
  * It also writes out the in-memory index to disk if the merge is successful.
  *
  * Outputs:
@@ -120,6 +126,7 @@ int merge_recursive_generic(struct merge_options *opt,
                            const struct object_id *merge,
                            int num_merge_bases,
                            const struct object_id **merge_bases,
+                           recursive_merge_fn_t merge_fn,
                            struct commit **result);

 #endif
--
gitgitgadget
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help