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
📋 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.
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.