2026-03-26git[advanced][walkthrough]

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.