From: Toon Claes Date: Fri, 13 Mar 2026 16:22:48 GMT Subject: Re: [PATCH RFC] git-replay: implement subcommands Message-ID: <87v7ezsnqf.fsf@iotcl.com> In-Reply-To: Justin Tobler 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 >> --- >> 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 | --advance | --revert ) [--ref-action[=]] ... > > 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 [--ref ] [--contained] >> + [--ref-action[=]] >> +git replay pick --ref [--onto ] >> + [--ref-action[=]] >> +git replay revert --ref [--onto ] >> + [--ref-action[=]] > > 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 [--ref ] [--contained] [--ref-action[=]] git replay pick [--onto ] [--ref-action[=]] git replay revert [--onto ] [--ref-action[=]] 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? > 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. -- Cheers, Toon