Git Rebase Explained — When to Use It and When to Avoid It
Introduction
If you've spent any time working with Git in a team environment, you've probably encountered the rebase vs merge debate. Some teams swear by rebase for its clean, linear history. Others avoid it entirely after a painful incident involving rewritten shared commits.
The truth is, rebase is a powerful tool — but it comes with real risks if misused. This guide breaks down exactly what rebase does under the hood, when it's the right choice, and when you should absolutely stay away from it.
What Is Git Rebase?
At its core, git rebase takes a series of commits from one branch and replays them on top of another branch. Unlike merge, it doesn't create a merge commit — instead, it rewrites your branch's commit history so that it appears as though you started your work from the latest commit on the target branch.
Basic Rebase
# You're on feature/login
git checkout feature/login
# Rebase your commits onto the latest main
git rebase main
What happens here: Git finds the common ancestor of feature/login and main, temporarily removes your branch's commits, moves the branch pointer to the tip of main, and then replays your commits one by one on top. Each replayed commit gets a new hash — this is the critical detail that makes rebase both powerful and dangerous.
Rebase vs Merge — The Core Difference
Both commands integrate changes from one branch into another, but they do it in fundamentally different ways.
Merge
git checkout main
git merge feature/login
Merge preserves the full branching history and creates a merge commit that ties the two branches together. Your Git log will show exactly when branches diverged and converged. The result is accurate but can get noisy with many branches.
Rebase
git checkout feature/login
git rebase main
# Then fast-forward main
git checkout main
git merge feature/login
Rebase rewrites your feature branch so it looks like you started from the current tip of main. The resulting history is linear and easy to read — no merge commits cluttering the log. But the trade-off is that you've rewritten history: every rebased commit has a new SHA.
Quick Comparison
| Aspect | Merge | Rebase |
|---|---|---|
| History | Non-linear, shows true branch topology | Linear, clean single-line history |
| Merge commits | Yes | No |
| Rewrites history | No | Yes — new commit hashes |
| Safe for shared branches | Yes | No — can cause conflicts for collaborators |
| Best for | Shared/public branches | Local/private feature branches |
Interactive Rebase
Interactive rebase is where things get really useful. It lets you edit, squash, reorder, or drop commits before applying them. This is your go-to tool for cleaning up messy commit history before opening a pull request.
# Rebase the last 4 commits interactively
git rebase -i HEAD~4
This opens your editor with something like:
pick a1b2c3d Add login form UI
pick e4f5g6h Fix typo in login component
pick i7j8k9l Add form validation
pick m0n1o2p Fix validation edge case
Available Commands
| Command | What It Does |
|---|---|
pick |
Keep the commit as-is |
reword |
Keep changes but edit the commit message |
squash |
Combine with the previous commit, merge messages |
fixup |
Combine with previous commit, discard this message |
drop |
Remove the commit entirely |
edit |
Pause rebase to amend the commit |
Practical Example: Squashing Fix Commits
pick a1b2c3d Add login form UI
fixup e4f5g6h Fix typo in login component
pick i7j8k9l Add form validation
fixup m0n1o2p Fix validation edge case
This collapses the typo fix into the UI commit and the edge case fix into the validation commit — leaving you with two clean, meaningful commits instead of four.
When to Use Rebase
1. Keeping your feature branch up to date with main.
Instead of merging main into your feature branch repeatedly (which creates noise), rebase your branch onto main to stay current with a clean history.
git checkout feature/login
git fetch origin
git rebase origin/main
2. Cleaning up commits before a pull request.
Use interactive rebase to squash WIP commits, fix typos, and craft a clean, reviewable commit history. Your reviewers will thank you.
3. Maintaining a linear project history.
If your team values a clean git log where you can read the project's evolution top-to-bottom without merge commit noise, rebase is the way to achieve that.
4. Before merging a short-lived feature branch.
Rebase onto main, resolve any conflicts in the context of your own changes, then fast-forward merge. This puts the conflict resolution burden on the feature author — where it belongs.
When to Avoid Rebase
1. On shared or public branches. Ever.
If other people have pulled your branch, rebasing rewrites commits they already have. When they try to pull again, Git sees a completely different history and chaos ensues — duplicate commits, confusing conflicts, and a lot of frustrated messages in Slack.
The golden rule: never rebase commits that exist outside your local repository.
2. On main, develop, or any long-lived branch.
These branches are shared by definition. Use merge (or squash-merge) to integrate work into them. Rewriting their history affects every contributor.
3. When you need an accurate historical record.
In regulated environments or when debugging complex integration issues, knowing exactly when branches diverged and merged can be invaluable. Merge preserves this; rebase erases it.
4. When you're not comfortable resolving conflicts commit-by-commit.
During a rebase, conflicts can appear at every replayed commit. If your branch has 15 commits and conflicts with main, you might need to resolve conflicts 15 separate times. A merge handles this in one pass. If you're not confident with the process, merge is the safer choice.
⚠️ WARNING: If you've already pushed your branch and then rebase, you'll need to git push --force-with-lease to update the remote. This overwrites remote history. Always use --force-with-lease instead of --force — it checks that no one else has pushed to the branch since your last fetch, preventing you from accidentally overwriting their work.
# Safer force push — fails if remote has new commits you haven't seen
git push --force-with-lease origin feature/login
Recovering From a Bad Rebase
Messed up a rebase? Don't panic. Git's reflog tracks every position of HEAD, even across rebases.
Undo the Entire Rebase
# View your recent HEAD history
git reflog
# Find the entry right before the rebase started, then:
git reset --hard HEAD@{5}
# Or use the ORIG_HEAD shortcut (points to HEAD before the rebase)
git reset --hard ORIG_HEAD
Abort a Rebase in Progress
# If you're in the middle of a rebase and want to bail out:
git rebase --abort
This restores your branch to exactly where it was before you started the rebase. No harm done.
Best Practices
After years of working with rebase across different teams and codebases, here are the patterns that consistently work well:
Rebase local, merge shared. This is the single most important rule. Rebase your own unpushed work freely. Once commits are shared, use merge.
Rebase before opening a PR, not after. Clean up your history with interactive rebase before requesting reviews. Once reviewers have commented on specific commits, rebasing makes their comments point to orphaned hashes.
Use --force-with-lease, never --force. If you must force push after a rebase, the lease flag adds a critical safety check.
Keep branches short-lived. The longer a branch lives, the more it diverges from main, and the more painful any rebase becomes. Aim for branches that last days, not weeks.
Communicate with your team. If your workflow involves rebasing pushed branches (some teams do this), make sure everyone knows. A quick "I'm rebasing the feature branch" message prevents confusion.
Configure pull to rebase by default. This avoids unnecessary merge commits when pulling remote changes:
# Set globally
git config --global pull.rebase true
# Or per-repo
git config pull.rebase true
Conclusion
Git rebase isn't something to fear — it's something to understand. When used on local branches, it gives you a clean, readable history and the ability to present your work as a polished, logical sequence of changes. When misused on shared branches, it creates real problems for your team.
The distinction is simple: if the commits are only yours, rebase freely. If anyone else has them, don't. Internalize that rule, get comfortable with interactive rebase for PR cleanup, and you'll have a workflow that's both clean and safe.
And if something goes wrong — git reflog has your back. It always does.