How I rebase linked or chained pull requests
Linked pull requests let me split a large change into smaller reviews. The tradeoff appears when an earlier branch changes or merges.
Git provides two options that make this cleanup easier. I use --onto for one branch and --update-refs for a branch chain.
Move one branch with --onto#
Consider the following Git history:
gitGraph
commit id: "0-19as8su"
commit id: "1-0a9s8ds"
branch feature-1
checkout feature-1
commit id: "2-a0s9asd"
commit id: "3-ss91820" type: HIGHLIGHT
branch feature-2
checkout feature-2
commit id: "4-as98dam"
commit id: "5-as0d9km"
checkout feature-1
commit id: "6-0as9d8s"
commit id: "7-as90dia"
checkout main
merge feature-1 id: "8-0as9d8s"
We create feature-1 from main. We then create feature-2 from feature-1 and continue working on it.
Later, we add two commits to feature-1 and merge that branch into main.
At this point, feature-2 still starts from the earlier feature-1 commit. Check out feature-2, then move its commits onto main:
git checkout feature-2git rebase --onto main 3-ss91820Here, 3-ss91820 is the commit where feature-2 branched from feature-1. Git takes the later commits and reapplies them onto main.
The new history looks like this:
gitGraph
commit id: "0-19as8su"
commit id: "1-0a9s8ds"
branch feature-1
checkout feature-1
commit id: "2-a0s9asd"
commit id: "3-ss91820" type: HIGHLIGHT
checkout feature-1
commit id: "6-0as9d8s"
commit id: "7-as90dia"
checkout main
merge feature-1
branch feature-2
checkout feature-2
commit
commit
Update a branch chain with --update-refs#
The --update-refs option can update the branches in a chain during one rebase. Consider this history:
%%{init: { 'gitGraph': {'showCommitLabel': false}} }%%
gitGraph
commit
commit
branch feature-1
checkout feature-1
commit
commit
branch feature-2
checkout feature-2
commit
commit
checkout feature-2
branch feature-3
checkout feature-3
commit
commit
commit
checkout feature-1
commit
commit
checkout main
merge feature-1
After we merge feature-1 into main, the remaining work has two parts:
- Rebase
feature-2ontomain. - Rebase
feature-3ontofeature-2.
Check out the last branch in the chain. Then use --update-refs to complete both steps:
# Check out the top branch in the chain.git checkout feature-3
# Rebase the branch onto main and update every branch in the chain.git rebase main --update-refsThe new history looks like this:
%%{init: { 'gitGraph': {'showCommitLabel': false}} }%%
gitGraph
commit
commit
branch feature-1
checkout feature-1
commit
commit
checkout feature-1
commit
commit
checkout main
merge feature-1
branch feature-2
checkout feature-2
commit
commit
checkout feature-2
branch feature-3
checkout feature-3
commit
commit
commit
This approach keeps the branch relationships intact without a separate rebase for every branch. I still review the new history before I force-push anything.