Simohamed Vue and React developer | Linux enthusiast | Interested in FOSS.

Using stacked pull requests in GitHub

4 min read 1182

GitHub Logo

When working on a team, one of the most inherently difficult and more involved processes is code reviews. To review a large pull request (PR), you need time and context as well as the energy to parse and hold your mental boundaries in focus.

The same cannot be said for small PRs, however, where it’s much easier to consider changes and to suggest changes of our own. So how can we introduce large changes while avoiding cognitively overloading our teammates? This is where stacked pull requests come into play.

In this article, we will cover what stacked pull requests are, when and how to use them, and how to convert a monolithic PR into a stacked one.

What are stacked pull requests?

Stacked PRs, also know as dependent, incremental, or chained PRs, are pull requests that branch off from other pull requests. In git terms, they are feature branches that are checked out from other feature branches to build small and coherent units to represent changes.

When working with stacked PRs, it’s helpful to think of your branches as a similar layer of code-change organization to git commits. You have the choice between pushing all your changes as a single big commit and organizing your code in separate commits. Having multiple commits is the better practice. So, what good is there in doing the same with your branches?

When and why should you use stacked PRs?

TL;DR:

  • To split large PRs
  • To share a piece of code between multiple feature branches
  • To make it easy for two people to work on the same feature

As previously stated, stacked PRs are useful when wanting to split large pull requests. The other situation where stacked PRs really shine is when you want to use a particular change in two or more branches.

For instance, imagine wanting to migrate a codebase to TypeScript where you rewrite the pages in TS while your teammate rewrites the components. The TypeScript setup (dependency installation, tsconfig.json, etc.) would have to be shared between the two of you, either by eagerly committing the setup to the master (or develop) branch, or by stacking your PRs on top of a ts-setup feature branch.

Migrate-pages and Migrate-components Branched From ts-setup

This would allow the two branches, migrate-pages and migrate-components, to share the TypeScript configuration in a master-like relationship with the ts-setup branch. This means that if a change occurs in ts-setup, migrate-pages would have to merge or rebase ts-setup.

We made a custom demo for .
No really. Click here to check it out.

If page migration is dependent on components migration, you can stack the branches even further.

Migrate-pages Branched From Migrate-components

This is especially handy when two people are trying to collaborate on the same feature. Stacking two branches is easier to handle than working on the same branch.

How to stack pull requests

To stack two PRs, checkout the first branch from your base master (or develop) and push your changes.

$ git status # we are on master
On branch master

$ git checkout -b ts-setup # checkout from master
$ npm i -D typescript && npx tsc --init
$ git add . && git commit -m 'Setup TypeScript'
$ git push -u origin ts-setup

In your GitHub repository, you’ll be prompted to create a pull request from ts-setup:

Pull Request From ts-setup

Create the PR with the base as master.

Base Master

Then, checkout the second branch from the first.

$ git status
On branch ts-setup

$ git checkout -b migrate-components # checkout from ts-setup
$ mv components/Button.jsx components/Button.tsx
$ git add . && git commit -m 'Migrate button to TS'
$ git push -u origin migrate-components

This effectively turns ts-setup and migrate-components into stacked branches ready to become stacked PRs.

Compare Pull Request

Setup Typescript

Notice that while master is set as the base of our PR, the changes from ts-setup (“Setup TypeScript” commit) are present, and our commit count is at two.

Ts-setup Present

Changing the base branch to ts-setup removes overlapping commits, bringing our commit count to just one.

Commit Count Down to One

Make sure to clearly state that a PR is stacked on top of another. A label might also help.

Migrate-components

The worst-case scenario is someone merging a PR, pulling master, not finding the changes, and getting confused, which begs the question, how do you merge stacked PRs?

How to merge stacked pull request

Squash, merge, or rebase?

The only restriction you have on merging while working with stacked PRs is that you cannot “squash and merge” or “rebase and merge.” You must merge directly. This restriction does not apply to the last PR on a given PR chain.

This is due to how git’s history works. Git tracks changes through commits by commit hashes. If you recall, changing the base from master to ts-setup shaved off the common commit between ts-setup and migrate-components.

Git knew to do so because it saw a commit with the same metadata on the two branches. Squashing and rebasing both overwrites Git’s history (albeit in different ways), removing the overlap that deemed the two branches continuous.

In what order should I merge?

TL;DR: All orders are valid. It depends on how you want the merge commits to look on master.

The order in which we should merge or stacked PRs is completely subjective. If we merge ts-setup with a commit message of “Setup TypeScript” and delete the PR branch, GitHub will automatically pick up on this and change the base of our migrate-components PR to master.

Base Automatically Changed

This will give us the chance to merge with master with a separate merge commit message, “Migrate Components to TS.”

Alternatively, we can first merge migrate-components into ts-setup, then merge ts-setup with master with a single merge commit message to master of “Setup and migrate components to TS.”

Splitting an existing large PR into a stack of PRs

Let’s say we’re trying to merge a large migrate-to-firebase branch with develop. The PR affects tens of files and has proven to be difficult to review. To split it into multiple PRs, locally, we do the following:

$ git checkout migrate-to-firebase
$ git reset --soft develop
$ git restore --staged .

First, we checkout the branch and then we uncommit all the changes that do not exist on develop without removing the changes themselves. This results in all the changes being staged as git status would indicate, so we unstage them by running git restore --staged.

You can rename the branch to give an accurate account of what it actually does by running:

$ git branch -M setup-firebase

You then can start adding, committing, and checking out new branches, forming a chain.

Using stacked PRs in Gitflow

One of the issues you encounter while using the Gitflow workflow is being unable to selectively push feature branches from develop in a given release. For example, if you have a redesign coming up that you wish to work on but not release yet, you can scope that redesign in a parent feature branch that smaller feature branches can be stacked on top of, then merge that parent branch with develop once it’s finished.

Conclusion

In this article, we learned about stacked PRs, why and when to use them, and how to create and merge them. We also talked about how they can enhance the Gitflow workflow. Thanks for reading!

: Full visibility into your web apps

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

.
Simohamed Vue and React developer | Linux enthusiast | Interested in FOSS.

Leave a Reply