Git Merge vs Rebase: When to Use Which (and Why It Matters)
If you've ever stared at your terminal after a git pull, wondering why your commit history looks like a bowl of spaghetti, you're not alone. The merge vs rebase debate is one of the most common sources of confusion for developers—and for good reason. Both commands integrate changes from one branch into another, but they do it in fundamentally different ways that can dramatically affect your project's history.
Here's the thing: neither approach is universally "right." They're tools with different trade-offs, and knowing when to use each one is a skill that separates junior developers from seasoned pros. Let's demystify both approaches so you can make confident decisions.
The Core Difference
At a high level, both git merge and git rebase do the same thing: they integrate changes from one branch into another. But how they do it—and what happens to your commit history—is where they diverge.
Merge: Preserve Everything
When you merge, Git creates a new commit (a merge commit) that has two parents. It combines the histories of both branches without modifying existing commits.
Before merge: After merge:
A---B---C feature A---B---C-------M feature
/ / \
D---E---F main D---E---F---------M main
That M is your merge commit. It says, "These two histories came together here." Your original commits (A, B, C, D, E, F) remain untouched with their original SHAs.
Rebase: Rewrite History
Rebase takes a different approach. It replays your commits on top of the target branch, effectively rewriting history.
Before rebase: After rebase:
A---B---C feature A'--B'--C' feature
/ /
D---E---F main D---E---F main
Notice that A, B, and C became A', B', and C'. They're new commits with new SHAs. The original commits no longer exist in your branch's history—it's as if you started your feature branch from the latest main all along.
When to Use Merge
Use Cases
1. Integrating feature branches into main or develop
When your feature is complete and ready to be merged into a shared branch, git merge --no-ff (no fast-forward) is often the safest choice. It preserves the context of your feature as a cohesive unit.
2. Documenting when and why branches converged
Merge commits act as historical markers. They tell future you (or future teammates) exactly when two streams of work came together.
3. Collaborating on shared branches
If multiple developers are working on the same branch, merge is your friend. It doesn't rewrite history, so you won't step on anyone's toes.
Code Example
# Switch to your target branch
git checkout main
# Merge your feature branch with a merge commit
git merge --no-ff feature/login
# Or let Git decide (fast-forward if possible)
git merge feature/login
Pros and Cons
| Pros | Cons |
|---|---|
| Non-destructive: doesn't rewrite history | Can create "cluttered" history with many merge commits |
| Preserves complete context of when branches merged | Makes git bisect harder (bisect may stop at merge commits) |
| Safe for shared branches | Can be confusing for beginners to read history |
| Easy to understand and reason about | Commit graph can become complex |
When to Use Rebase
Use Cases
1. Cleaning up local feature branches before a PR
Before you push your feature branch for review, rebasing onto the latest main ensures your changes integrate cleanly and your history is linear.
2. Squashing commits
Want to turn 15 "fix typo" commits into one clean "Implement user authentication" commit? Interactive rebase (git rebase -i) is your tool.
3. Maintaining a linear history
Some teams prefer a clean, linear history without merge commits. Rebase makes that possible.
Code Example
# Update your target branch
git checkout main
git pull origin main
# Switch to your feature branch
git checkout feature/login
# Rebase onto the updated main
git rebase main
For interactive rebase (to squash, reorder, or edit commits):
# Rebase last 5 commits interactively
git rebase -i HEAD~5
This opens an editor where you can mark commits to be squashed, edited, or dropped.
Pros and Cons
| Pros | Cons |
|---|---|
| Creates linear, readable history | Destructive: rewrites commit SHAs |
Makes git bisect and git log cleaner |
Dangerous on shared/public branches |
| Allows squashing and cleaning up commits | Requires force-push after rebase |
| No merge commit clutter | Can cause conflicts multiple times (once per commit) |
The Golden Rule: Never Rebase Public Branches
This is the most important thing you'll learn about rebase, so read it twice:
Never rebase branches that others have pulled.
Here's why: rebase creates new commits with new SHAs. If someone else has pulled your branch and based work on commits A-B-C, and you rebase to create A'-B'-C', their repository still has the old commits. When they try to pull again, Git will be confused—those commits don't exist on the remote anymore.
The result? Duplicate commits, merge conflicts, and a very sad team.
What happens when you rebase a shared branch:
Your teammate's history: A---B---C---D (their work based on your commits)
Your history after rebase: A'--B'--C' (you rewrote history)
Git sees: "Hey, A-B-C and A'-B'-C' look similar but have different SHAs!"
Result: Both sets of commits exist → merge hell
The rule: If a branch is local and you haven't pushed it yet, rebase away. If others have pulled it, merge instead.
Real-World Workflow Example
Let's walk through a realistic scenario. You're working on a feature branch, and main has moved forward. Here's how both approaches play out:
Scenario: Feature Branch Behind Main
Your feature branch: A---B---C (feature)
/
Main branch: D---E---F---G---H (main)
Approach 1: Merge
git checkout feature
git merge main
Result:
A---B---C-------M feature
/ /
D---E---F---G---H main
You get a merge commit M. Your history now shows exactly when your feature caught up with main. But your feature branch history now has an extra commit, and the graph is more complex.
Approach 2: Rebase
git checkout feature
git rebase main
Result:
A'--B'--C' feature
/
D---E---F---G---H main
Your commits A-B-C are replayed on top of H. Your history is linear and clean. But note: A-B-C no longer exist—they've been replaced by A'-B'-C'.
Which to Choose?
- Use rebase if the branch is local (not pushed) and you want a clean PR.
- Use merge if the branch is shared or you want to preserve the exact history.
FAQ / Troubleshooting
Q1: "I rebased and now I can't push!"
You're seeing:
! [rejected] feature -> feature (non-fast-forward)
This is expected. Rebase rewrote history, so your local branch diverged from the remote. You need to force-push:
git push --force-with-lease origin feature
Use --force-with-lease instead of --force—it's safer and won't overwrite others' work if the remote has commits you don't know about.
Q2: "Should I use rebase or merge for git pull?"
By default, git pull does a merge. If you prefer rebase:
git pull --rebase origin main
Or set it as your default:
git config --global pull.rebase true
Q3: "I rebased and have conflicts in every commit. Help!"
This happens when rebasing onto a branch with conflicting changes. You can:
- Resolve each conflict as it arises (tedious but accurate)
- Or use
git rebase --abortto cancel and merge instead
Q4: "Can I rebase after I've already pushed?"
Technically, yes—but you'll need --force-with-lease. Whether you should depends on whether others have pulled your branch. If you're working alone on the branch, go ahead. If it's shared, merge instead.
Q5: "What about git merge --squash?"
git merge --squash takes all commits from your feature branch and squashes them into a single commit on your target branch, without creating a merge commit. It's useful when you want a clean, single-commit integration but don't want to rebase.
Conclusion
Merge and rebase aren't enemies—they're tools for different jobs:
Use
git mergewhen you want to preserve history, work collaboratively on shared branches, or document when work converged. It's safe, non-destructive, and beginner-friendly.Use
git rebasewhen you're working on a local feature branch and want a clean, linear history before submitting a PR. It's powerful but requires understanding its destructive nature.
The golden rule is simple: never rebase public branches. If others have pulled it, merge. If it's local and personal, rebase away.
And remember: a messy history with working code beats a "perfect" history with broken merges. Choose the approach that serves your team, your workflow, and your sanity.
Top comments (0)