You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The headline benefit of git rebase --update-refs is that one rebase from the leaf of a stack updates every intermediate branch ref in one operation. gh-stack today does the opposite: it walks the tree parent-first and runs one rebase per branch.
For a linear stack of N branches, this means we go from N subprocess invocations + N possible conflict interruption points down to 1 + 1.
Proposal
Refactor cmd/restack.go to walk leaves and do one git rebase --update-refs per chain, instead of the current per-branch loop.
Scope
Identify leaves in the tree, not just descendants.
For each leaf, find the path from trunk and run one rebase from the leaf's tip.
After the rebase, refresh stackForkPoint for every branch in the rebased chain (currently we update fork point per branch immediately after each rebase).
Update conflict-state machinery to map .git/rebase-merge/stopped-sha back to a gh-stack branch (today's state file records Current: <branch> because we know exactly which branch is mid-rebase; with leaf-path rebase, we'll need a lookup).
Continue to suppress --update-refs when --worktrees mode is active (see investigate git rebase --update-refs #112 finding 4: silent stack corruption when intermediate refs are checked out elsewhere).
Update cmd/continue.go resume path to handle the new state shape.
E2E coverage for: linear chain, branching tree (multiple leaves), conflict on intermediate commit + continue, conflict on intermediate commit + abort, interaction with --onto fork-point flow after a squash merge upstream.
Trade-offs
Wins
Fewer subprocess invocations for linear chains.
Fewer conflict interruption points per stack.
No more "skipped previously applied commit" advisory noise when users have rebase.updateRefs = true set globally.
Costs
Per-branch progress messaging (Restacking <branch>... ok) is replaced by git's end-of-rebase summary. UX shift.
Conflict-to-branch mapping requires new lookup logic.
Per-leaf fork-point refresh becomes a post-rebase pass instead of an inline update.
Changes blast radius across cmd/restack.go, cmd/continue.go, internal/state, e2e tests.
Out of scope for this issue
The opportunistic --update-refs opt-in in the existing per-branch loop. That's the recommendation from investigate git rebase --update-refs #112 and ships with that issue's PR.
Changes to worktree dispatch beyond the existing --no-update-refs suppression.
Acceptance criteria
Restack of a linear stack runs exactly one git rebase invocation.
Restack of a branching tree runs one git rebase per leaf.
Mid-chain conflict + gh stack continue produces correct final refs.
Mid-chain conflict + gh stack abort restores all branches to pre-operation state.
--worktrees mode falls back to per-branch behavior automatically.
Background
Spun out of the investigation in #112 (see
docs/update-refs.mdfor full context).The headline benefit of
git rebase --update-refsis that one rebase from the leaf of a stack updates every intermediate branch ref in one operation. gh-stack today does the opposite: it walks the tree parent-first and runs one rebase per branch.For a linear stack of N branches, this means we go from N subprocess invocations + N possible conflict interruption points down to 1 + 1.
Proposal
Refactor
cmd/restack.goto walk leaves and do onegit rebase --update-refsper chain, instead of the current per-branch loop.Scope
stackForkPointfor every branch in the rebased chain (currently we update fork point per branch immediately after each rebase)..git/rebase-merge/stopped-shaback to a gh-stack branch (today's state file recordsCurrent: <branch>because we know exactly which branch is mid-rebase; with leaf-path rebase, we'll need a lookup).--update-refswhen--worktreesmode is active (see investigategit rebase --update-refs#112 finding 4: silent stack corruption when intermediate refs are checked out elsewhere).cmd/continue.goresume path to handle the new state shape.--ontofork-point flow after a squash merge upstream.Trade-offs
Wins
rebase.updateRefs = trueset globally.Costs
Restacking <branch>... ok) is replaced by git's end-of-rebase summary. UX shift.cmd/restack.go,cmd/continue.go,internal/state, e2e tests.Out of scope for this issue
--update-refsopt-in in the existing per-branch loop. That's the recommendation from investigategit rebase --update-refs#112 and ships with that issue's PR.--no-update-refssuppression.Acceptance criteria
git rebaseinvocation.git rebaseper leaf.gh stack continueproduces correct final refs.gh stack abortrestores all branches to pre-operation state.--worktreesmode falls back to per-branch behavior automatically.