git rebase <base> takes the commits of the current branch from its
common ancestor with <base> up to HEAD, and replants them on top of
<base>. Each commit gets a new parent, so each commit gets a new SHA.
Basic scenario
before: main: A → B → E
\
feat: C → D
git switch feat
git rebase main
after: main: A → B → E
\
feat: C' → D' ← new SHAs
The changes in C' and D' are the same as in C and D. But they are different objects: for Git they are new commits.
When to use it
- Before a PR, so the feature branch sits on top of a fresh main with no merge commits.
- Cleaning up local history via
rebase -i(see interactive-rebase). - Moving a branch to a different base with
--onto.
The main rule
Do not rebase public branches. If someone has cloned the branch, they still have the old SHAs. After your force-push their history diverges. Rebase is safe only on your personal feature branch, before or immediately after review.
Conflicts
If a conflict occurs on any commit, Git stops, marks the conflicting files, and waits for you to resolve them:
# edit the files, remove the markers
git add <files>
git rebase --continue # continue with the next commit
# or skip the current commit:
git rebase --skip
# or cancel everything:
git rebase --abort
A conflict can appear on every commit in a row if each one touches the disputed lines. After rebase each intermediate result is already clean.
After rebase
If the branch was already pushed, a plain push will be rejected because the SHAs changed:
git push --force-with-lease
--force-with-lease verifies that the remote has not moved forward
(no one else pushed in the meantime). Always prefer --force-with-lease
over --force.
Pitfalls
- Rebase copies author dates as-is but updates committer dates. After a
rebase,
git log --pretty=fullermay show seemingly odd discrepancies between the two. - On a large number of commits, rebase can take noticeable time and may require resolving conflicts at each step.
git pull --rebasecombines fetch with a rebase of your local commits on top of the remote ones. A useful default:git config --global pull.rebase true.