Skip to content

fix: run the post-merge restack through the shared engine - #142

Merged
rohoswagger merged 1 commit into
mainfrom
fix/merge-restack-shared-engine
Aug 11, 2026
Merged

fix: run the post-merge restack through the shared engine#142
rohoswagger merged 1 commit into
mainfrom
fix/merge-restack-shared-engine

Conversation

@rohoswagger

Copy link
Copy Markdown
Owner

The bug

ez merge exited 1 after a successful merge:

● Merged PR #141 for `fix/needs-restack-from-git`
   aa9196e..799e282  main -> origin/main
✗ git command failed: fatal: no such branch/commit 'repair/move-checkout-track'
[exit:1 | 7.2s]

The PR was merged, the remote branch cleaned up, trunk fast-forwarded — then the post-merge restack died on an unrelated stack entry whose git branch no longer exists. An agent branching on the exit code would conclude the merge failed and retry it.

Root cause: fetch_restack_and_push_remaining is a private restack loop in merge.rs that predates the shared engine added in 0.2.29, and never got converged. It was worse in four ways:

old merge loop restack_branches_with_options
stack entry with no git branch ? on rev-parse → aborts the run skipped (if !git::branch_exists)
stale parent_head replays stored_parent_head..branch verbatim effective_old_base falls back to merge-base
conflict on one branch aborts the remaining stack isolated, reported, others continue
branch in another worktree rebased in the wrong root git -C <worktree> via the worktree guard

The fix

Delete the loop, call the shared engine. Two supporting changes:

  • Local trunk is fast-forwarded to the fetched remote tip first (update_branch_to_latest_remote, same as restack::run does). The shared engine reads local refs; the old loop rev-parsed <remote>/<trunk> inline.
  • RestackReport gains restacked_branches: Vec<String>. Merge force-pushes each branch it restacked, so a count isn't enough. Both increment sites now go through record_restacked.

Restack failures surface as exit 3 via incomplete_error — consistent with ez sync and ez restack — and only after the merge is recorded and state is saved, so a partial failure can never again make a completed merge look like it failed.

Test

fetch_restack_survives_a_stack_entry_whose_branch_is_gone_from_git reproduces the exact scenario: a stack with one live branch and one ghost entry, trunk advanced so there is real work behind the ghost. Asserts the live branch still restacks and is pushed, and that the call returns Ok.

Full suite green (485 unit + all integration suites), clippy clean.

`ez merge` kept its own restack loop, written before the shared engine landed
in 0.2.29. It was strictly worse in the ways that engine exists to fix:

- A stack entry whose git branch is gone — deleted by hand, cleaned up in
  another worktree — aborted the whole command. The shared engine skips those
  (`if !git::branch_exists(branch_name) { continue }`). This is what bit an
  actual merge: the PR merged, the remote branch was cleaned up and trunk
  fast-forwarded, then the run exited 1 on `fatal: no such branch/commit`
  for an unrelated stale entry. Merge succeeded, exit code said failure.
- It replayed `stored_parent_head..branch` directly instead of going through
  `effective_old_base`, so a stale `parent_head` resurrected foreign commits
  or conflicted for no reason.
- A conflict on any one branch aborted the rest of the stack instead of
  isolating that branch and continuing.
- It bypassed the worktree guard, so a branch checked out elsewhere was
  rebased in the wrong root rather than via `git -C <worktree>`.

Replace the loop with `restack::restack_branches_with_options`. Local trunk is
fast-forwarded to the fetched remote tip first, because the shared engine reads
local refs where the old loop rev-parsed `<remote>/<trunk>` inline. Failures
now surface as exit 3 via `incomplete_error`, consistent with sync and restack,
and only after the merge is recorded and state is saved.

`RestackReport` gains `restacked_branches` — merge force-pushes each branch it
restacked, so it needs the names, not just the count.
@rohoswagger
rohoswagger merged commit ee97733 into main Aug 11, 2026
6 checks passed
rohoswagger added a commit that referenced this pull request Aug 11, 2026
Three stale-metadata and cleanup fixes since v0.3.2 (#141, #142, #143, #144).

Also backfills the 0.3.0–0.3.2 rows in the version history table, which were
never recorded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant