Thread (28 messages) 28 messages, 5 authors, 4h ago
HOTtoday
Revisions (4)
  1. v1 [diff vs current]
  2. v2 current
  3. v3 [diff vs current]
  4. v4 [diff vs current]

[PATCH v2 0/3] Teach git-replay(1) to linearize merge commits

From: Toon Claes <hidden>
Date: 2026-06-10 14:49:26

As an alternative to dscho's patch series to replay merges[1], add
option to git-replay(1) to linearize merges. This mimics wath
git-rebase(1) does too with --no-rebase-merges (the default).

The first two patches do some refactoring. The third patch implements
the actual change. I was kindly helped by dscho to implement this
change.

The --linearize option is only added to git-replay(1) and not to
git-history(1) because in my opinion doesn't make much sense to do so,
but I'm happy to hear if anyone disagrees.

This series might conflict with Kristoffer's series to make
documentation changes[2], but should be trivial to resolve. And I don't
think there's a conflict with Patrick's series on adding "drop" to
git-history(1)[3].

dscho's series to replay merges[1] need 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]: [ref]
[2]: [ref]
[3]: [ref]

Signed-off-by: Toon Claes <redacted>
---
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

---
Johannes Schindelin (1):
      replay: offer an option to linearize the commit topology

Toon Claes (2):
      replay: refactor enum replay_mode into a bool
      replay: add helper to put entry into mapped_commits

 Documentation/git-replay.adoc |   5 ++
 builtin/replay.c              |   4 ++
 replay.c                      | 114 ++++++++++++++++++++++++------------------
 replay.h                      |   5 ++
 t/t3650-replay-basics.sh      |  26 ++++++++++
 5 files changed, 105 insertions(+), 49 deletions(-)

Range-diff versus v1:

1:  7f3bc6f425 ! 1:  0975b142e3 replay: refactor enum replay_mode into a bool
    @@ Commit message
     
          - The value `REPLAY_MODE_REVERT` is used when option `--revert` is
            passed to git-replay(1). When using this value the commits are
    -       possible in reverse order and the inverse of the changes are applied.
    +       processed in reverse order and the inverse of the changes are
    +       applied.
     
          - The value `REPLAY_MODE_PICK` is used when either option `--onto` or
    -       `--advance` is used. In both cases the commits are pocessed in normal
    -       order, and the changes are applied as-is.
    +       `--advance` is used. In both cases the commits are processed in
    +       normal order, and the changes are applied as-is.
     
         Since there are only two possible values of this enum, simplify the code
    -    by converting the enum into a bool. This avoid adding code paths that
    -    check for invalid vaues of the enum, and shortens code where the value
    +    by converting the enum into a bool. This avoids adding code paths that
    +    check for invalid values of the enum, and shortens code where the value
         is checked with a ternary operator.
     
         Signed-off-by: Toon Claes [off-list ref]
2:  0868871c78 ! 2:  db88193624 replay: add helper to put entry into mapped_commits
    @@ Commit message
         replay: add helper to put entry into mapped_commits
     
         The function replay_revisions() in replay.c is rather lengthy. Extract
    -    the logic to put commit entry into mapped_commits into a helper
    -    function.
    +    the logic to put a commit entry into mapped_commits into a helper
    +    function put_mapped_commit().
    +
    +    While at it, rename mapped_commit() to get_mapped_commit() to pair with
    +    this new function.
     
         Signed-off-by: Toon Claes [off-list ref]
     
3:  a432ae753b ! 3:  d0c220ec8e replay: offer an option to linearize the commit topology
    @@ Commit message
     
         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
    -    linearized the commit history into a sequence of the
    +    linearizes the commit history into a sequence of the
         regular (single-parent) commits.
     
    -    Add option `--linearize` to git-replay(1) do the same.
    +    Add option `--linearize` to git-replay(1) to do the same.
     
         Co-authored-by: Toon Claes [off-list ref]
    +    Signed-off-by: Johannes Schindelin [off-list ref]
    +    Signed-off-by: Toon Claes [off-list ref]
     
      ## Documentation/git-replay.adoc ##
     @@ Documentation/git-replay.adoc: incompatible with `--contained` (which is a modifier for `--onto` only).
    @@ replay.c: int replay_revisions(struct rev_info *revs,
      		const struct name_decoration *decoration;
      
     -		if (commit->parents && commit->parents->next)
    -+		if (opts->linearize && (!commit->parents || commit->parents->next))
    -+			; /* map current commit to the same as the previous commit */
    -+		else if (commit->parents && commit->parents->next)
    - 			die(_("replaying merge commits is not supported yet!"));
    -+		else {
    +-			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!"));
    ++			/*
    ++			 * When linearizing, a merge commit itself is not picked,
    ++			 * but refs that point to it might need updating.
    ++			 */
    ++		} else {
     +			struct commit *to_pick = reverse ? last_commit : onto;
     +			last_commit =
     +				pick_regular_commit(revs->repo, commit,
    @@ t/t3650-replay-basics.sh: test_expect_success '--onto with --ref rejects multipl
      	test_grep "cannot be used with multiple revision ranges" err
      '
      
    -+test_expect_success 'linearize the commit topology' '
    -+	test_tick &&
    -+	N=$(git commit-tree -m N -p L -p I L:) &&
    -+	N=$(git commit-tree -m N-child -p $N L:) &&
    -+	git update-ref refs/heads/N $N &&
    ++test_expect_success 'replay merge commit fails' '
    ++	echo "fatal: replaying merge commits is not supported yet!" >expect &&
    ++	test_must_fail git replay --ref-action=print --onto main I..P 2>actual &&
    ++	test_cmp expect actual
    ++'
    ++
    ++test_expect_success 'replay to rebase merge commit with --linearize' '
    ++	git replay --ref-action=print --linearize --onto main I..topic-with-merge >result &&
    ++
    ++	test_line_count = 1 result &&
    ++
    ++	git log --format=%s $(cut -f 3 -d " " result) >actual &&
    ++	test_write_lines O N J M L B A >expect &&
    ++	test_cmp expect actual
    ++'
     +
    -+	git replay --ref-action=print --linearize \
    -+		--onto A B..refs/heads/N >out &&
    ++test_expect_success 'replay to rebase merge commit with --linearize down to root commit' '
    ++	git replay --ref-action=print --linearize --onto main A..topic-with-merge >result &&
     +
    -+	test_line_count = 1 out &&
    -+	read N1 N2 N3 N4 <out &&
    ++	test_line_count = 1 result &&
     +
    -+	cat >expect <<-EOF &&
    -+	* N-child
    -+	* I
    -+	* L
    -+	o A
    -+	EOF
    -+	git log --format=%s --graph --boundary A...$N3 >actual &&
    ++	git log --format=%s $(cut -f 3 -d " " result) >actual &&
    ++	test_write_lines O N J I M L B A >expect &&
     +	test_cmp expect actual
     +'
     +


---
base-commit: 9ac3f193c05c2237e2b14ebaa1149e9fc8a1abe0
change-id: 20260604-toon-git-replay-drop-merges-807fa008d395
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help