Re: [PATCH v4 3/3] replay: offer an option to linearize the commit topology
- From
Patrick Steinhardt <ps@pks.im>
- Date
- Jun 22, 2026, 13:53 UTC
- Message-ID
- <ajk-a4a3KSJ2u7Ju@pks.im>
- 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:
Show 11 quoted lines
> From: Johannes Schindelin <Johannes.Schindelin@gmx.de> > > 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.
Show 18 quoted lines
> 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.
Show 15 quoted lines
> @@ -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