GitHub Stacked PRs: A Practical Guide to Smaller, Faster Code Reviews

Wait 5 sec.

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 offer a more deliberate workflow: split one large change into a sequence of small, dependent pull requests (PRs) that can be reviewed independently.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.If you need a visual demonstration, I have also made a video walkthrough of this workflow.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)mainEach 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.PrerequisitesBefore 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-stackGitHub 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 StackWe 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 maingit pull --ff-only origin main1. Initialize the bottom layerRun gh stack init with a branch name to create the first branch and register a stack locally:gh stack init feat/user-modelThe 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 layerOnce the user-model work is committed, create a branch above it:gh stack add feat/auth-apiThis 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.tsgit 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 layerCreate the third layer while you are on the current top branch:gh stack add feat/login-uiAdd the form and client-side integration, then commit:git add src/components/LoginForm.tsx src/pages/login.tsxgit 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 viewTip: 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 GitHubWhen the layers are ready, submit them together:gh stack submitThis 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 --autoBy 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 --openAfter submission, the stack looks like this on GitHub:PR #1: Add user model → mainPR #2: Add authentication API → feat/user-modelPR #3: Add login interface → feat/auth-apiThe 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 RequestsThe 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 FeedbackFeedback 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 filesgit add src/models/user.tsgit commit -m "Rename user identifier field"Then rebase the dependent branches in order:gh stack rebasegh 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 gh stack rebase --continueFinally, push the updated branches and refresh their PRs:gh stack submitFor routine maintenance, gh stack sync is often the simplest option:gh stack syncIt 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 PRsUse 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-model and feat/auth-api make 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.ConclusionGitHub 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.