fix(sequencer): clear orphaned state file after git rebase --abort#737
Conversation
When `av sync` or `av restack` hits a rebase conflict, sequencer state is
persisted to `.git/av/stack-{sync-v2,restack}.state.json` so the operation
can resume. If the user runs `git rebase --abort` directly (instead of
`av {sync,restack} --abort`), the rebase ends but the av state file is
left behind. Subsequent `av {sync,restack}` invocations honor the orphaned
state — re-using its cached `CurrentSyncRef` and `BranchingPointCommitHash`
— and replay the same stale rebase, producing spurious conflicts.
Add `Repo.IsRebaseInProgress()` (checks `.git/rebase-merge/` and
`.git/rebase-apply/`) and use it in the `readState` paths of both
`av restack` and `av sync`. If a state file is loaded but no rebase is
actually in progress, treat the state as orphaned, clear it, and fall
through to fresh planning from `av.db`.
Includes an e2e regression test (`restack_external_rebase_abort.txtar`)
that fails on master and passes with this fix.
Closes #734
Current Aviator status
This PR was merged using Aviator (commit abe5de4).
See the real-time status of this PR on the
Aviator webapp.
Use the Aviator Chrome Extension
to see the status of your PR within GitHub.
|
✅ FlexReview StatusCommon Owner:
Review SLO: |
There was a problem hiding this comment.
Code Review
This pull request introduces a mechanism to detect and clear orphaned state files when a Git rebase is aborted externally (e.g., via git rebase --abort). It adds an IsRebaseInProgress method to the Repo struct and updates the readState functions in restack.go and sync.go to verify the rebase status before proceeding. An end-to-end test is included to verify the fix. I have no feedback to provide.
| } else if err != nil { | ||
| return nil, err | ||
| } | ||
| if !vm.repo.IsRebaseInProgress() { |
There was a problem hiding this comment.
we should do some check in av reoder as well.
`av reorder` persists state to `.git/av/stack-reorder.state.json` when a cherry-pick conflict interrupts the reorder. If the user runs `git cherry-pick --abort` directly (instead of `av reorder --abort`), the cherry-pick ends but the av state file is left behind, and subsequent `av reorder` invocations either error with "reorder already in progress" or attempt to operate on stale state. Apply the same orphan-detection pattern as restack/sync: if the state file loaded but no cherry-pick is in progress, treat the state as orphaned, clear it, and either error cleanly (`--continue` / `--abort`) or fall through to a fresh reorder. Adds `Repo.IsCherryPickInProgress()` for symmetry with `IsRebaseInProgress()`. Per @jainankit's review on #737.
Summary
When
av sync/av restackhits a rebase conflict, sequencer state is persisted to.git/av/stack-{sync-v2,restack}.state.jsonso the operation can resume. If the user runsgit rebase --abortdirectly (instead ofav {sync,restack} --abort), the rebase ends but the av state file is left behind. Subsequentav {sync,restack}invocations honor the orphaned state — re-using its cachedCurrentSyncRefandBranchingPointCommitHash— and replay the same stale rebase, producing spurious conflicts.Fix
Repo.IsRebaseInProgress()ininternal/git/git.go— checks for.git/rebase-merge/or.git/rebase-apply/directories (both removed bygit rebase --abort).cmd/av/restack.goandcmd/av/sync.go, after loading a state file inreadState(), verify a rebase is actually in progress. If not, treat the state as orphaned, clear the file, and returnnilso the caller plans fresh fromav.db.cmd/av/reparent.gois unchanged — it already always creates fresh state and doesn't read the file.This corresponds to option (1) from the design sketch in #734 — the most conservative fix targeting the exact trigger.
Reproduction
e2e_tests/testdata/script/restack_external_rebase_abort.txtarreproduces the bug:main → stack-1 → stack-2.stack-1.av restackfromstack-2→ conflicts, persists state file.git rebase --abort→ REBASE_HEAD cleared, av state file orphaned.git checkout stack-1.av restack --current(no-op forstack-1as it has no children to restack).CurrentSyncRef=stack-2is reused; same conflict reproduced. Test fails.--currentplan runs and reports done. Test passes.Verified locally: test fails on master, passes with the fix.
Test plan
go build ./...go tool golangci-lint run(0 issues)go test ./e2e_tests/(all pass)go test ./internal/git/... ./internal/sequencer/... ./cmd/av/...(all pass)Closes #734