Skip to content

refactor: unify the paused-rebase classifier (RestackBranch + Onto)#91

Merged
andyrewlee merged 1 commit into
mainfrom
refactor/unify-rebase-classifier
Jun 21, 2026
Merged

refactor: unify the paused-rebase classifier (RestackBranch + Onto)#91
andyrewlee merged 1 commit into
mainfrom
refactor/unify-rebase-classifier

Conversation

@andyrewlee

Copy link
Copy Markdown
Owner

What

RestackBranch (restack.go) and Onto (engine.go) each contained their own copy of the "a RebaseOnto failed — is it a paused conflict, a rebase-state-probe failure, or an outright failure?" logic. This is the most safety-critical error path in the engine, so two drifting copies are exactly what you don't want.

Extracts the classification into one helper:

func rebaseFailure(g Git, rebaseErr error, action, branch, onto string) (paused bool, nonConflictErr error)

Each caller keeps its own bookkeeping around it:

  • Onto still records the PendingReparent and aborts the rebase if the save fails — byte-for-byte unchanged.
  • RestackBranch still restores HEAD on a non-conflict failure.

Review notes — the one behavioural delta

In RestackBranch, when git's rebase-state probe itself fails and the subsequent HEAD restore also fails, the wrapped error is now the more-informative "checking rebase state … failed (original error: …)" instead of the bare rebase error. Reaching this corner requires RebaseInProgress() to return an error, which the fake git never does, so no test exercises it; the new message is strictly more complete. Every other path (including all conflict/continue/abort flows) is identical.

Verification

Engine unit tests, the model/invariant test (thousands of random op sequences including injected conflicts), e2e, and a manual real-git smoke test of both a restack conflict and an onto conflict (both exit 2; st continue promotes the pending reparent correctly). make ci green — lint 0 issues, coverage 86.4%.

… Onto

Both RestackBranch and Onto open with the same "rebase failed -> is it a
paused conflict, a state-probe failure, or an outright failure?" logic.
Extract it into rebaseFailure(g, rebaseErr, action, branch, onto), so the
definition of "did the rebase pause on a conflict?" lives in one place and
the two cannot drift.

Each caller keeps its own bookkeeping around the shared classification:
Onto still records the PendingReparent (and aborts on a save failure);
RestackBranch still restores HEAD on a non-conflict failure. Onto's
behaviour is byte-for-byte unchanged.

The only behavioural delta: in RestackBranch, when the rebase-state probe
itself fails AND the subsequent HEAD restore also fails, the wrapped error
is now the (more informative) "checking rebase state ... failed" message
rather than the bare rebase error. This corner requires git's rebase-state
probe to fail, which no test can reach with the fake git; the new error is
strictly more complete.

Verified: engine + model/invariant (random op sequences incl. conflicts) +
e2e + a manual restack/onto-conflict smoke test (exit 2, st continue
promotes the reparent). make ci green, coverage 86.4%.
@andyrewlee andyrewlee merged commit 1f59b14 into main Jun 21, 2026
4 checks passed
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