How to rebase stacked Git branches

When working on a feature, you might split it into several stacked branches, so you can merge each one separately. But updating such branches can be annoying, since you have to manage each one. Git 2.38 (2022-10-15) makes such updates easier, with the ability to rebase a stack of branches at once, with the new --update-refs
. Let’s look at a couple of examples.
Rebase stacked branches
Imagine you have this situation, most recent commits first:
* e67fe90 Add deployment (main)
|
| * 73145a7 Add Poll views (HEAD -> poll_views)
| |
| * 3345a24 Add Poll database models (poll_models)
|/
|
* 86e3722 Set up Django
The main
branch has two commits on it. On the side, there are two stacked branches for a new “poll” feature, poll_views
and poll_models
.
Imagine you’d like to rebase the poll_*
branches on top of the latest main
. Without --update-refs
, you’d need to first rebase poll_models
, then rebase poll_views
on top of the new poll_models
. But with the flag you can do this in one command.
First, switch to the final feature branch:
$ git switch poll_views
Second, rebase onto the main
branch with --update-refs
:
$ git rebase --update-refs main
Successfully rebased and updated refs/heads/poll_views.
Updated the following refs with --update-refs:
refs/heads/poll_models
Git tells about poll_views
being rebased, and that it update poll_models
in the process.
Now both branches live on top of the latest commit on main
:
* c6ac1a3 Add Poll views (HEAD -> poll_views)
|
* 9f9622b Add Poll database models (poll_models)
|
* e67fe90 Add deployment (main)
|
* 86e3722 Set up Django
Ta-daa!
How to add changes to stacked branches
Imagine you had that same initial situation:
* bc63397 Add deployment (HEAD -> main)
|
| * 94d92fd Add Poll views (poll_views)
| |
| * 229c030 Add Poll database models (poll_models)
|/
|
* 86e3722 Set up Django
After submitting poll_models
and poll_views
for code review, you have some changes to make to both. How can you make those changes, and ensure they end up in the right branches?
First, check out the last of the stacked branches:
$ git switch poll_views
Second, make changes applicable to both branches:
...
$ git commit -m "Add database constraint to Poll model"
...
$ git commit -m "Add rate-limiting to view"
...
Third, start an interactive rebase of the stack:
$ git rebase -i --update-refs main
-i
is short for --interactive
, which enables rebase’s interactive mode. This will open your text editor with a file you can use to control rebase operations.
Fourth, change the rebase file to move the commits to the appropriate branches. When the file opens, you’ll see it starts like:
pick 229c030 Add Poll database models
update-ref refs/heads/poll_models
pick 94d92fd Add Poll views
pick 31d5fc9 Add database constraint to Poll model
pick af05cde Add rate-limiting to view
# Rebase bc63397..af05cde onto bc63397 (5 commands)
#
# Commands:
...
The pick
lines list commits, in the order they’ll be rebased. The update-ref
line controls the commit that the poll_models
branch will point to after the rebase. These are also explained in the Commands comment from Git:
# p, pick <commit> = use commit
...
# u, update-ref <ref> = track a placeholder for the <ref> to be updated
# to this position in the new commits. The <ref> is
# updated at the end of the rebase
...
In this case, the “Add database constraint to Poll model” commits should be part of poll_models
. Move its line above the update-ref
line:
pick 229c030 Add Poll database models
pick 31d5fc9 Add database constraint to Poll model
update-ref refs/heads/poll_models
pick 94d92fd Add Poll views
pick af05cde Add rate-limiting to view
# Rebase bc63397..af05cde onto bc63397 (5 commands)
...
Fourth, save and close the file, and Git will perform the instructed rebase:
$ git rebase -i --update-refs main
Successfully rebased and updated refs/heads/poll_views.
Updated the following refs with --update-refs:
refs/heads/poll_models
Now the two branches are on top of the latest main
, with their respective commits:
* 9182779 Add rate-limiting to view (HEAD -> poll_views)
|
* df9ae26 Add Poll views
|
* d0cfd78 Add database constraint to Poll model (poll_models)
|
* e7ee0b7 Add Poll database models
|
* bc63397 Add deployment (main)
|
* 86e3722 Set up Django
Huzzah!
Now you can send each branch for another round of code review, probably with git push
.
Enable --update-refs
by default
You can make it easier to use stacked branches by enabling --update-refs
by default. Then, every time you rebase a branch, any earlier branches in the stack will also get updated.
To enable the flag by default, for all repos, add the rebase.updateRefs
boolean option to your global config:
$ git config --global --add --bool rebase.updateRefs true
This will add the option in your ~/.gitconfig
file like so:
[rebase]
updateRefs = true
Nice one.
Learn how to make your tests run quickly in my book Speed Up Your Django Tests.
One summary email a week, no spam, I pinky promise.
Related posts:
- How to Squash and Rebase a Git Branch
- How to Clean Up Unused Code With Git
- How to Run a Command on Many Files in Your Git Repository
Tags: git