Skip to content

feat: ez move restructures the GitHub native stack it moves within - #155

Merged
rohoswagger merged 1 commit into
mainfrom
feat/move-native-stack
Aug 12, 2026
Merged

feat: ez move restructures the GitHub native stack it moves within#155
rohoswagger merged 1 commit into
mainfrom
feat/move-native-stack

Conversation

@rohoswagger

Copy link
Copy Markdown
Owner

Closes the gap reported from a real stack: ez move --onto <parent> rebased locally and updated ez's metadata, then called update_pr_base, which GitHub rejected with "Cannot change the base branch because the pull request is part of a stack". The failure became a ui::warn, the command still printed Moved, and the receipt carried no remote status at all. Local and ez topology were right; GitHub's was stale, and nothing said so. Recovering meant gh stack unstack + gh stack link by hand.

What changed

Inserting a PR into the middle of an existing native stack is only supported by dissolving and recreating the stack — and ez already knows how, via repair_native_stack_exact behind ez sync --repair-native-stack. ez move now performs that restructure itself.

Detection before mutation. detect_native_stack looks the PR's stack up before anything local is touched, so the decision is made with full knowledge rather than discovered from a failed edit after the fact. A lookup error is non-fatal — ez move is primarily a local operation and shouldn't refuse to work because GitHub is unreachable.

No more doomed base edit. A PR in a native stack skips update_pr_base entirely; the restructure is what moves the base. That removes the misleading warning at its source.

Scoped to moves that involve a stack. The restructure runs only when the branch was actually in one. An early version reconciled every chain in the repo unconditionally, which made ez move fail on repos and GitHub installations with nothing to do with the move — caught by move_force_allows_descendant_merge_linearization.

Receipts split local from remote, which was the specific agent-facing complaint:

{"cmd":"move","branch":"feat/x","from":"main","onto":"feat/base",
 "local":"moved","restacked":1,
 "pr_base":"deferred_to_native_stack",
 "native_stack_before":{"number":88,"size":4}}

pr_base is one of updated, deferred_to_native_stack, {"status":"failed","detail":"..."}, or null when there's no PR. The restructure emits its own receipts under cmd: "move", and a restructure failure exits 2 after the local receipt — so an agent can always tell exactly how far the move got.

Refactor

Sync's native-stack reconciliation moved to cmd/native_stack::reconcile_stacks(state, cmd, retry_command, repair), so ez sync and ez move restructure identically instead of drifting apart the way merge's restack loop did.

Cost

One repo view round-trip per move on a branch with a PR — the probe that makes detection-before-mutation possible. Pinned in move_warns_when_pr_base_update_fails_but_persists_local_move.

493 unit tests + all 25 integration suites green, clippy clean.

`ez move --onto <parent>` rebased locally, updated ez's metadata, then called
`update_pr_base`. For a PR inside a GitHub native stack that call is rejected —
"Cannot change the base branch because the pull request is part of a stack" —
and the failure was swallowed into a warning while the command still printed
"Moved" and emitted a receipt with no remote status at all. Local and ez
topology were correct; GitHub's was stale, and nothing said so.

Inserting a PR into the middle of an existing native stack is only supported by
dissolving and recreating the stack. ez already knows how — `ez sync
--repair-native-stack` does exactly that via `repair_native_stack_exact` — so
`ez move` now does it too.

- Detection happens before any local mutation: `detect_native_stack` looks the
  PR's stack up first, so the decision to restructure is made with full
  knowledge rather than discovered from a failed edit afterwards.
- A PR in a native stack skips `update_pr_base` entirely. That edit was never
  going to succeed, and the restructure is what moves the base.
- The restructure runs only when the branch was actually in a stack. A move
  that involves no native stack has no business reconciling every other chain
  in the repo, and doing so would make `ez move` fail on installations that
  have nothing to do with this move.
- Receipts split local from remote. The local receipt reports `local: "moved"`,
  `restacked`, `pr_base` (`updated` | `deferred_to_native_stack` | `failed`
  with the reason | null), and the stack the branch came from. The restructure
  emits its own receipts under `cmd: "move"`, and a failure exits 2 *after*
  the local receipt, so an agent can tell exactly how far the move got.

Sync's native-stack reconciliation moves to `cmd/native_stack::reconcile_stacks`
so both commands restructure identically instead of diverging.

Costs one `repo view` round-trip per move on a branch with a PR — the probe
that makes the detection possible.
@rohoswagger
rohoswagger merged commit 20cd444 into main Aug 12, 2026
6 checks passed
rohoswagger added a commit that referenced this pull request Aug 12, 2026
Two user-visible features since v0.3.3 (#155, #156).
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