From: Patrick Steinhardt Date: Mon, 22 Jun 2026 13:53:47 GMT Subject: Re: [PATCH v4 3/3] replay: offer an option to linearize the commit topology Message-ID: In-Reply-To: <20260622-toon-git-replay-drop-merges-v4-3-ff257f534319@iotcl.com> On Mon, Jun 22, 2026 at 02:41:57PM +0200, Toon Claes wrote: > From: Johannes Schindelin > > One of the stated goals of git-replay(1) is to allow implementing the > git-rebase(1) functionality on the server side. > > The default mode of git-rebase(1) is to act as if `--no-rebase-merges` > was given. This mode drops merge commits instead of replaying them, and > linearizes the commit history into a sequence of the > regular (single-parent) commits. > > Add option `--linearize` to git-replay(1) to do the same. git-rebase(1) essentially knows about three different modes: - "--no-rebase-merges", which is the default and maps to your "--linearize". - "--rebase-merges", which by default doesn't rebase cousins by using "--ancestry-path" internally. - "--rebase-merges=rebase-cousins", which doesn't pass the above option. So it's not a simple boolean there, which makes me wonder whether we should mirror the same interface so that all of git-rebase(1)'s modes can be represented, as well. > diff --git a/replay.c b/replay.c > index 7921d7dba3..5539daff00 100644 > --- a/replay.c > +++ b/replay.c > @@ -277,12 +277,16 @@ static struct commit *pick_regular_commit(struct repository *repo, > struct commit *onto, > struct merge_options *merge_opt, > struct merge_result *result, > + struct commit *replayed_base, > bool reverse, > enum replay_empty_commit_action empty) > { > - struct commit *base, *replayed_base; > + struct commit *base; > struct tree *pickme_tree, *base_tree, *replayed_base_tree; > > + if (replayed_base && reverse) > + BUG("Linearizing commits is not supported when replaying in reverse"); Nit: Error messages should typically start with a lower-case letter. > @@ -430,12 +435,25 @@ int replay_revisions(struct rev_info *revs, > while ((commit = get_revision(revs))) { > const struct name_decoration *decoration; > > - if (commit->parents && commit->parents->next) > - die(_("replaying merge commits is not supported yet!")); > + if (commit->parents && commit->parents->next) { > + if (!opts->linearize) > + die(_("replaying merge commits is not supported yet!")); > + /* > + * Drop the merge commit: do not pick it and leave > + * last_commit unchanged, so its children (and any ref > + * pointing at it) are reparented onto the previous > + * non-merge commit, which the ref-update loop below uses. > + */ One could add a hint here that tells the user to pass the option. But I guess that might be somewhat weird, as we cannot assume that we're called by git-replay(1) here. In any case, this here is the core of the change where we stop dying in case "--linearize" was passed, and instead we simply skip the commit altogether. Makes sense. Thanks! Patrick