Skip to main content
Prerequisite: You should be comfortable with Git Conventions and with creating worktrees, since every PR in a stack gets its own.

TL;DR

A stack is a chain of pull requests where each one targets the branch below it instead of main. Use it when a change is too large to review in one sitting, or when follow-up work builds on a branch that has not merged yet. GitHub has a first-class feature for this, currently in public preview. It tracks the chain as a unit, and merging one pull request rebases and retargets the rest. Use it when you can:
Everything still merges bottom-up — that is GitHub’s rule, not a convention: “pull requests must merge from the bottom up.”
Merging the top pull request merges the whole stack: “Every pull request below it comes with it.” If you only mean to land the bottom one, merge the bottom one.

Native stacks vs. base chaining

Two things get called “stacked PRs”, and they behave differently on merge. Native stacks — created with gh stack or the web UI. GitHub knows the chain exists. Merging a mid-stack pull request merges everything below it, and “the pull requests above stay open and automatically re-target the stack’s base branch.” All three merge methods work: “Stacks support merge commit, squash, and rebase merge methods, and they are merge-queue aware.” Base chaining — plain gh pr create --base <parent-branch>, with no stack object behind it. This is what you get by default, and what the rest of this page’s manual steps describe. It mostly works, but the merge-time guarantees above do not apply to it; see Base chaining without a native stack. Limits on the native feature, both from GitHub: it “is in public preview and subject to change”, and it “require[s] all branches to be in the same repository. Cross-fork stacks are not supported.” It is also “not supported in GitHub Desktop”, so a contributor working from a fork is on the base-chaining path whether they want to be or not.

When to stack

Stack when the parts are genuinely dependent:
  • A large feature that splits into reviewable slices, where slice 2 imports code slice 1 introduces.
  • Follow-up work on a branch that is still waiting for review — stacking lets you keep building instead of idling or piling more into a PR someone has already started reviewing.
  • A refactor that must land before the change that depends on it, where you want them reviewed separately but merged together.
Do not stack when the parts are independent. Two unrelated fixes should be two PRs off main: they can merge in any order, neither blocks the other, and neither needs a rebase when the other lands. A stack buys ordering at the cost of rebasing, so only pay it when you need the ordering.

Creating a stack

Each PR gets its own worktree, per the repo-wide rule that PR work never switches the shared checkout:
Then open each PR against the branch below it. The first targets main; every later one targets its parent:
Basing the child on the parent is what keeps the diff honest: GitHub diffs against the merge base, so the child PR shows only its own commits rather than re-displaying everything in the parent.

Say where each PR sits

Reviewers cannot see the stack from the PR page alone. Put the position and the parent in the body of every PR above the base:

Keeping each PR green

Every PR in the stack must pass on its own. A child that only compiles once its parent is merged is not reviewable, and CI will say so. Run the usual gates in the child’s worktree, not just at the top of the stack:
bun check does not compile Next apps, so a slice that touches routes, pages or dependencies also needs the affected app’s real build before it can be called verified.
Agents must not run bun run build on their own — the repo-wide prohibition on long-running build commands applies inside a stack too. Report that the build is required and ask; run it only once the user says so.

Merging the stack

Merge bottom-up, always. GitHub states it as a requirement, not a preference: “pull requests must merge from the bottom up.” Never merge a pull request whose parent is still open and unmerged — that pulls the parent’s unreviewed commits into main through the child. How much lands depends on which one you merge:
“Merge the entire stack at once by merging the top pull request. Every pull request below it comes with it.” If you meant to land one slice and clicked merge on the top of the stack, you have merged all of it.
On a native stack that is the whole procedure. The merge method is free — “Stacks support merge commit, squash, and rebase merge methods, and they are merge-queue aware” — and the branches above are rebased and retargeted for you.

Base chaining without a native stack

A chain built with gh pr create --base has no stack object behind it, so none of the guarantees above apply. Two extra rules: Merge the parent with a merge commit.
Not --squash and not --rebase. Both rewrite the parent’s commits into new ones, so the originals never enter main’s ancestry — and the child, once retargeted, shows the parent’s changes all over again as if they had never landed. A merge commit keeps them reachable from main, which is what makes the child’s diff shrink to its own work. If a parent has already been squash-merged, rebase the child onto main, drop the duplicated commits, and re-run its gates. Check the retargeting actually happened. When a merged parent’s branch is deleted through the pull-request flow — the merge button, or the repository’s automatic head-branch deletion — GitHub retargets any open PR that had it as a base onto the parent’s base.
Do not assume it did. Deleting the branch another way — gh or git push origin --delete — can close the dependent pull requests instead of retargeting them, and a closed PR whose base branch is gone is awkward to reopen. Closing a parent without merging does not delete its branch at all, so its children keep pointing at a live but abandoned base.
Then re-run the child’s gates: its merge base moved, so its diff is no longer the one that was last verified.

Either way

Each pull request needs its own approving review from a code owner and its own resolved review threads — the repository ruleset applies per PR, not per stack. Follow the normal merge closeout (quiet window, main-green verification, bun git-sync, production verification) for each one that lands.

Rebasing a stack

If you rebase or amend a parent branch, every branch above it still points at the old commits and must be rebased too, from the bottom up:
Both then need a force-push. Use --force-with-lease so a push is refused if someone else moved the branch:
Force-pushing rewrites published history. It is routine for a branch only you are working on, but confirm with the owner before rewriting a branch anyone else has pulled or reviewed.
Rebasing a whole stack is the main cost of stacking, and it grows with depth. Three PRs is comfortable; much beyond that, prefer merging the lower slices sooner so the stack stays shallow.

Cleaning up

Remove a worktree and delete its local branch only after that PR is confirmed merged into main. Never remove a worktree that is dirty, blocked, unmerged, or owned by someone else.