Large pull requests are difficult to review well.
They combine too many concerns, make feedback harder to act on, and often leave reviewers deciding where to start.
GitHub Stacked PRs offers a more deliberate workflow: split one large change into a sequence of small, dependent pull requests (PRs) that can be reviewed independently.
If you need a visual demonstration, I have also made a video walkthrough of this workflow.
In this tutorial, you will learn what stacked PRs are, when to use them, and how to manage a complete stack using GitHub CLI. We will use a small authentication feature as an example, breaking it into database, API, and interface layers.
What Are GitHub Stacked PRs?
A stacked pull request is a chain of two or more PRs in the same repository. The bottom PR targets your trunk branch, usually main, and every PR above it targets the branch directly below it.
For example, an authentication feature might be organized like this:
feat/login-ui → PR #3 (base: feat/auth-api)
feat/auth-api → PR #2 (base: feat/user-model)
feat/user-model → PR #1 (base: main)
main
Each layer contains one discrete, reviewable change. A reviewer opening feat/auth-api sees the API work introduced by that layer, not the database work already included in feat/user-model.
GitHub also displays the stack relationship in the pull request interface, so the full context remains available.
This differs from simply splitting a feature into arbitrary branches. The order matters: a layer can depend on code in the same branch or a lower branch, but not on code higher in the stack. Put foundational work, such as schema changes and shared types, near the bottom; put code that consumes those foundations, such as endpoints and UI components, above it.
Why Use Stacked PRs?
Stacked PRs are useful when a change is too large for one focused review but its pieces must be developed in order. They let you open the first layer for review and immediately continue building the next layer instead of waiting for a merge.
The main benefits are:
- Smaller, more focused diffs: Reviewers can concentrate on one concern at a time.
- Earlier feedback: You can request feedback on a data model or API contract before the rest of the feature is complete.
- Clearer dependencies: The branch structure documents which work builds on which foundation.
- Safer high-volume development: This is especially helpful when coding agents generate a larger feature in several logical steps.
- Less manual Git work: GitHub can cascade rebases through a stack when lower layers change or merge.
Stacks are not the right answer for every change. Use a normal PR for a small, self-contained fix. A stack adds coordination overhead, so its layers should be meaningful on their own, not tiny fragments created only to increase the number of PRs.
Prerequisites
Before starting, make sure you have:
- A GitHub repository with a default branch such as
main. -
GitHub CLI version 2.0 or later installed and authenticated. If needed, run
gh auth login. - The GitHub Stacked PRs extension installed:
gh extension install github/gh-stack
GitHub Stacked PRs is currently in public preview, so the interface and commands may evolve. All branches in a stack must also live in the same repository; cross-fork stacks are not supported.
Create Your First Stack
We will create a three-layer stack for an authentication feature:
- A user model.
- Authentication API routes.
- A login interface.
Start from an up-to-date local copy of your default branch:
git switch main
git pull --ff-only origin main
1. Initialize the bottom layer
Run gh stack init with a branch name to create the first branch and register a stack locally:
gh stack init feat/user-model
The bottom branch is based on the repository's default branch unless you explicitly choose another base with --base. Make the database or domain-model changes on this branch, then commit them as you normally would:
git add src/models/user.ts migrations/
git commit -m "Add user model"
You can also run gh stack init without a branch name and select the first layer interactively.
2. Add a dependent API layer
Once the user-model work is committed, create a branch above it:
gh stack add feat/auth-api
This creates and checks out feat/auth-api at the current HEAD. Implement the login and session routes, then commit the work:
git add src/routes/auth.ts src/services/session.ts
git commit -m "Add authentication API"
The API branch now contains the commits from the user-model layer plus its own commits. However, its eventual PR diff will show only the API-specific changes because its base branch is feat/user-model.
3. Add the UI layer
Create the third layer while you are on the current top branch:
gh stack add feat/login-ui
Add the form and client-side integration, then commit:
git add src/components/LoginForm.tsx src/pages/login.tsx
git commit -m "Add login interface"
Use gh stack view at any point to inspect the local order, branch names, associated PR links, and recent commits:
gh stack view
Tip: Add a new branch when the concern changes or the current layer has grown large enough to make review difficult. A database migration, an API contract, and a UI implementation are usually better review units than three random groups of files.
Submit the Stack to GitHub
When the layers are ready, submit them together:
gh stack submit
This command pushes every branch, creates or updates a PR for each branch, and links the PRs as one stack on GitHub. In an interactive terminal, it opens an editor where you can review each PR title and description and choose whether it is ready for review or a draft.
For automation or a quick initial submission, use:
gh stack submit --auto
By default, the non-interactive form creates new PRs as drafts. Add --open if the new PRs should be ready for review immediately:
gh stack submit --auto --open
After submission, the stack looks like this on GitHub:
PR #1: Add user model → main
PR #2: Add authentication API → feat/user-model
PR #3: Add login interface → feat/auth-api
The PR page includes a stack map that shows the layers and their status. Reviewers can jump between the PRs without returning to the pull request list.
Review Stacked Pull Requests
The best way to review a complete stack is from bottom to top. Start with the foundational model, then review the API that depends on it, then the interface that uses that API. This mirrors the dependency order and provides the full story.
Each PR should also make sense as a focused review on its own. If reviewing one layer requires holding too much unrelated information in mind, consider restructuring the stack so that the layer becomes more atomic.
GitHub evaluates each stacked PR against the requirements of the bottom PR's base branch. In practice, this means branch protection rules and pull-request CI checks associated with main apply throughout the stack, including layers that directly target another feature branch.
Update a Stack After Feedback
Feedback often arrives on the lower layers first. For example, a reviewer might ask you to rename a field in feat/user-model after you have already built the API and UI on top of it.
Check out the layer you need to change, make the correction, and commit it:
gh stack checkout feat/user-model
# Edit the model files
git add src/models/user.ts
git commit -m "Rename user identifier field"
Then rebase the dependent branches in order:
gh stack rebase
gh stack rebase fetches from the remote and runs a cascading rebase from the trunk upward. If Git reports a conflict, resolve it, stage the resolved files, and continue:
git add <resolved-files>
gh stack rebase --continue
Finally, push the updated branches and refresh their PRs:
gh stack submit
For routine maintenance, gh stack sync is often the simplest option:
gh stack sync
It fetches updates, reconciles the local and remote stack, fast-forwards the trunk when possible, cascades rebases when needed, pushes branches, and synchronizes PR state. If a rebase rewrites branch history, the extension uses --force-with-lease when pushing, which protects against overwriting a remote update you do not have locally.
Merge a Stack
Stacked PRs merge from the bottom upward. You can merge one layer, a contiguous portion of the stack, or the whole stack:
- Merge the bottom PR to land only that PR.
- Merge a middle PR to land it and every unmerged PR below it.
- Merge the top PR to land the entire stack.
You cannot merge a middle layer while leaving an unmerged dependency below it. After a partial merge, GitHub automatically rebases and retargets the remaining upper layers so that the next unmerged PR is ready to continue through review.
GitHub supports merge commits, squash merges, and rebase merges for stacks, and the final history matches the result of merging the PRs individually from bottom to top.
Best Practices for GitHub Stacked PRs
Use these guidelines to keep stacks readable and easy to maintain:
- Make each layer independently reviewable. A layer can depend on lower layers, but its purpose should be clear in its own title, description, and diff.
- Keep the stack shallow when possible. Two to five thoughtful layers are easier to reason about than a long chain of micro-PRs.
- Order layers by dependency. Put shared types, migrations, and core logic below their consumers.
-
Use descriptive branch and PR names. Names such as
feat/user-modelandfeat/auth-apimake the stack map immediately understandable. - Submit early as drafts. This gives teammates visibility and lets you ask for architecture feedback before implementation is complete.
- Sync before continuing work after lower layers merge. This keeps local branches aligned with GitHub's updated stack.
- Explain the stack in the PR descriptions. A short sentence such as “Part 2 of 3: adds the API on top of the user model” helps reviewers understand the intended sequence.
Conclusion
GitHub Stacked PRs turn a large, difficult-to-review change into a series of focused, connected reviews. The workflow is straightforward: initialize a bottom branch, add layers as the work gains dependencies, submit the stack, and keep it synchronized as reviews and merges happen.
Start with a feature that naturally separates into two or three layers. Once your team is comfortable reviewing one focused change at a time, stacked PRs can make large changes feel much more manageable.



Top comments (0)