/lessons/2026-03-26
The Art of git rebase --onto
git rebase --onto lets you transplant a branch to any base — the surgical precision tool most developers never learn.
Standard git rebase replays your branch commits on top of another branch. But git rebase --onto gives you a third argument — and with it, surgical control over exactly which commits go where.
The Three-Argument Form
git rebase --onto <newbase> <oldbase> <branch>
Translation: take the commits between oldbase and branch, and replay them onto newbase.
Scenario 1: Moving a Branch to a Different Parent
You branched feature-b off feature-a, but now feature-a is abandoned and you want feature-b based on main:
# Before:
# main -- A -- B (feature-a) -- C -- D (feature-b)
git rebase --onto main feature-a feature-b
# After:
# main -- C' -- D' (feature-b)
Only commits C and D — the ones unique to feature-b — are replayed onto main.
Scenario 2: Removing Commits from the Middle
You made commits A, B, C, D on a branch but B and C were a wrong turn:
git rebase --onto A C feature-branch
# Before: main -- A -- B -- C -- D (feature-branch)
# After: main -- A -- D' (feature-branch)
Commits B and C are cleanly removed. D is replayed directly onto A.
Scenario 3: Splitting a Stacked PR
You have a branch with 6 commits but only the first 3 should be in PR #1:
git checkout -b pr-2 feature-branch
git rebase --onto $(git rev-parse HEAD~3) feature-branch~3 pr-2
Now pr-2 contains only the last 3 commits, based on the tip of what will be PR #1.
Pro Tip
Always use git log --oneline --graph before and after a rebase --onto operation. The three-argument form is powerful but easy to get wrong if you miscounted commits or picked the wrong base.
If something goes wrong: git reflog is your safety net. Every rebase records the pre-rebase HEAD, so you can always git reset --hard back to it.
Example
Real-world scenario — you started a feature branch off a release branch, but now need it on main:
# See where your branch diverged
git log --oneline --graph --all
# Count: release-2.1 is 12 commits ahead of main
# Your feature has 4 commits on top of release-2.1
git rebase --onto main release-2.1 my-feature
# Verify
git log --oneline main..my-feature
# Should show exactly your 4 commits
The key insight: the second argument (oldbase) defines the boundary. Everything between oldbase and branch is what moves.