When developers work on a large feature, the pull request can quickly become difficult to review. It might contain changes to database models, API endpoints, business logic, tests, and user interface components. Reviewing all these changes together takes time, and finding the cause of a problem becomes harder.

GitHub stacked pull requests offer a practical way to manage this situation. Instead of submitting one large pull request, developers divide the feature into smaller pull requests that depend on one another.

Each pull request focuses on a specific part of the implementation. Reviewers can understand the changes more easily, provide focused feedback, and identify problems before the entire feature is merged.

In this article, you will learn how stacked pull requests work, how to create them with Git, how to manage dependencies, and which practices help keep the workflow manageable.

What Are Stacked Pull Requests?

A stacked pull request is a pull request that builds on another pull request that has not yet been merged.

For example, imagine you are developing an order management feature. The implementation requires changes to database models, an order service, API endpoints, and automated tests.

Instead of submitting all the changes in one pull request, you can organize the work into a stack:

  • PR 1: Add order models and database configuration.

  • PR 2: Implement order service logic.

  • PR 3: Add order API endpoints.

  • PR 4: Add integration tests.

Each pull request depends on the changes introduced by the previous one.

The dependency relationship looks like this:

Code
main
 |
 +-- PR 1: Order Models
       |
       +-- PR 2: Order Service
             |
             +-- PR 3: Order API
                   |
                   +-- PR 4: Integration Tests

The diagram represents the intended dependency structure. In a normal Git workflow, each branch contains the commits inherited from its parent branch.

Stacked pull requests are useful when a feature is too large for one review but contains smaller changes that can be reviewed independently.

Why Use Stacked Pull Requests?

Large pull requests can create several problems for development teams.

Reviewers must understand many unrelated changes at once. Developers may need to wait for extensive feedback before discovering problems in an early part of the implementation. If the pull request changes significantly during review, reviewers might also need to inspect the same areas again.

Stacking reduces these problems by dividing the implementation into smaller units.

Area

Single large pull request

Stacked pull requests

Review scope

Many changes together

Smaller, focused changes

Dependencies

Mixed into one change set

Explicitly represented by branches

Feedback

Often covers several areas

Can target a specific implementation layer

Parallel work

Can be difficult to coordinate

Different parts may be reviewed independently

Maintenance

One large branch to manage

Multiple dependent branches to maintain

Merge process

Usually one large merge

Multiple merges in dependency order

However, stacking does not automatically make every pull request independent. If PR 3 requires the code from PR 2, it cannot be merged safely before PR 2.

How to Create Stacked Pull Requests With Git

Consider a feature developed in a repository where the main branch is named main.

You will create three branches:

  • feature/order-model

  • feature/order-service

  • feature/order-api

Each branch will build on the previous branch.

Step 1: Create the First Branch

Start by updating your local main branch and creating the model branch.

Bash
git switch main
git pull origin main

git switch -c feature/order-model

Implement the order model and database configuration. Commit the changes:

Bash
git add .
git commit -m "Add order model and database configuration"

git push -u origin feature/order-model

Open a pull request from feature/order-model into main.

This pull request should contain only the model and database configuration changes.

Keeping the scope narrow makes it easier for reviewers to verify the data structure and its intended behavior.

Step 2: Create a Branch Based on the First Branch

Now create the service branch from the model branch.

Bash
git switch feature/order-model
git switch -c feature/order-service

Implement the service logic and commit it:

Bash
git add .
git commit -m "Implement order service"

git push -u origin feature/order-service

Create another pull request, but target feature/order-model instead of main.

This is important because the service implementation depends on the model changes.

The second pull request should display only the changes introduced by the service branch when compared with its base branch.

If you target main while the model pull request is still open, the review may include both the model and service changes. That makes the second pull request harder to review.

Step 3: Add the API Branch

Create the API branch from the service branch:

Bash
git switch feature/order-service
git switch -c feature/order-api

Add the API controllers or endpoints and commit the changes.

Bash
git add .
git commit -m "Add order API endpoints"

git push -u origin feature/order-api

Open a pull request from feature/order-api into feature/order-service.

The resulting dependency chain is:

Code
main
  |
  +-- feature/order-model
        |
        +-- feature/order-service
              |
              +-- feature/order-api

GitHub pull requests should use the appropriate parent branch as their base until the preceding changes are merged.

How to Manage the Stack When a Pull Request Is Merged

The most important part of stacked pull requests is maintaining the correct branch relationships.

Suppose the first pull request is approved and merged into main.

The service branch originally contains commits based on feature/order-model. After the model branch is merged, you should update the service branch so that it uses main as its base.

First, update the local main branch:

Bash
git switch main
git pull origin main

Then rebase the service branch:

Bash
git switch feature/order-service
git rebase main

If the rebase succeeds, update the remote branch:

Bash
git push --force-with-lease origin feature/order-service

Use --force-with-lease carefully. Rebasing changes commit history, so the remote branch may need a history update. The command checks that the remote reference has not changed unexpectedly before replacing it.

Do not force-push shared branches without coordinating with other contributors.

Next, change the service pull request's base branch to main in GitHub.

Repeat the process when the service pull request is merged, updating the API branch and changing its base to main.

Always resolve conflicts and run the relevant tests after rebasing.

How to Handle Merge Conflicts

Merge conflicts can occur when a parent branch changes while a child branch is under development.

For example, a reviewer might request a change to the order model. The service branch may then need to be rebased onto the updated parent branch.

Use the following workflow:

  1. Fetch the latest repository changes.

  2. Switch to the branch that needs updating.

  3. Rebase it onto the updated parent branch.

  4. Resolve conflicts in the affected files.

  5. Stage the resolved files and continue the rebase.

  6. Run tests and inspect the final diff.

  7. Push the updated branch and review the pull request again.

The Git commands for resolving a conflict during a rebase are:

Bash
git status

# Edit the files containing conflict markers.

git add path/to/resolved-file.cs
git rebase --continue

If you need to abandon the rebase, use:

Bash
git rebase --abort

This returns the branch to its state before the rebase started.

Avoid resolving conflicts by blindly accepting all changes from one side. That can remove valid work from the other branch.

Best Practices for Stacked Pull Requests

Keep Each Pull Request Focused

Each pull request should represent a meaningful part of the feature.

For example, database models, service logic, and API endpoints are reasonable boundaries when they have clear responsibilities.

Avoid creating many tiny pull requests that require reviewers to switch contexts repeatedly. The goal is to reduce review complexity, not increase the number of reviews unnecessarily.

Make Dependencies Explicit

Use descriptive branch names and explain dependencies in pull request descriptions.

A description might state that the current pull request depends on the order model pull request and should not be merged independently.

This prevents reviewers and maintainers from merging changes in the wrong order.

Run Tests at Each Stage

A child branch should be tested with the changes it depends on.

If the repository uses continuous integration, configure the checks to build and test the pull request's complete proposed change set. Verify how your repository handles pull requests whose base branches are still open.

Tests that pass on a child branch may fail after its parent changes, so rerun relevant checks after rebasing.

Coordinate With Reviewers

Tell reviewers which pull request to review first. If several pull requests are submitted together, they should understand the intended order.

When a parent pull request changes, communicate the impact on child branches rather than allowing reviewers to continue working against outdated diffs.

Avoid Unnecessary History Rewrites

Rebasing can keep the commit history easier to follow, but it changes commit identifiers.

If several developers are working on the same branch, coordinate history changes and avoid rewriting commits that others are actively building on.

Common Mistakes to Avoid

Mistake

Why it causes problems

Better approach

Targeting every pull request at main

Child pull requests may include parent changes

Target the immediate parent branch

Merging a child before its parent

Required changes may be missing

Merge in dependency order

Rebasing without checking the diff

Changes can be lost or unintentionally included

Review the diff and run tests

Force-pushing without coordination

Other contributors may lose work

Coordinate and use --force-with-lease when appropriate

Creating too many small pull requests

Review overhead can increase

Split by meaningful responsibilities

Ignoring outdated pull requests

Reviewers may examine incorrect changes

Update branches and pull request bases after merges

Advantages and Disadvantages

Stacked pull requests provide several benefits:

  • Smaller changes are easier to understand and review.

  • Feedback can focus on specific parts of a feature.

  • Teams can begin reviewing earlier implementation stages.

  • Dependencies become more visible.

  • Large changes can be divided into manageable development tasks.

There are also trade-offs:

  • Developers must maintain branch dependencies.

  • Rebasing and conflict resolution require additional effort.

  • Pull request bases must be updated as parent branches are merged.

  • Continuous integration configuration may require extra attention.

  • Reviewers need to understand the dependency order.

For a small change that fits comfortably into one pull request, stacking may add unnecessary complexity.

Summary

GitHub stacked pull requests help developers divide large features into smaller, dependent changes. By creating branches from their parent branches, developers can keep each pull request focused on a specific part of the implementation.

The key is to maintain the correct dependency order, target each pull request at its immediate parent, and update child branches when earlier changes are merged.

Use stacking when it improves review quality and reduces the risk of large, difficult-to-understand changes. Keep the workflow simple, communicate branch dependencies clearly, and test each stage before merging it into the main branch.