Re: [PATCH v11 8/8] builtin/history: implement "reword" subcommand
- From
Elijah Newren <newren@gmail.com>
- Date
- Jan 17, 2026, 22:56 UTC
- Message-ID
- <CABPp-BHkNLdH4C7U4sFoVhrsSPH8KAaDtOdLEQGyajmXZz9hVg@mail.gmail.com>
- In-Reply-To
- <aWpnFqTmWB9XIWUW@szeder.dev>
On Fri, Jan 16, 2026 at 8:28 AM SZEDER Gábor <szeder.dev@gmail.com> wrote:
Show 21 quoted lines
> > On Tue, Jan 13, 2026 at 10:54:39AM +0100, Patrick Steinhardt wrote: > > Implement a new "reword" subcommand for git-history(1). This subcommand > > is similar to the user performing an interactive rebase with a single > > commit changed to use the "reword" instruction. > > > > The "reword" subcommand is built on top of the replay subsystem > > instead of the sequencer. This leads to some major differences compared > > to git-rebase(1): > > > > - We do not check out the commit that is to be reworded and instead > > perform the operation in-memory. This has the obvious benefit of > > being significantly faster compared to git-rebase(1), but even more > > importantly it allows the user to rewrite history even if there are > > local changes in the working tree or in the index. > > In an earlier round I pointed out some of the differences between the > 'reword' instruction of 'git rebase' and 'git history rebase', > including some drawbacks of the latter. It's disheartening to see > that you only picked those differences that are in favor of your 'git > history' command, but neglected its drawbacks.
That'd be https://lore.kernel.org/git/aSVpXPtrqa0cBsEm@szeder.dev/ , right? I see two things you commented on there. Although I'm not Patrick, let me respond to each, in reverse order:
Personally, I'm having difficulty understanding your second stated advantage of picking the commit from the rebase instruction sheet. That instruction sheet is obtained by rebasing on top of something, and it seems as easy to me to see the commits since that point in a git log command and pick your desired commit from log output as it is to invoke an interactive rebase on top of that base commit to get the rebase instruction sheet and then pick out your desired commit from there. Same number of operations and work either way, so I don't see how either is more or less work than the other.
You did make a good point that one of the differences is that you don't have the commit checked out. That's a useful distinction to be aware of. To me, that seems to be somewhat implied already both by "in-memory" and "even if there are local changes in the working tree or index", though it wouldn't hurt to explicitly call it out.
> Please strive for less biased and more objective commit messages.
He rewrote the commit message from v6 to v7 (https://lore.kernel.org/git/20251027-b4-pks-history-builtin-v6-5-407dd3f57ad3@pks.im/ -> https://lore.kernel.org/git/20251203-b4-pks-history-builtin-v7-5-9e9f849bfd0e@pks.im/), as far as I can tell precisely to respond to your feedback. Perhaps he didn't achieve what you wanted, but why jump to the conclusion of "bias" rather than that he missed conveying an important distinction as clearly as you wanted?
Show 9 quoted lines
> > - We do not execute any hooks, even though we leave some room for > > changing this in the future. > > > > - By default, all local branches that contain the commit will be > > rewritten. This especially helps with workflows that use stacked > > branches. > > Please don't just state that all local branches containing the > modified commit are rewritten, but justify why it behaves that way.
It feels like each of your complaints with the new proposed commands (given commit not checked out, rebase instruction sheet vs listing commit, and HEAD-only) can be boiled down to the fact that they don't behave like `git rebase`. Is that accurate? If you like rebase, is there a reason you are worried you can't just keep using it? I don't see why others should be required to implement another exact copy of rebase, though. Further, if we only wanted minor modifications, we could have just done those to git rebase.
If you aren't just arguing to match git rebase's behavior exactly, let me try to explain the all-descendant-branches thing from my angle.
One of the issues that I've long hated about git rebase is that if you have multiple inter-dependent branches, it's a royal pain to rebase them all. You cannot rebase them independently, because that disconnects the history by duplicating the shared portions so that you have N copies of each of those. And I couldn't see a way to fix that inside git rebase; its design basically ties you to a single branch.
Further, maybe it'd be useful to explore the different proposed defaults a bit with the 'history reword' example. Let's assume we used your preferred default (rewrite only a single branch) for a user who didn't like that default. If a user does a history reword, and later realizes that their other branches didn't get updated, how do they fix the others? They might be inclined to loop over the other branches and do a 'git history reword CommitZ' on each of them, but then they'd get the nasty surprise of having N different reworded CommitZ's (even if they reworded identically), one per branch. Alternatively, if they are aware that they can't simply reuse the same command to replay the other branches, they'll then start asking questions about how exactly to fix up all the other branches...and the command(s) they need to run is going to be a lot more complicated, especially if they have since added additional commits on top of the active branch that they don't want to undo and lose. It feels like a hard recovery story. In contrast, we can consider the case of the default being to update all branches for a user who doesn't want it. If that user finds that other local branches were also rebased and they didn't want them to be, they just go reset that branch or branches from the reflog, which is pretty easy.
> Git's porcelain commands operate on the current branch, unless the > user specifies a different branch or an option like '--all' or > '--branches'. The default chosen here is inconsistent with the rest > of Git.
That's a good point that there are existing commands that default to the current branch. By my accounting, commands that operate on a range of commits are: "view a range of commits": log, and derivatives like rev-list "edit a range of commits": fast-import, filter-branch, filter-repo, rebase, replay "copy a range of commits (or copy their inverse)": cherry-pick, revert
Hopefully I didn't miss any from skimming over 'git help --all'; my apologies if so. Anyway, let's consider these commands, in reverse order:
I'm not sure the "copy a range of commits" provide much of a precedent for editing a range, since the replay equivalent was always envisioned to only update a single branch for those despite otherwise being envisioned as a rewrite-all-branches-by-default thing. But there's actually a unifying piece of logic that can lead you to the different branch handling: we want to avoid having duplicates of commits simultaneously in use and thus default to as few copies as possible. That logic means that for "copy a range", we apply them to just one branch. The same logic for "edit a range", means to rebase all descendants on any local branch.
For the "edit a range of commits", filter-branch did default to a single branch but is deeply deprecated and more or less warns you that the whole tool was a design mistake. The replacement (filter-repo) in its name makes it clear that one of the mistakes was single branch vs. all branches. fast-import is also more of an all-branch thing, though it doesn't so much have a default. git-rebase does have a single-branch precedent, but that was called out rather strongly in my design goals for its newer alternative, git-replay ("Decapitate HEAD-centric assumptions", https://lore.kernel.org/git/20230407072415.1360068-1-christian.couder@gmail.com/). If I hadn't viewed the single-branch handling in rebase as a mistake, I probably would have never created git-replay and instead just incrementally improved git-rebase. Since it was that mistake that lead to new tools in the first place, I'm not sure how much of a precedent git-rebase should be considered to be setting here.
For "viewing a range of commits", git log does lend towards your argument; it could be seen as precedent setting in favor of just-the-current-branch.
So, the precedent seems to not be uniform among git commands, though I think git log is strongly in your favor. As a really rough measure of how much that precedent matters, we can look at how folks have commented on what they think the default should be so far in this whole thread:
Rebase-all-descendant branches:
* Phillip (https://lore.kernel.org/git/91bd9241-96c1-4b34-98a9-af3bad345c4d@gmail.com/)
* Junio (https://lore.kernel.org/git/xmqqms3qh13e.fsf@gitster.g/)
* Kristoffer (https://lore.kernel.org/git/b3ddfaa4-526b-41e3-b12a-0fec846ac7bc@app.fastmail.com/)
* Ben (https://lore.kernel.org/git/3600D877-4999-4EE3-8C1C-893E12D35B6A@gmail.com/)
* me (this email, among others)
* Martin (https://lore.kernel.org/git/CANiSa6hxjghKQMhURx8qC2t=+1gEE7p8YaHbWkg3rYOYa=poVg@mail.gmail.com/)
* Patrick ("Yup" from
https://lore.kernel.org/git/aKs3tqjE510MF0T-@pks.im/, plus this v11
we're responding to, though he did seem to vacillate over the course
of the series)Rebase-current-branch-only: * You (your email that I'm responding to)
Do not allow editing a commit shared by multiple branches: * Matthias (https://lore.kernel.org/git/4m6rmefbv4hftclimitz5rp6yapswjtnjsxymrsdkuan4jbg3u@dm5jzdiq5cxz/)
(Sorry if I missed any, I tried to find them all in the threads on this topic.)
> This is a bad default for any future subcommands implementing common > history rewriting operations that can cause conflicts.
Totally disagree; we'd want rebase-all-descendant-local-branches for those commands too. In fact, that's precisely what I did with "git replay edit". (True, "git replay edit" was just a proof-of-concept because I didn't have conflict handling implemented, but it was very much an intentional default for a command known to need to deal with conflicts before being productionized).
Further, we have two semi-independent implementations of replay-all-descendant-branches-by-default in the form of JJ and GitButler, with real world use (not just demos) and apparent consensus that it not only works but was a good decision.
So, I'm a little unsure at how you arrived at this conclusion; do you care to elucidate?
> Users must remember to specify a non-default '--ref-action' if they > don't want this behavior. If they forget to do so and don't notice > it, the old commits will be gc-ed away. Therefore, I consider this > to be a dangerous default that can lead to data loss.
You elided over "for a really long time so that the reflogs expire", but you bring up a good point. I think the key here is "don't notice it". With "git replay edit", I'd print notices about what was updated after each operation (particularly important since the operation could be a `git commit --amend` or `git reset HEAD~1` or whatever, which causes commits which are the descendants of the one you are operating on to be replayed). To avoid the "don't notice it" issue, we could do the same with Patrick's history command. We could also prevent a forgotten --ref-action by allowing a config variable, which I'll cover more below.
> I firmly believe that operating on all local branches must always be > the result of an explicit user action.
Over at https://lore.kernel.org/git/aUVaEPGoOkATQGl3@szeder.dev/ , you alternatively suggested the idea of an "escape hatch". I think that may be a good idea. What if we had a "history.scope" config variable, with values like "descendant-branches", "current-branch", and "error-if-multiple-branches", corresponding to each of the requested defaults we've seen in response to this series? I can't imagine using anything other than descendant-branches, and the preponderance of those who have commented on the default so far seem to be in agreement, but it would allow you and Matthias and others like you two to pick an alternative. Thoughts?