From: Siddharth Asthana <siddharthasthana31@gmail•com>
To: Toon Claes <toon@iotcl•com>, Justin Tobler <jltobler@gmail•com>
Cc: git@vger•kernel.org, Christian Couder <chriscool@tuxfamily•org>
Subject: Re: [PATCH RFC] git-replay: implement subcommands
Date: Sat, 14 Mar 2026 12:48:55 +0530 [thread overview]
Message-ID: <5370f3b2-2c4a-4d9d-904d-2a8f6094b6e1@gmail.com> (raw)
In-Reply-To: <87v7ezsnqf.fsf@iotcl.com>
On 13/03/26 21:52, Toon Claes wrote:
> Justin Tobler <jltobler@gmail•com> writes:
>
>> On 26/03/09 08:30PM, Toon Claes wrote:
>>> git-replay(1) has various operation modes. The mode depends on which of
>>> the options `--onto`, `--advance`, or `--revert` is given. These options
>>> are mutually exclusive. This usage pattern is counterintuitive and
>>> uncommon for Git commands to behave this way.
>>>
>>> Implement subcommands into git-replay(1):
>>>
>>> * `rebase`: This replaces what `--onto=` used to do.
>>
>> I'm a bit confused by this. It appears that the "rebase" subcommand
>> still requires the `--onto` option so it doesn't seem to really be
>> replacing anything. I assume we are tyring to break these operations
>> into distinct categories which seems reasonable.
>
> Okay, maybe I should be more verbose about the problem I'm trying to
> solve.
>
> Let's start with the existing option `--onto`. This implies a "rebase"
> operation. The argument to this option is a revision which acts as the
> base for the rebase. On this base the commits in the revision-range are
> replayed.
>
> In the end, git-replay(1) takes the ref from the upper boundary of this
> revision-range, and updates that to the result of the rebase.
>
> A usage example:
>
> $ git replay --onto=master master..my-branch
>
> With option --ref-action=print, you'll get:
>
> update refs/heads/my-branch aaabbbccc 000111222
>
> (using abbreviated OIDs for simplification in this email)
>
> So 000111222 would be the commit my-branch is pointing to before the
> git-replay(1), and aaabbbccc the new commit.
>
> Now looking at `--advance`, this works differently:
>
> $ git replay --advance=other-branch master..my-branch
>
> With option --ref-action=print, you'll get:
>
> update refs/heads/other-branch 888999000 444555666
>
> As you can see, the argument of --advance is the ref that gets updated.
>
> So this command would be identical to:
>
> $ git replay --advance=other-branch master..000111222
>
> If we try to do use the commit OID in the revision-range when using
> --onto:
>
> $ git replay --onto=master master..000111222
>
> Nothing (noticable) happens. git-replay(1) does the replay, but doesn't
> know which ref to update.
>
> This assymetry between --onto and --revert & --advance is the main issue
> I'm trying to resolve with this proposal.
>
>
>>> * `pick`: This replaces what `--advance=` used to do.
>>> * `revert`: This replaces what `--revert=` used to do.
>>>
>>> Option `--onto` is still accepted. It's mandatory for the `rebase`
>>> subcommand and needs to be used in the exact same way.
>>>
>>> Option `--ref` is added and required for the `pick` and `revert`
>>> subcommands. This replaces what `--advance` and `--revert` used to do,
>>> but as a single uniform option for all subcommands.
>>>
>>> The `rebase` subcommand also accepts option `--ref`, and when given this
>>> is the ref that's updated with the outcome of the git-replay(1) command.
>>> Thus following commands are identical:
>>>
>>> $ git replay rebase --onto=master master..branch-1
>>>
>>> $ git replay rebase --onto=master master..branch-1^{0} --ref=refs/heads/branch-1
>>>
>>> In the second example the upper boundary of the revision range is peeled
>>> down to a commit (using '^{0}'). Without option `--ref`, git-replay(1)
>>> doesn't know which ref to update, that's why `--ref` is passed
>>> explicitly.
>>>
>>> For the subcommands `pick` and `revert` it's also possible to combine
>>> `--ref` and `--onto`. Here are again two identical examples:
>>>
>>> $ git replay pick --onto=branch-1 master..aabbccdd
>>>
>>> $ git replay pick --onto=branch-1^{0} master..aabbccdd --ref=refs/heads/branch-1
>>>
>>> In the latter the argument for `--onto` is peeled down to a commit, so
>>> the command doesn't know which ref to update. To inform git-replay(1)
>>> which refs should be updated, it's passed explicitly as option `--ref`.
>>>
>>> Signed-off-by: Toon Claes <toon@iotcl•com>
>>> ---
>>> In the patch series by Siddharth Asthana[1] the option `--revert` is
>>> added to git-replay(1). This is implemented as option `--revert`, next
>>> to the existing options `--advance` and `--onto`.
>>>
>>> The usage of these options is mutually exclusive, so the user can only
>>> use one of them, and depending on which one, git-replay(1) selects a
>>> "mode of operating".
>>>
>>> Various people have raised this behavior is somewhat confusing. In this
>>> series we attempt to make the usage of git-replay(1) more intuitive and
>>> user-friendly by implementing the modes as subcommands.
>>
>> Ok, subcommands for git-replay(1) seem like they could be a good fit
>> here.
>
> <3
>
>>
>>> This patch is submitted as an RFC to gather feedback about the design.
>>> All changes are implemented as a single patch right now, and thus
>>> reviewing the changes might be challenging. When we got people aligned
>>> on the direction, I'll work toward cleaner patches.
>>>
>>> These changes are based on 'master' at 864f55e190 (The second batch,
>>> 2026-02-09) with the patches of Siddharth[1] applied: 'sa/replay-revert'
>>> at f79189a653 (replay: add --revert mode to reverse commit changes,
>>> 2026-02-19)
>>>
>>> [1]: 20260218234215.89326-3-siddharthasthana31@gmail•com
>>> ---
>>> Documentation/git-replay.adoc | 124 ++++++++++++++++----------
>>> builtin/replay.c | 150 ++++++++++++++++++++++++-------
>>> replay.c | 66 +++++++-------
>>> replay.h | 31 +++----
>>> t/t3650-replay-basics.sh | 199 +++++++++++++++++++++++-------------------
>>> 5 files changed, 349 insertions(+), 221 deletions(-)
>>>
>>> diff --git a/Documentation/git-replay.adoc b/Documentation/git-replay.adoc
>>> index ffdf790278..a7e8dac23f 100644
>>> --- a/Documentation/git-replay.adoc
>>> +++ b/Documentation/git-replay.adoc
>>> @@ -8,8 +8,13 @@ git-replay - EXPERIMENTAL: Replay commits on a new base, works with bare repos t
>>>
>>> SYNOPSIS
>>> --------
>>> -[verse]
>>> -(EXPERIMENTAL!) 'git replay' ([--contained] --onto <newbase> | --advance <branch> | --revert <branch>) [--ref-action[=<mode>]] <revision-range>...
>>
>> Do we intent to remove the experimental marker?
>
> Yeah, I did. I don't like them in this synopsis. It seems git-replay(1)
> is the only one doing this, so I'd like to get rid of it.
>
> Doing this should end up in a separate commit.
>
>>> +[synopsis]
>>> +git replay rebase --onto <newbase> [--ref <branch>] [--contained]
>>> + [--ref-action[=<mode>]] <revision-range>
>>> +git replay pick --ref <branch> [--onto <newbase>]
>>> + [--ref-action[=<mode>]] <revision-range>
>>> +git replay revert --ref <branch> [--onto <newbase>]
>>> + [--ref-action[=<mode>]] <revision-range>
>>
>> Subcommands with required options like this feel quite bad IMO and I'm
>> not sure it makes it much more intuitive.
>
> Okay, I did some looking up, and maybe you're right, I couldn't find any
> other command that has required options. It seems all commands have some
> kind of "default behavior" when no options are given.
>
>> I guess subcommands do make it easier to convey which options pertain
>> to which operation. Maybe it would be better if required arguments
>> remained positional though?
>
> I'm not sure that's better:
>
> [synopsis]
> git replay rebase <newbase> [--ref <branch>] [--contained]
> [--ref-action[=<mode>]] <revision-range>
> git replay pick <branch> [--onto <newbase>]
> [--ref-action[=<mode>]] <revision-range>
> git replay revert <branch> [--onto <newbase>]
> [--ref-action[=<mode>]] <revision-range>
>
> I don't think is better because the required argument for 'rebase' is
> used differently than 'pick' and 'revert', as explained above.
>
> I guess my main gripe with the current options is the naming: `--onto`
> to rebase, `--advance` to cherry-pick, and `--revert` to revert. And
> while the last one does sound intuitive, the argument to that option is
> the branch you want to replay the reverted commits onto. So the argument
> isn't *what* you're reverting, it's *where* you're reverting to.
>
> With my proposal I wanted to make that more clear.
>
> This proposal is trying to be not too disruptive (for example, for
> rebase you only need to a add `rebase`), but that's maybe not a good
> idea. So an alternative could be: on top of this proposal, make both
> `--onto` and `--ref` required. In various cases the user will provide
> the exact same argument to both options, but since git-replay(1) is a
> plumbing command, we can consider this is acceptable?
>
> And now, while writing this, I was thinking about yet another proposal.
> Because --advance and --revert are pretty similar. Why is --revert not
> an additional option you can add when using --advance. So instead of:
>
> git replay --revert=my-branch master..other-branch
> git replay --advance=my-branch --revert master..other-branch
>
> @Siddharth, have you considered that?
Yeah, I though about this early on but it didn't feel right. --advance
means "cherry-pick onto this branch and move it forward", so combining
it with --revert reads weird -- you would be "advancing" by reverting.
They also work quite differently under the hood. Revert uses
newest-first ordering (revs.reverse = 0) while advance uses
oldest-first, and revert sets author to the current user (author = NULL
in create_commit) instead of preserving the original. So they really are
different modes, kind of like how cherry-pick and revert are separate
commands even though the merge machinery is shared.
On the subcommand RFC -- I think this is a good direction and it would
clean up the asymmetry you pointed out between --onto and
--advance/--revert. My v4 should be a clean base for it (which your RFC
already builds on).
Thanks,
Siddharth
>
>> Also, using these subcommands appears to be required now which is a
>> breaking change compared to before. The command is experimental, so this
>> may be fine, but should probably be more directly mentioned.
>
> Sure, I can do that when I clean up the commits.
>
prev parent reply other threads:[~2026-03-14 7:18 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-09 19:30 [PATCH RFC] git-replay: implement subcommands Toon Claes
2026-03-11 18:33 ` Justin Tobler
2026-03-13 16:22 ` Toon Claes
2026-03-14 7:18 ` Siddharth Asthana [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=5370f3b2-2c4a-4d9d-904d-2a8f6094b6e1@gmail.com \
--to=siddharthasthana31@gmail$(echo .)com \
--cc=chriscool@tuxfamily$(echo .)org \
--cc=git@vger$(echo .)kernel.org \
--cc=jltobler@gmail$(echo .)com \
--cc=toon@iotcl$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox