Stacked pull requests with git rebase --update-refs
If you are using pull/merge requests as your code integration strategy, you will run into situations where multiple PRs will depend on one another. This can happen when PRs grow too big and need to be split up, the PR depends on another feature branch, or anytime you want to submit a PR for review but still continue to work without waiting.
When you end up with multiple PRs that depend on one another, you get stacked pull requests. The image below illustrates the situation:
Creating stacked PRs is simple, and one has to only go and create new branches from the correct parent, so that pr-1
would be based on main
, pr-2
would be based on pr-1
, and so on.
The trouble comes when branches in such a chain start to change or open pull requests from these branches get merged. When this happens, some branches will be left in a bad state, pointing to the wrong commits. Our goal is to always have the correct commits pointing to each other so that the whole chain of commits makes sense.
There are multiple approaches to deal with updating the branches in the stack: updating each branch one by one or using --update-refs
to do it in one go.
We can always update each branch individually—one by one—following the chain (e.g., by updating, pr-2
before pr-3
and pr-3
before pr-4
):
Using `
git rebase <parent>
` when the parent branch introduces changes unrelated to commits in our branch. This is the simplest case, and it is the same `rebase` you would do without dealing with stacked branches.Using `
git rebase --onto <parent> <original parent>
` in situations where the original parent branch gets merged. For example, ifpr-1
gets merged intomain
, we want to change ourpr-2
parent frompr-1
tomain
(`git rebase --onto main pr-1
`).
(Please note that the examples above assume that you have currently checked out the branch you want to update.)
Depending on the situation, this might be a lot of work, since some manual steps are required to be done for each branch in the chain. I used both approaches in the past, and there is nothing wrong with them, especially if you manage one or two stacked branches.
The rebase --onto
can also be useful when changing parents outside of stacked PRs (“take the last 2 commits and move them on top of feature-x” can be as simple as `git rebase --onto feature-x HEAD~2
`).
Fortunately, Git makes it possible to directly update all branches in the stack by using rebase --update-refs
. To use it:
Check out the last branch in the chain, like
pr-3
Run
rebase main --update-refs
If, for instance, the pr-1
branch is already merged into main
, Git recognizes that and updates all references from pr-2
to main
and pr-3
to the new pr-2
. This makes it seamless to keep your stacked PRs in sync, although these steps don’t cover pushing the updated branches upstream. You will still have to do that yourself.
Happy stacking and have a nice weekend,
—Petr