Gh stacked pr merge order
Merge stacked pull requests (chained PRs whose base is the previous PR's head branch) with the gh CLI without GitHub auto-closing the stack. Use on symptoms like: a child PR auto-closed when its base branch was deleted, "Cannot change the base branch of a closed pull request", "gh pr reopen" failing on an auto-closed PR, "gh pr merge --delete-branch" closing descendant PRs, retargeting children with "gh pr edit --base", a silent merge failure on a branch-protected repo followed by branch deletion auto-closing the PR, or a stacked docs PR polluted with feature commits. Covers the safe merge order (retarget before deleting base branches), a retarget-first bulk landing alternative, MERGED-state confirmation before branch deletion, supersedes-PR recovery, and cherry-pick stack splitting.From its SKILL.md
npx -y skills add h8nc4y/gh-stacked-pr-merge-orderAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.9 KB, ~3.0k tokens by cl100k_base, as published. Nobody here has run it
gh Stacked PR Merge Order
Procedure for landing a stack of pull requests — #A ← #B ← #C, where each child PR's base is the previous PR's head branch — with the GitHub CLI, without GitHub auto-closing part of the stack. The core hazard: deleting a base branch through the gh CLI or API can close every descendant PR without retargeting it, and a PR closed that way can be neither retargeted nor reopened. Prevention is everything.
When To Use
- You merged the bottom PR of a stack with
gh pr merge --delete-branchand the child PR closed itself the moment the branch vanished. gh pr edit <n> --base <default>fails withCannot change the base branch of a closed pull request.gh pr reopen <n>fails on a PR that GitHub closed automatically (for exampleCould not open the pull request).- You are about to integrate a chain of PRs with the gh CLI and want the order that avoids all of the above.
- On a branch-protected repository, a merge "ran", but after a branch deletion the PR turned out closed instead of merged.
- A stacked documentation PR turns out to contain unrelated feature commits.
Why It Happens
How the base branch dies determines what happens to the PRs based on it (observed as of July 2026 — GitHub may change this behavior):
- Deleting the branch from the web UI (the post-merge "Delete branch" button) retargets descendant PRs onto the repository's default branch automatically.
- Deleting the branch through the gh CLI or the API — including
gh pr merge --delete-branch— has been observed to close descendant PRs without retargeting them.
Once a PR is auto-closed this way, it is unrecoverable in place (field-tested):
gh pr edit <n> --base <default>is refused:Cannot change the base branch of a closed pull request.gh pr reopen <n>is refused as well.
So the discipline is preventive: never let a branch disappear while any open PR still uses it as base.
The Safe Merge Order
For a stack #A ← #B ← #C (#A's base is the default branch, #B's base is
#A's head branch, #C's base is #B's head branch). Get <default> from
gh repo view <owner>/<name> --json defaultBranchRef -q .defaultBranchRef.name instead of assuming main.
Also check the repository's auto-delete setting before the first merge:
gh repo view <owner>/<name> --json deleteBranchOnMerge
If deleteBranchOnMerge is true, the server deletes each head branch
as its PR merges — even without --delete-branch — so the step-by-step
order below would lose a base branch at the very first merge. In that
case retarget every descendant onto the default branch before merging
anything (the bulk-landing order below), or temporarily disable the
setting for the integration. What GitHub does to descendant PRs when
auto-delete removes their base is untested here — do not rely on it.
-
Merge the bottom PR without
--delete-branch:gh pr merge <A> --repo <owner>/<name> --mergeUse your repository's merge mode (
--merge,--squash, or--rebase); what matters here is the absent--delete-branch. -
Confirm the merge actually happened before mutating anything else:
gh pr view <A> --repo <owner>/<name> --json state,mergedAtRequire
"state": "MERGED". Do not skip this on branch-protected repositories (see the timing trap below). -
Retarget the next PR onto the default branch:
gh pr edit <B> --repo <owner>/<name> --base <default> -
Check mergeability after the retarget:
gh pr view <B> --repo <owner>/<name> --json mergeable,mergeStateStatusImmediately after a retarget,
mergeablemay reportUNKNOWNwhile GitHub recomputes it asynchronously (observed as of July 2026); re-poll until it settles toMERGEABLEorCONFLICTING.If
mergeableisCONFLICTING, resolve on the head branch locally:git fetch origin git switch <B-head-branch> git merge origin/<default> # resolve conflicts, then commit git push origin <B-head-branch> -
Merge #B (again without
--delete-branch), confirmMERGED, and repeat retarget → verify → merge for #C and any further descendants. -
Delete head branches only after every PR in the stack reports
MERGED, and check each branch for remaining open PRs first — both as a base and as a head (the head check catches a straggler PR the automatic MERGED detection missed):gh pr list --repo <owner>/<name> --base <branch> --state open # must be empty gh pr list --repo <owner>/<name> --head <branch> --state open # must be empty git push origin --delete <A-head> <B-head> <C-head>
Rules of thumb:
--delete-branchis banned inside a stack. It is fine on an isolated, single PR whose head branch no other PR uses as base.- One mutation at a time: after every merge, verify state before the next retarget or deletion.
Alternative: Retarget-First Bulk Landing
A field-tested variant that lands the whole stack with a single merge:
- Retarget every open PR in the stack onto the default branch first,
bottom-up:
gh pr edit <n> --repo <owner>/<name> --base <default>. - Merge only the tip PR (the one whose head branch contains all commits
in the stack) — without
--delete-branch. - GitHub detects that the other PRs' head commits are now contained in
their base and marks those PRs
MERGEDautomatically (observed as of July 2026).
When to prefer it: a purely additive stack (each PR's head strictly contains its ancestors' commits) where one CI run and one merge beat N of them.
Caveats:
- Retargeting changes each intermediate PR's visible diff to the cumulative diff against the default branch; reviewers subscribed to those PRs see the shape change.
- The automatic
MERGEDdetection requires the stack's commits to become ancestors of the default branch, so merge the tip with a merge commit (--merge). After--squashor--rebasethe original commits never become ancestors, so do not expect the contained PRs to be detected as merged — derived from git semantics, not exercised (untested). - Interaction with merge queues: untested.
If A PR Was Auto-Closed
First identify what was lost: a PR reporting "state": "CLOSED" with
"mergedAt": null was closed without its content merging. Reopening is
not possible; supersede instead.
Which branch died depends on how you got here. After a stack-internal branch deletion, the closed PR usually lost its base branch and its own head branch survives. After the branch-protection trap below, the deleted branch was the PR's own head. Verify before creating anything:
git ls-remote --heads origin <head-branch> # must print a ref
If the head branch is gone too, push it back first from a local clone or reflog. Then create the supersedes PR:
gh pr create --repo <owner>/<name> --head <same-head-branch> --base <default> \
--title "<original title>" \
--body "Supersedes #<closed-number>.
<original body>"
- Write
Supersedes #<closed-number>in the body so the history stays traceable from the closed PR's timeline. - Review approvals do not carry over; re-request them if the repository requires reviews.
- Land the new PR with the safe order above.
Branch-Protection Timing Trap
On a repository with branch protection (required status checks), a merge attempted before the checks complete does not happen — and in scripted or loop contexts that refusal is easy to miss. The dangerous sequence, observed in the field: merge attempt (refused, unnoticed) → branch deletion → the still-open PR is auto-closed.
The rule: merge → confirm MERGED → only then delete, every time,
including inside loops:
gh pr merge <n> --repo <owner>/<name> --merge
gh pr view <n> --repo <owner>/<name> --json state,mergedAt # require MERGED
# delete the head branch only after this, and only once the stack is fully landed
On protected repositories, either wait for checks with bounded polling
(gh pr checks <n> --repo <owner>/<name> --watch=false, repeated with a
capped attempt count), or queue the merge with gh pr merge --auto, which
merges only after requirements are met. Never combine --auto with
--delete-branch inside a stack — the deferred merge would delete a base
branch out from under the descendants at an unattended moment.
If the trap already fired, the PR is closed with its content unmerged: recover with a supersedes PR exactly as above.
Splitting Mixed Stacks
When a stacked documentation or refactor PR turns out to contain unrelated feature commits, do not merge the mix and do not rewrite the stack in place. Split it (field-tested in that direction; the reverse case — a feature PR polluted with docs commits — is the same procedure, untested):
- Branch fresh from the default branch.
git cherry-pickonly the commits that belong.- Open a new PR from that branch.
- Close the old PR with a comment noting
Superseded by #<new-number>.
The stray commits stay on the old head branch, unmerged, until their own properly-scoped PR picks them up.
State-Snapshot Document Conflicts
Documents that snapshot mutable state — handoff notes, task backlogs, status boards — tend to be edited in every layer of a stack, so they conflict at almost every retarget or merge step.
- Resolve mechanically by taking the newer side (the descendant's version).
- Do not hand-craft an intermediate state mid-stack. Land the stack, then write one final docs-only PR that syncs the snapshot document to post-integration reality.
Do Not / Stop Conditions
- Never delete a branch while
gh pr list --base <branch> --state openorgh pr list --head <branch> --state openreports any PR. - Never use
--delete-branch— including with--auto— inside a stack. - Do not retry
gh pr edit --baseorgh pr reopenagainst an auto-closed PR expecting a different outcome; go straight to the supersedes recovery. - Do not resolve state-snapshot conflicts by inventing an intermediate state mid-stack; take the newer side and sync at the end.
- If the same failure class does not improve after three attempts, stop and report. Cost, secret, and credential stop conditions always take precedence.
Completion Checklist
- Every PR in the stack reports
MERGED:gh pr view <n> --repo <owner>/<name> --json state,mergedAt. - No open PR still uses any branch about to be deleted, as base or head:
gh pr list --repo <owner>/<name> --base <branch> --state openandgh pr list --repo <owner>/<name> --head <branch> --state openare both empty. - Head branches deleted only after the two checks above, in one final sweep.
- Every superseded or auto-closed PR has a successor PR whose body links
it (
Supersedes #<n>). - Snapshot documents synced by a final docs-only PR if they conflicted during the stack.
Portability
Every command above is git or gh and runs unchanged in POSIX shells
and PowerShell. The multi-line --body example uses POSIX line
continuation; in PowerShell, pass --body-file <file> instead of
embedding newlines.
Reporting
- Start reports with a timestamp (date and time, in a stated timezone).
- Include: the PRs landed in order with their final states, conflicts hit
and how they were resolved, branches deleted with the pre-deletion
evidence (
MERGEDstates and the empty dependent-PR listing), any supersedes recoveries performed, and open unknowns. - Do not assert platform behavior you did not observe; mark it unverified.
Provenance
Distilled from repeated real agent operations landing multiple stacked-PR sets across multiple repositories, including live incident recoveries — every rule above traces to an observed failure or a verified recovery. GitHub-side behaviors (CLI/API deletion closing descendants without retargeting, web-UI deletion retargeting them, automatic MERGED detection after a bulk landing) are observations as of July 2026, not documented contracts; re-verify before depending on them, and expect them to change without notice. Items marked untested — the bulk-landing behavior under squash/rebase modes and any merge-queue interaction — have not been exercised.
What ships with it: 20 files
398.0 KB alongside SKILL.md, 5 of them executable
docs/
- SCANNER-HARDENING.md8.8 KB
- SKILL.ja.md14.7 KB
- VERIFICATION.md1.9 KB
examples/
scripts/
- private-marker-process.ps1runs46.7 KB
- private-marker-windows-process.ps1runs33.3 KB
- scan-private-markers.ps1runs55.1 KB
- test-scan-private-markers.ps1runs177.1 KB
- validate-oss-readiness.ps1runs20.3 KB
- CHANGELOG.md5.7 KB
- CODE_OF_CONDUCT.md1.0 KB
- CONTRIBUTING.md3.4 KB
- .editorconfig370 B
- .gitattributes91 B
- .gitignore155 B
- LICENSE1.0 KB
- README.md15.4 KB
- SECURITY.md3.6 KB