Updating stacked branches with --update-refs Jump to heading

Stacked branches β€” feature/api on main, feature/ui on feature/api, feature/docs on feature/ui β€” let you split large work into reviewable pull requests that build on each other. The cost used to be maintenance. Rebasing the bottom branch onto a new main left the upper branches pointing at the old commits, and each had to be moved separately with careful --onto commands. Since Git 2.38, git rebase --update-refs does it in one step: rebase the top of the stack, and every branch that pointed into the rebased range is moved to the corresponding new commit. This page shows the workflow for keeping a stack current, editing a lower branch, and pushing the result, within interactive rebase workflows.

When to use this approach Jump to heading

  • You split a large change into dependent branches, each reviewed as its own pull request.
  • Main has moved and the whole stack needs to follow.
  • A reviewer requested a change in a lower branch, and the upper branches must pick it up.
  • You use Git 2.38 or newer. The surrounding review practice is in stacked pull requests without a dedicated tool.

Step 1 β€” Turn it on by default Jump to heading

rebase.updateRefs makes every rebase update branches pointing into the rebased range. It is safe to enable globally: it only moves branches whose commits are being rewritten anyway.

git config --global rebase.updateRefs true
git --version      # 2.38 or newer
# Verification: the stack's branches all sit on one line of history
git log --oneline --decorate main..feature/docs
# 6c1f2e0 (feature/docs) Document export API
# 1d7a9b3 (feature/ui) Add export button
# 8e2c4f5 Wire export form
# 4b9d0a7 (feature/api) Add export endpoint

Step 2 β€” Rebase the whole stack onto a new main Jump to heading

Check out the top branch and rebase it. Every branch below it in the range is updated to its rewritten commit.

git switch feature/docs
git rebase main
git log --oneline --decorate main..feature/docs   # all three branch names moved
One rebase moves the whole stackThree stacked branches β€” api, ui and docs β€” sit on an old main. Rebasing the docs branch onto the new main with update-refs replays all of their commits and moves the api and ui branch names to the rebased equivalents of their tips.git rebase main, from the top of the stackmainoldnewbeforeoldAU1U2DafternewA'U1' U2'D'branchesapiuidocswithout update-refs, api and ui would still point at the old A and U2 One rebase moves the whole stackThree stacked branches β€” api, ui and docs β€” sit on an old main. Rebasing the docs branch onto the new main with update-refs replays all of their commits and moves the api and ui branch names to the rebased equivalents of their tips.git rebase main, from the top of the stackmainoldnewbeforeoldAU1U2DafternewA'U1' U2'D'branchesapiuidocswithout update-refs, api and ui would still point at the old A and U2

In the interactive todo list, --update-refs shows as update-ref lines, one per branch, placed after the commit each branch should point to. You can see exactly what will move.

pick 4b9d0a7 Add export endpoint
update-ref refs/heads/feature/api

pick 8e2c4f5 Wire export form
pick 1d7a9b3 Add export button
update-ref refs/heads/feature/ui

pick 6c1f2e0 Document export API

Step 3 β€” Edit a lower branch and carry the change upward Jump to heading

A reviewer asks for a change in feature/api. Make it as a fixup on the top branch and let autosquash place it, or make it as a new commit in the right place during an interactive rebase. Either way, rebase from the top so every branch updates.

git switch feature/docs
# change the endpoint, then record it as a fixup for the api commit
git commit -a --fixup=4b9d0a7
git rebase -i --autosquash main

The fixup is folded into the api commit, and the update-ref lines move feature/api, feature/ui and feature/docs to the rewritten commits. The edit technique itself is described in editing an old commit in the middle of a branch.

A review change to the bottom of a stackThe fix is committed on the top branch as a fixup for the lowest commit. An interactive autosquash rebase from the top folds it into place, rewrites everything above it, and update-refs moves all three branch names, which are then pushed together.Fixup on top--fixup=<api commit>rebase -i--autosquash mainupdate-refapi, ui, docs movedPush all--force-with-leaseyou never check out the lower branches β€” the top of the stack is where you work A review change to the bottom of a stackThe fix is committed on the top branch as a fixup for the lowest commit. An interactive autosquash rebase from the top folds it into place, rewrites everything above it, and update-refs moves all three branch names, which are then pushed together.Fixup on top--fixup=<api commit>rebase -i--autosquash mainupdate-refapi, ui, docs movedPush all--force-with-leaseyou never check out the lower branches β€” the top of the stack is where you work

Step 4 β€” Push every branch in the stack Jump to heading

Each branch backs a pull request, so all of them must be pushed. Push them together, each with a lease, so a reviewer’s push to one of them is not overwritten.

git push --force-with-lease origin feature/api feature/ui feature/docs
# or with --atomic, so either all update or none do
git push --atomic --force-with-lease origin feature/api feature/ui feature/docs

⚠️ SAFETY WARNING: Force-pushing a stack rewrites every pull request in it. If a collaborator pushed to any branch in the stack, --force-with-lease refuses that branch; fetch and incorporate their commit before pushing again. If a rebase of the stack went wrong, the reflog of each branch records its previous tip: git reflog show feature/ui.

Step 5 β€” Land the stack from the bottom Jump to heading

When the bottom pull request merges, the next one should retarget to main. If the bottom was squash-merged, its original commits are not on main, and the next branch must be moved off them with --onto.

# After feature/api was squash-merged into main
git fetch origin
git switch feature/docs
git rebase --onto origin/main feature/api feature/docs   # update-refs moves feature/ui too
gh pr edit "$(gh pr view feature/ui --json number --jq .number)" --base main
git push --force-with-lease origin feature/ui feature/docs
A stack from first push to last mergeThe stack of three branches is pushed as three pull requests. Main moves and the stack is rebased together. A review change to the bottom is folded in. The bottom merges, the next pull request retargets to main, and so on until the top merges.Stack pushed3 pull requestsday 1main movedrebase from topday 2Review fixfixup + autosquashday 3api mergedui retargets mainday 4ui, docs mergedstack doneday 5each merge shortens the stack by one; the workflow stays the same at every step A stack from first push to last mergeThe stack of three branches is pushed as three pull requests. Main moves and the stack is rebased together. A review change to the bottom is folded in. The bottom merges, the next pull request retargets to main, and so on until the top merges.Stack pushed3 pull requestsday 1main movedrebase from topday 2Review fixfixup + autosquashday 3api mergedui retargets mainday 4ui, docs mergedstack doneday 5each merge shortens the stack by one; the workflow stays the same at every step

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Does update-refs move branches that are checked out in another worktree? Jump to heading

No. Git refuses to move a branch checked out elsewhere, and the todo list notes it. Switch that worktree to another branch first, or update it separately after the rebase.

What if I only want to rebase the bottom branch? Jump to heading

Rebasing a lower branch alone leaves the upper ones behind. Always rebase from the top; if you only want to change the bottom, the upper branches still need to follow its new commits.

Do I still need a stacking tool? Jump to heading

For a few branches, --update-refs covers most needs. Dedicated tools add conveniences β€” automatic retargeting, a dashboard of stack status β€” that become worthwhile with deep stacks or many people stacking.