From: Elijah Newren Date: Tue, 01 Sep 2026 22:15:20 GMT Subject: Re: [PATCH v9 0/3] Teach git-replay(1) to linearize merge commits Message-ID: In-Reply-To: <20260831-toon-git-replay-drop-merges-v9-0-61c4232c6f36@iotcl.com> On Mon, Aug 31, 2026 at 6:14 AM Toon Claes wrote: > > Hi, > > I'm back with another iteration of the patch series to implement > --linearize into git-replay(1). My apologies for the long period of > radio silence, but this topic was stalled on reviews for a while, and > when I got some feedback I was on leave, so I'm finally back. > > As far as I could tell there weren't any remaining comments on the > implementation itself, only on commit message and docs. > > Laters, > Toon > > Original cover letter: > === > > As an alternative to dscho's patch series to replay merges[1], add > an option to git-replay(1) to linearize merges. This mimics what > git-rebase(1) does with --no-rebase-merges (the default). > > The first two patches do some refactoring. The third patch implements > the actual change. The original patch was kindly provided by Dscho, > which I've tweaked to be upstreamed. > > The --linearize option is only added to git-replay(1) and not to > git-history(1) because in my opinion it doesn't make much sense to do > so, but I'm happy to hear if anyone disagrees. > > Dscho's series to replay merges[1] needs a bit of rework to fit on top > of this, but I'm happy to help figuring that out. We've been discussing > to either name the option --flatten or --linearize, but I've decided on > "linearize" because the documentation of git-rebase(1) also mentions > "linearize". > > [1]: > > --- > Changes in v9: > - Rephrase "multiple revision ranges" to "multiple branches". > - Reword some things in the commit message. > - Tweak wording in replay.adoc. > - Link to v8: https://patch.msgid.link/20260728-toon-git-replay-drop-merges-v8-0-ced11dffe749@iotcl.com > > Changes in v8: > - Disallow multiple revision ranges with --linearize. > - Disallow --contained with --linearize. > - Link to v7: https://patch.msgid.link/20260707-toon-git-replay-drop-merges-v7-0-808ab9b4afa6@iotcl.com > > Changes in v7: > - Allow --revert and --linearize to be used together. > - Because quite a lot of changes have been made since the original > patch, change author from Johannes to Toon for the last commit. > Johannes already told me he doesn't really care about authorship when > he initially shared the patch with me. > - Link to v6: https://patch.msgid.link/20260702-toon-git-replay-drop-merges-v6-0-78a07cdd0382@iotcl.com > > Changes in v6: > - Reworked the second commit that moves picking the base completely > outside pick_regular_commit(), instead of adding more explanation. > - Drastically extended the commit message on commit #3. > - Extended docs on flattening multiple revision ranges and how it's > different from git-rebase(1)'s --no-rebase-merges. > - Added a bunch of tests to cover various scenarios. > - Remove newline from BUG() message. > - Link to v5: https://patch.msgid.link/20260626-toon-git-replay-drop-merges-v5-0-5e120738b9d0@iotcl.com > > Changes in v5: > - Dropped the enum->bool patch and instead added a patch that better > explains how pick_regular_commit() picks a base. > - Order of commits is shuffled. > - (BIGGEST CHANGE) When working on a refactor to undo the enum->bool > patch, I extended the code comments to explain how things work. This > made me realize the use of the "replayed_base" was incorrect when > multiple branches are rebased with --onto. This is fixed now and a > test is added for this scenario. > - Link to v4: https://patch.msgid.link/20260622-toon-git-replay-drop-merges-v4-0-ff257f534319@iotcl.com > > Changes in v4: > - Use test_grep instead of a bare grep in the range-diff test, to > prepare for mm/test-grep-lint. > - Link to v3: https://patch.msgid.link/20260616-toon-git-replay-drop-merges-v3-0-153e9eb99ce1@iotcl.com > > Changes in v3: > - Add --linearize to Documentation SYNOPSIS, and mention it's > incompatible with --revert. > - Small language change in help message for --linearize. > - Rephrase comment to include last_commit isn't modified when > linearizing merges. > - Remove test that was added in earlier versions, but actually is > a duplicate of 'replaying merge commits is not supported yet'. > - Add test to verify --revert and --linearize are incompatible. > - Properly test that replaying down to root with --linearize works. > - Add test for --linearize with --advance. > - Add test that uses git-range-diff(1) to verify the patches created by > --linearize are correct. > - Link to v2: https://patch.msgid.link/20260610-toon-git-replay-drop-merges-v2-0-5714a71c6d83@iotcl.com > > Changes in v2: > - Restructured the conditions to detect merge commits and added a line > of comment why the loop continues. > - Rewrote tests to use the history from the setup step and added a few > test cases. > - Re-added Johannes's Signed-off-by trailer. Johannes gave me the > patches with this trailer, and if I understand correctly, I can keep > it. Please let me know if that wrong. > - Link to v1: https://patch.msgid.link/20260608-toon-git-replay-drop-merges-v1-0-e3ee71fce7b4@iotcl.com > > --- > Toon Claes (3): > replay: add helper to put entry into replayed_commits > replay: resolve the replay base outside pick_regular_commit() > replay: offer an option to linearize the commit topology > > Documentation/git-replay.adoc | 17 ++++++- > builtin/replay.c | 6 ++- > replay.c | 87 +++++++++++++++++++++++---------- > replay.h | 5 ++ > t/t3650-replay-basics.sh | 109 +++++++++++++++++++++++++++++++++++++++++- > 5 files changed, 196 insertions(+), 28 deletions(-) > > Range-diff versus v8: > > 1: 9fd3641eaf = 1: 3ea73d7c98 replay: add helper to put entry into replayed_commits > 2: 0bd4cd9b9c = 2: 4a40c42684 replay: resolve the replay base outside pick_regular_commit() > 3: 45faa926f8 ! 3: d6247ea743 replay: offer an option to linearize the commit topology > @@ Commit message > If a ref was pointing to a merge commit, that ref is updated to the > merge's last replayed ancestor. > > - git-replay(1) accepts multiple revision ranges, for example: > + git-replay(1) accepts multiple branches, for example: > > $ git replay --onto main topic1 topic2 > > Without `--linearize` this replays 'topic1' and 'topic2' onto 'main' > - independently and updates both refs. > + (keeping shared portions of history shared and divergent parts > + divergent) and updates both refs. > > - For now this is disallowed with option `--linearize`. Linearizing more > - than one branch at once would concatenate unrelated histories into a > - single line, and update each branch to some point in that line. That > - won't be the result most users want, especially because the order > - depends on the order of the revision walk, not the order of the branch > - names on the command line. > - > - For the same reason disallow the use of `--contained` with > - `--linearize`. > + Due to current implementation limitations, replaying multiple branches > + with `--linearize` is disallowed to avoid concatenating unrelated > + histories into a single line. For the same reason disallow the use of > + `--contained` with `--linearize`. > > Users who want to linearize multiple branches are advised to do this in > separate git-replay(1) invocations. Linearizing multiple branches at > @@ Documentation/git-replay.adoc: SYNOPSIS > > DESCRIPTION > ----------- > -@@ Documentation/git-replay.adoc: incompatible with `--contained` (which is a modifier for `--onto` only). > +@@ Documentation/git-replay.adoc: Expanded description list compared to 'replay.refAction'. > + > The default mode can be configured via the `replay.refAction` configuration variable. > > @@ Documentation/git-replay.adoc: incompatible with `--contained` (which is a modif > + previously replayed one, so all replayed commits are flattened into > + a single linear history. > ++ > -+When a merge commit is encountered, the behavior of git-rebase(1)'s > -+option `--no-rebase-merges` is imitated. All commits in the range > -+reachable from the merge commit are replayed into a linear history, and > -+the merge commit itself is dropped. A ref that pointed to a merge commit > -+is updated to the merge's last replayed ancestor. > ++When a merge commit is encountered, all commits in the range reachable > ++from the merge commit are replayed into the linear history, and the > ++merge commit itself is dropped. A ref that pointed to a merge commit is > ++updated to the merge's last replayed ancestor. (This matches the > ++behavior of git-rebase(1)'s `--no-rebase-merges` option.) > ++ > -+Only a single branch can be linearized at a time: `--linearize` cannot > -+be combined with multiple positive revisions or with `--contained`, > -+because that would concatenate otherwise unrelated histories into one > -+line. To linearize several branches, replay them in separate `git > -+replay` invocations. > ++`--linearize` cannot be combined with multiple branches or with > ++`--contained`. To linearize several branches, replay them in separate > ++`git replay` invocations. > + > :: > Range of commits to replay; see "Specifying Ranges" in > @@ replay.c: int replay_revisions(struct rev_info *revs, > > + if (opts->linearize && > + update_refs && strset_get_size(update_refs) > 1) { > -+ ret = error(_("'--linearize' cannot be used with multiple revision ranges")); > ++ ret = error(_("'--linearize' cannot be used with multiple branches")); > + goto out; > + } > + > @@ t/t3650-replay-basics.sh: test_expect_success '--onto with --ref rejects multipl > + test_line_count = 3 out > +' > + > -+test_expect_success '--linearize rejects multiple revision ranges' ' > ++test_expect_success '--linearize rejects multiple branches' ' > + test_must_fail git replay --ref-action=print --linearize \ > + --onto main ^B topic2 topic3 topic4 2>err && > -+ test_grep "cannot be used with multiple revision ranges" err > ++ test_grep "cannot be used with multiple branches" err > +' > + > +test_expect_success 'replay with --linearize of a divergent merge keeps both sides' ' Thanks for making all these changes! I think this version is good to go. Reviewed-by: Elijah Newren