Message ID | 4537d473b937b182cd79b2f3c5673b75d92cab23.1651083378.git.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Sparse index: integrate with 'git stash' | expand |
On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget <gitgitgadget@gmail.com> wrote: > > From: Victoria Dye <vdye@github.com> > > 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... > Signed-off-by: Victoria Dye <vdye@github.com> > --- > 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/ and https://lore.kernel.org/git/CABPp-BFr=1iVY739cfh-1Hp82x-Mny-k4y0f3zZ_YuP3PxiGfQ@mail.gmail.com/ for more details. 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. > 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
Elijah Newren wrote: > On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget > <gitgitgadget@gmail.com> wrote: >> >> From: Victoria Dye <vdye@github.com> >> >> 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... > >> Signed-off-by: Victoria Dye <vdye@github.com> >> --- >> 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/ > and https://lore.kernel.org/git/CABPp-BFr=1iVY739cfh-1Hp82x-Mny-k4y0f3zZ_YuP3PxiGfQ@mail.gmail.com/ > 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/ >> 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
On Mon, May 9, 2022 at 12:24 PM Victoria Dye <vdye@github.com> wrote: > > Elijah Newren wrote: > > On Wed, Apr 27, 2022 at 11:16 AM Victoria Dye via GitGitGadget > > <gitgitgadget@gmail.com> wrote: > >> > >> From: Victoria Dye <vdye@github.com> > >> > >> 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... > > > >> Signed-off-by: Victoria Dye <vdye@github.com> > >> --- > >> 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/ > > and https://lore.kernel.org/git/CABPp-BFr=1iVY739cfh-1Hp82x-Mny-k4y0f3zZ_YuP3PxiGfQ@mail.gmail.com/ > > 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/ Oh, right, it's only the recursive merge that needs commits (so that it can find ancestors and ancestors of ancestors, etc.). So, ignore my comments about making fake commits; that's not needed.
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")); 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