What Is Rebase?
Rebase re-applies your commits on top of a different base commit. Instead of creating a merge commit that joins two branches, rebase takes each commit from your branch, lifts it off its current base, and replays it starting from a new base commit.
The result: your branch looks like it was created from the latest state of main — no divergence, no merge commit, clean linear history.
Even though the content of your commits stays the same, rebase creates new commits with new SHA hashes. The original commits still exist temporarily but become unreachable. This is why rebase on shared branches is dangerous — your teammates have the old SHAs and Git can't reconcile them.
Rebase vs Merge
| Aspect | Merge | Rebase |
|---|---|---|
| History shape | Preserves branch topology (forked graph) | Creates linear history (straight line) |
| Merge commit | Creates one (with --no-ff or when diverged) | No merge commit |
| Commit SHAs | Original commits unchanged | New commits created (new SHAs) |
| Safety on shared branches | Safe | DANGEROUS — never rebase shared commits |
| Best for | Preserving full branch history, team merges | Cleaning up local feature branch before PR |
Basic Rebase
Before rebase (diverged branches)
C4 ← C5
/ ↑
C1 ← C2 ← C3 feature/login
↑
main (new commits C6, C7 added after branching)
C6 ← C7
/
C1 ← C2 ← C3
↑
main
After: git rebase main (from feature/login)
C1 ← C2 ← C3 ← C6 ← C7 ← C4' ← C5'
↑ ↑
main feature/login (HEAD)
C4' and C5' are NEW commits (same content as C4/C5, but new SHAs).
The branch now starts from the tip of main — clean linear history.
# Standard rebase workflow
git switch feature/login # make sure you're on the feature branch
git rebase main # rebase onto main
# If there are no conflicts, it completes automatically.
# If there ARE conflicts, resolve them then:
git add <resolved-file>
git rebase --continue # continue to next commit
# After rebase, merge into main (will be a fast-forward now)
git switch main
git merge feature/login # fast-forward — clean!
Interactive Rebase
Interactive rebase (git rebase -i) opens a text editor listing your recent commits. You can reorder, rename, squash, or drop individual commits before they get replayed. This is the most powerful way to clean up your commit history before submitting a pull request.
# Interactively edit the last 3 commits
git rebase -i HEAD~3
# OR: interactively rebase everything since branching off main
git rebase -i main
Git opens your editor with a list like this:
pick a1b2c3d Add login form HTML
pick 9f8e7d6 fix typo
pick 5c4b3a2 Add login form validation
pick 2d1e0f9 WIP: more validation
# Commands:
# p, pick = use commit as-is
# r, reword = use commit but edit the message
# s, squash = combine into previous commit (keep both messages)
# f, fixup = combine into previous commit (discard this message)
# d, drop = remove this commit entirely
# (lines can be reordered to reorder commits)
Example: squash the messy commits into one clean commit
pick a1b2c3d Add login form HTML
f 9f8e7d6 fix typo ← fixup: fold into previous, discard message
s 5c4b3a2 Add login form validation ← squash: fold in, keep message option
f 2d1e0f9 WIP: more validation ← fixup: fold in, discard message
# Save and close the editor.
# Git opens editor again to write the final combined commit message.
# Result: ONE clean commit instead of four messy ones.
| Command | Shorthand | What it does |
|---|---|---|
| pick | p | Use the commit as-is — no change |
| reword | r | Use commit but open editor to rewrite the message |
| squash | s | Combine into previous commit; opens editor to merge messages |
| fixup | f | Combine into previous commit; silently discard this commit's message |
| drop | d | Delete this commit entirely from history |
The Golden Rule of Rebase
When you rebase, Git creates new commits with new SHA hashes. If your teammates already have the original commits (because they pulled them), their Git history now conflicts with yours. When they try to pull, Git sees two different commits with the same content but different hashes — causing confusing merge conflicts and duplicated commit history. This is the most dangerous mistake in Git. The rule: rebase only on your local branch before pushing, or on a branch only you are working on.
# SAFE: rebase your LOCAL feature branch before pushing
git switch feature/login
git rebase -i main # clean up commits
git push -u origin feature/login # first push — fine
# DANGEROUS: rebasing AFTER you've already pushed
# (teammates or CI have already pulled these commits)
git rebase main # creates new SHAs
git push --force-with-lease # need force push — red flag!
# This BREAKS anyone who pulled the old branch
# Only OK if you're SURE no one else is on that branch:
git push --force-with-lease origin feature/login # safer than --force
When to Use Rebase
- Clean up local feature branch before opening a pull request — squash "WIP" and "fix typo" commits into logical units.
- Update feature branch with latest main — rebase onto main instead of merging main into your branch (avoids merge commit pollution).
- Interactive rebase to reorder commits — put related changes together for reviewers.
# Abort rebase if something goes wrong
git rebase --abort
# Returns everything to pre-rebase state
# If a conflict occurs during rebase, resolve it then:
git add <resolved-file>
git rebase --continue
# Skip a problematic commit (use carefully — loses that commit's changes)
git rebase --skip
Rebase vs Merge — and the One Rule You Must Not Break
Both integrate changes, but they tell different stories. Merge preserves history exactly and adds a merge commit (a fork-and-join in the graph). Rebase rewrites your commits on top of the latest base, producing a clean, linear history — as if you'd branched from the newest point.
# replay your feature commits on top of updated main
git switch feature
git rebase main
Rebase creates new commits with new hashes and discards the old ones. If teammates based work on the originals, you've rewritten history out from under them — painful conflicts and lost work. Rebase freely on your local, unpushed branch; use merge for anything public.
Interactive rebase = tidy up before sharing
git rebase -i main lets you squash "wip", "fix typo", "oops" commits into one clean commit, reorder them, or reword messages — so your pull request reads as deliberate history instead of a mess.
🏋️ Practical Exercise
- Rebase a feature branch onto main.
- Compare history before and after the rebase.
- Run an interactive rebase with
-i. - Squash two commits with rebase.
- Abort a rebase in progress.
🔥 Challenge Exercise
Rebase a feature branch onto an updated main for a linear history, then use interactive rebase to squash two commits into one. Explain the difference between merge and rebase, and the golden rule: never rebase shared/public commits.
📋 Summary
- Rebase re-applies your commits on top of a new base — creating new commit SHAs and a clean linear history.
- Rebase vs merge: rebase = linear history (no merge commit); merge = preserves branch topology.
git rebase main— rebase current branch onto main (update with latest main changes).git rebase -i HEAD~N— interactive rebase for last N commits: squash, reword, drop, reorder.- Interactive commands: pick (keep), squash (combine + merge messages), fixup (combine + discard message), reword (edit message), drop (delete).
- The Golden Rule: NEVER rebase commits that have been pushed to a shared branch. Rebase only local, unpushed commits.
git rebase --abortcancels the rebase safely at any point.
Interview Questions
- What does
git rebasedo? - What is the difference between merge and rebase?
- What is interactive rebase used for?
- What is the golden rule of rebasing?
- How do you squash commits?
Related Topics
FAQ
No — the content of the commits stays the same. The code changes introduced by each commit are preserved exactly. What changes is the commit's metadata: its parent hash (which is now a different base), and therefore its own SHA hash. Think of it like printing the same document on new paper — the text is identical, but the paper (the object in Git's store) is new.
Both combine multiple commits into one, but they work differently. git merge --squash combines all commits from a feature branch into a single staged change on the target branch — it doesn't touch the feature branch itself. Interactive rebase's squash/fixup combines commits within the same branch's history, rewriting that branch. Merge squash is for when you're done with the feature; interactive rebase squash is for cleaning up before the merge.
If you're the only one working on that branch (a personal feature branch, not shared), you can rebase locally and then force-push: git push --force-with-lease origin branch-name. The --force-with-lease flag is safer than --force because it checks that no one else has pushed to the remote branch since your last fetch. If you're collaborating with others on the same branch, communicate before rebasing or use merge instead.
Both work. git rebase main (from your feature branch) gives a cleaner linear history and makes the eventual merge into main a fast-forward. git merge main (into your feature branch) creates merge commits in your feature branch history, which can clutter it. Most teams that care about clean history prefer rebase here — it's a safe use since you're rebasing your own local branch, not shared commits.

