Rebasing onto a new base with --onto Jump to heading
Plain git rebase main moves your branch so that it starts from the tip of main. It decides which of your commits to move by finding where your branch and main diverged. That works until the branchβs history contains commits that should not move with it: the commits of a parent branch that was squash-merged, a few experimental commits at the start, or a base that was the wrong branch entirely. git rebase --onto separates the two decisions β which commits to move, and where to put them β and handles all of these cases precisely. This page explains its three arguments and the situations where it is the right tool, within interactive rebase workflows.
When to use this approach Jump to heading
- Your branch was built on top of another feature branch that has since been squash-merged into main.
- You branched from the wrong base β main instead of a release branch, or the reverse.
- You want to drop the first few commits of a branch while keeping the rest.
- You manage dependent branches, a pattern described in stacked pull requests without a dedicated tool.
Step 1 β Learn the three arguments Jump to heading
The full form is git rebase --onto <newbase> <upstream> <branch>. It takes the commits in upstream..branch β reachable from the branch but not from upstream β and replays them on top of newbase.
# Preview exactly which commits will move before running it
git log --oneline <upstream>..<branch> That preview is the single most useful habit with --onto. If the list is wrong, the arguments are wrong.
Step 2 β Detach a branch from a squash-merged parent Jump to heading
You built feature/b on top of feature/a. feature/a was squash-merged into main, so its commits exist on main only as a single new squash commit. A plain git rebase main on feature/b tries to replay feature/aβs original commits too, producing conflicts with the squash.
# Move only feature/b's own commits: those after the old tip of feature/a
git log --oneline feature/a..feature/b # preview: just b's commits
git rebase --onto main feature/a feature/b If feature/a has already been deleted, use the commit it pointed to β find it with git merge-base feature/b <old-a-sha> or from the pull requestβs head commit.
# Verification: feature/b now contains main plus only its own commits
git log --oneline main..feature/b Step 3 β Retarget a branch to a different base Jump to heading
You started a fix from main, but it needs to go to release/2.4. Move the fixβs commits from main to the release branch.
git log --oneline main..fix/timezone # the fix's own commits
git rebase --onto release/2.4 main fix/timezone Expect conflicts if main and the release branch differ in the area the fix touches. This is the same situation as a backport, and the analysis in resolving cherry-pick conflicts on older branches applies.
Step 4 β Drop the first commits of a branch Jump to heading
To discard the first N commits of a branch and keep the rest, use the Nth commit as the upstream and the original base as the new base.
git log --oneline --reverse main..feature/x # 1 spike, 2 spike, 3 real, 4 real
git rebase --onto main feature/x~2 feature/x # move only the last two commits β οΈ SAFETY WARNING: Every form of rebase rewrites the moved commits. If the branch was already pushed and others use it, they must reset to the new version, and you must push with
git push --force-with-lease. If the result is wrong,git reset --hard ORIG_HEADimmediately after the rebase restores the branch; later, usegit reflog show feature/xto find its previous tip.
Step 5 β Rebase several dependent branches at once Jump to heading
When feature/c depends on feature/b, which depends on feature/a, moving one means moving all. Since Git 2.38, --update-refs updates every branch pointing into the moved range, so a single rebase of the top branch moves the whole stack.
git rebase --onto main feature/a feature/c --update-refs
git log --oneline --decorate main..feature/c The workflow is covered in detail in updating stacked branches with --update-refs.
Step 6 β Find the right upstream when the old parent is gone Jump to heading
The hardest part of --onto in practice is the middle argument when the old parent branch has been deleted after merging. You need the commit where your own work begins. Three sources usually have it.
# 1. The pull request of the parent branch records its final head commit
gh pr view 801 --json headRefOid --jq .headRefOid
# 2. Your branch's reflog records where it was created or last rebased
git reflog show feature/b | tail -3
# 3. Failing those, the first commit you authored on the branch
git log --reverse --format='%h %an %s' main..feature/b | awk -v me="$(git config user.name)" '$2==me {print $1; exit}' With the first of your own commits identified as F, the upstream is its parent, so the command becomes git rebase --onto main F^ feature/b. Preview with git log --oneline F^..feature/b first: the list should start with F and contain nothing from the parent branch.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
What if I get the upstream argument wrong? Jump to heading
Too old an upstream moves extra commits; too new a one leaves some of your commits behind. The preview command catches both. If you already ran it, git reset --hard ORIG_HEAD undoes the rebase.
Can I combine --onto with -i? Jump to heading
Yes. git rebase -i --onto main feature/a feature/b opens the todo list for exactly the commits being moved, so you can also reorder, squash or drop them in the same operation.
Does --onto work with merges in the moved range? Jump to heading
By default, rebase flattens merges. Add --rebase-merges to keep them, as described in keeping merge commits with --rebase-merges.
Related Jump to heading
- Interactive Rebase Workflows β the parent topic.
- Safe Git Rebase -i for Shared Branches β rewriting branches others use.
- Integrating Dependent Feature Branches β when branches depend on each other.
- Backporting a Range of Commits in Order β the cherry-pick alternative to retargeting.