Rebasing & History Rewriting
Rebase replays commits onto a new base for a linear, readable history. Interactive rebase squashes, rewords, reorders and drops commits; cherry-pick moves individual commits. Rewriting always creates new hashes, so never rewrite shared commits.
What is this?
git rebase takes the commits on your branch and replays them on top of another branch, producing a linear history with new commit hashes. Interactive rebase (-i) lets you squash, reword, reorder, edit or drop commits. git cherry-pick applies a single commit onto your current branch. Rewriting history is safe only for commits you have not shared.
Why do you need it?
- A linear history is easier to read, bisect and review than a web of merge commits.
- Squashing turns a pile of WIP commits into clean, logical units before a pull request.
- Rebasing a feature onto main integrates the latest changes without an extra merge commit.
- Cherry-picking lets you move an urgent hotfix to multiple branches instantly.
Mental model
Rearranging slides in a presentation deck
Interactive rebase
Before you present, you reorder, merge and rewrite slides. Interactive rebase does the same to commits before you share them.
How it works
- git rebase main finds the merge base, then replays your branch commits on top of the latest main tip.
- Each replayed commit becomes a new commit object with a new parent and therefore a new SHA-1 hash.
- git rebase -i HEAD~3 opens an editor with a todo list; changing verbs (pick, squash, reword, reorder, drop) changes how commits are replayed.
- Squashing folds a commit into the one above it; rewording changes the message; dropping removes the commit entirely.
- If the replayed history was already pushed, you must push --force-with-lease to update the remote.
Rebasing feature onto an updated main.
Key commands
git rebase mainReplay your current branch's commits on top of main.
git rebase -i HEAD~3Interactively edit the last three commits.
git cherry-pick a1b2c3dApply a single commit onto the current branch.
git push --force-with-lease origin featureSafely update a rewritten branch that was previously pushed.
git rebase --continueResume a rebase after resolving a conflict.
git rebase --abortCancel the rebase and return to the original state.
Examples
Squash WIP commits with an interactive rebase
git rebase -i HEAD~3# The todo list opens in your editor:# pick a1b2c3d add login page# pick e4f5g6h wip# pick i7j8k9l fix typo## Change the middle verbs so only the first stays 'pick':# pick a1b2c3d add login page# squash e4f5g6h wip# squash i7j8k9l fix typo## Save and close; you get a second editor to write the combined message. git push --force-with-lease origin feature/loginThe three commits collapse into one with the first message as the default. Because the hashes changed, the already-pushed branch needs --force-with-lease to update the remote safely.
In production
- Squash WIP commits into clean units before opening a pull request.
- Keep a feature branch up to date with main by rebasing, then pushing with --force-with-lease.
- Cherry-pick a critical hotfix from main onto a release branch without merging unrelated work.
- Clean up a messy local commit series so the merged history is bisectable and auditable.
Best practices
- Follow the golden rule: never rebase commits that others have based work on or that live on a shared branch.
- Use --force-with-lease, never bare --force, so you cannot clobber someone else's push.
- Squash to logical units, not one giant commit; preserve meaningful history.
- Rebase your feature onto main frequently to shrink conflicts and keep integration easy.
- Check out git reflog before panicking — rebase mistakes are recoverable.
Common mistakes
- Rebasing a public/shared branch, rewriting history other teammates already have.
- Force pushing without --with-lease and silently overwriting a collaborator's commits.
- Squashing away attribution, co-authors or important context in commit messages.
- Using rebase where a merge is expected on shared branches, breaking downstream fast-forward expectations.
Common issues
error: could not apply <hash>... (patch does not apply)The replayed commit conflicts with the new base.
Resolve the conflict, git add the file, then run git rebase --continue; or git rebase --abort to bail.
! [rejected] feature -> feature (non-fast-forward)Local history was rewritten but the remote still has the old commits.
Since the rewrite is intentional, push with git push --force-with-lease.
fatal: The previous cherry-pick is now emptyThe commit's changes are already present in the target branch.
Use git cherry-pick --skip to drop the empty commit, or --abort to stop.
Debugging tips
- git status during a rebase shows which step is in progress and the conflicted files.
- git rebase --abort returns you to the state before the rebase started.
- git reflog shows every HEAD movement, including the pre-rebase commits, for recovery.
- git show HEAD~1 after a rebase confirms whether the commit was rebuilt correctly.
Interview checkpoint
Hands-on lab
- Create a commit series with messy history
- Squash commits with interactive rebase
- Reword a commit message
- Recover a dropped commit with reflog
Squash and Rewrite with Interactive Rebase
25 min · intermediate
Quick quiz
Which command rewrites and reorders recent commits interactively?
Rebasing preserves the original commit hashes.
What is the safest way to update a pushed branch after a rebase?
Cheat sheet
git rebase mainReplay branch onto maingit rebase -i HEAD~nInteractive edit of last n commitssquashFold commit into the previous onerewordChange a commit messagegit cherry-pick HASHApply one commit elsewheregit push --force-with-leaseUpdate rewritten pushed branchgit rebase --abortCancel the rebase