Skip to content

feat: gw cleanup won't delete a base branch open child PRs still target - #49

Merged
zawakin merged 1 commit into
mainfrom
feature/cleanup-child-pr-guard
Jun 29, 2026
Merged

feat: gw cleanup won't delete a base branch open child PRs still target#49
zawakin merged 1 commit into
mainfrom
feature/cleanup-child-pr-guard

Conversation

@zawakin

@zawakin zawakin commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

What

Phase 4-B / Guard B: gw cleanup no longer deletes a remote branch that open child PRs still use as their base.

Deleting a branch that is the base of an open PR makes GitHub close that PR. In the stacked flow, gw await <base-pr> runs gw cleanup the instant the base merges — exactly when a child PR is most likely still targeting it — so the destructive timing is the default, not a corner case.

Before deleting a merged branch's remote, cleanup now lists open PRs whose base is that branch (gh pr list --base <branch> --state open):

  • Children exist → skip only the remote deletion (local branch delete + home switch still happen), warn, and point the user at gw sync to restack each child first.
  • Query errors → skip conservatively too (don't risk silently closing a child PR), consistent with cleanup's existing "refuse when safety can't be confirmed" ethos.

Net effect: the base branch stays alive until its children are restacked — which is also what the upcoming gw sync --onto <base> fix needs.

Changes

  • github: open_prs_with_base + parse_pr_list_json (with unit tests for both list parsing and the client query).
  • cleanup: remote_deletion_blocked_by_children guard inserted before the merged-branch remote deletion.

Test

  • parser: parse_pr_list_json (array + empty).
  • github: open_prs_with_base lists children / empty (mock executor).
  • mise run verify passes. (cleanup's wiring is exercised only when a merged PR is confirmed, which the file-remote integration tests don't hit, so the guard logic is covered at the client/parser unit layer.)

Sequence

This is step 2 of the stacked-restack package. Next: step 3 — gw sync uses git rebase --onto origin/main <old-base> (now safe because this PR keeps the base alive); step 4 — record base SHA at gw new --stack for the pre-PR fallback.

🤖 Generated with Claude Code

Deleting a branch that is the base of an open PR makes GitHub close that PR.
In the stacked flow, `gw await <base-pr>` fires `gw cleanup` the moment the
base merges — exactly when a child PR is most likely still targeting it — so
the destructive timing is the default, not a corner case.

Before deleting a merged branch's remote, cleanup now lists open PRs whose base
is that branch (gh pr list --base). If any exist it skips ONLY the remote
deletion (local cleanup and home switch still happen) and tells the user to
`gw sync` the children first. On a query error it skips conservatively too.
This keeps the base branch alive so child `gw sync` can rebase --onto it.

- github: open_prs_with_base + parse_pr_list_json (+ unit tests)
- cleanup: remote_deletion_blocked_by_children guard

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zawakin zawakin self-assigned this Jun 29, 2026
@zawakin
zawakin merged commit 4942112 into main Jun 29, 2026
6 checks passed
@zawakin
zawakin deleted the feature/cleanup-child-pr-guard branch June 29, 2026 13:01
zawakin added a commit that referenced this pull request Jun 29, 2026
gw sync ran `git rebase origin/main`, which on a stacked branch re-applies the
base PR's commits too — doubled and conflict-prone once the base was
squash-merged. Use `git rebase --onto origin/main origin/<old-base>` so only
this branch's own commits (old-base..HEAD) are replayed.

Also reorder to rebase -> update PR base -> push. Moving the PR base first
would leave GitHub showing the new base while the branch still carried the old
commits if the rebase then failed.

The old base's remote-tracking ref still points at its pre-merge tip because
cleanup now keeps a base branch alive while open child PRs target it (#49).

- git: add rebase_onto
- git tests: prove --onto replays only the child's commits after a squash merge

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
zawakin added a commit that referenced this pull request Jun 29, 2026
The pre-PR restack hint (StackedBaseMerged) used the base branch NAME as the
`git rebase --onto` boundary. But a child with no PR yet isn't protected by
the cleanup guard (#49 only sees open child PRs), so the base branch may be
deleted before the child opens its PR — leaving the name unresolvable.

gw new --stack now also records the base tip SHA (branch.<child>.gwBaseSha),
which equals the fork point since --stack branches off the current HEAD. The
StackedBaseMerged hint prefers that SHA as the `--onto` boundary, so it still
resolves after the base branch is gone. gw sync clears both keys on restack;
branch deletion clears the rest.

- git: branch_base_sha / set_branch_base_sha; unset_branch_base clears both keys
- new: record gwBaseSha alongside gwBase
- next_action/status: thread recorded_base_sha into StackedBaseMerged's hint

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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