Rebase vs merge
Status: đźź© COMPLETE Last updated: 2026-06-20 Plain-English tagline: Two ways to integrate one branch into another. Merge preserves the branching history; rebase rewrites it into a straight line. Both work; people argue.
In plain English
When you bring changes from one branch into another, Git offers two main strategies: merge and rebase. They have the same end result (your branch contains the other branch’s changes) but differ in how the history looks afterward.
Merge creates a new “merge commit” that joins the two branches. History shows the actual branching.
Rebase re-applies your branch’s commits on top of the other branch’s tip, as if you’d written them after the other branch’s recent work. History looks linear.
Different teams prefer different ones. Both are valid. Knowing both lets you read history clearly and pick the right tool for the moment.
Why it matters
You’ll encounter both in any non-trivial Git work:
- Merging is what happens by default in most PRs
- Rebasing is used to “catch up” your branch with main without polluting history
- Squash merging (a third strategy) is the default for many teams now
Picking the right strategy keeps your project’s history readable. Picking the wrong one creates confusion that’s hard to undo.
The visual difference
Imagine two parallel branches:
main: A ── B ── C ── D
\
E ── F (feature)
You want feature to include main’s recent changes (or vice versa). Two approaches:
Merge (creates a merge commit)
main: A ── B ── C ── D ─────── M (main after merge)
\ /
E ── F ── (feature)
Merge commit M has two parents. History records the branching.
Rebase (re-applies commits onto main’s tip)
main: A ── B ── C ── D (main, unchanged)
\
E' ── F' (feature, re-based on D)
Commits E' and F' are new copies of E and F, applied as if you’d written them after D. The original E and F are gone — they’ve been replaced.
The branch is now “linear” relative to main. Clean.
How to do each
Merge
git switch feature
git merge main
# OR if you want main to get feature's changes:
git switch main
git merge featureGit tries to combine. If the branches haven’t diverged messily, you get a merge commit. If they’ve changed the same lines, you get conflicts (see Resolving merge conflicts).
Rebase
git switch feature
git rebase mainGit rewinds your feature commits, fast-forwards to main’s tip, then replays your commits one at a time. Conflicts during rebase get resolved per-commit.
When to use which
Merge — use when:
- It’s a PR merging into a long-lived branch (main, dev). The merge commit records “this feature landed here.”
- Multiple people are working on the same branch. Rebasing rewrites history; collaborators would have to un-rewrite to stay in sync.
- You want the branching structure visible in the log. Some teams value seeing the branch shape.
- The branch is shared / pushed. Rebasing rewrites history; if others have pulled, force-pushing breaks their copies.
Rebase — use when:
- You’re catching up your feature branch with main. “Bring in main’s latest changes” without a merge commit cluttering your branch’s history.
- You haven’t shared the branch yet (or only you work on it). Rebasing is safe.
- You want linear history. Clean reading order.
- Cleaning up commits before merge. Interactive rebase (
git rebase -i) lets you squash, reorder, edit, or drop commits.
A common pattern: rebase locally to keep your feature branch tidy, then merge (or squash-merge) into main. Get the best of both.
The “golden rule” of rebasing
Never rebase commits that other people might have based on.
Rebasing rewrites history — the rebased commits have new IDs. If someone else has the old commits (because you pushed them already, and they pulled), their copy now diverges from yours. Force-pushing your rebased branch will create headaches for them.
Safe rebase rules of thumb:
- Your local branch, not yet pushed → safe to rebase
- Your branch, pushed but you’re the only one using it → safe to rebase (force-push needed)
- Shared / multi-author branch → don’t rebase
- Main / develop → almost never rebase
Interactive rebase — the power tool
git rebase -i HEAD~5Opens an editor showing your last 5 commits with options:
pick a1b2c3 First commit
pick d4e5f6 Second commit
pick g7h8i9 Third commit
pick j0k1l2 Fourth commit
pick m3n4o5 Fifth commit
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the message
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's message
# d, drop = remove commit
# Or move lines to reorder
You can:
- Squash multiple commits into one
- Reword commit messages
- Reorder commits
- Drop commits entirely
- Split a commit (more advanced)
Interactive rebase is the right tool for “I made 6 messy commits on my branch; let me clean them up into 2 logical ones before opening a PR.”
Squash and merge — the third way
GitHub’s “Squash and merge” button is technically a third strategy. It:
- Squashes all of your branch’s commits into one commit
- Applies that single commit on top of main
- Does NOT touch your branch’s local history
Result: main gets one clean commit per merged PR; your local branch history is preserved (until you delete the branch).
For most teams in 2026, squash-merge is the default. Pros:
- Main’s history reads as a sequence of features
- One PR = one commit (easy to revert if needed)
- Forces a coherent merge commit message
Cons:
- Loses fine-grained commit history (gone unless someone reads the PR)
- For very large PRs, the squashed commit is enormous
For your projects, squash-merge is probably the right default.
A concrete example: keeping your branch current with main
You’ve been working on feature/dark-mode for two days. Meanwhile, main has had three commits. You want to bring those in:
With merge:
git switch feature/dark-mode
git merge main
# Possibly resolve conflicts
git pushYour branch’s history now has a merge commit. When you eventually open the PR, the diff shows both your changes AND the merged-in main changes (until GitHub filters to “only changes unique to your branch”).
With rebase:
git switch feature/dark-mode
git rebase main
# Possibly resolve conflicts per-commit
git push --force-with-lease # branch history rewritten — force-push neededYour branch’s history is now: main’s three new commits + your commits, in a clean line. When you open the PR, the diff shows ONLY your changes.
Most developers prefer rebase here. The PR is cleaner; main’s history is unchanged.
Common gotchas
-
Force-push after rebase. Rebase rewrites commits, so the remote has the old ones. You need
git push --force-with-lease(safer than--force) to update the remote. -
Conflicts during rebase are per-commit. A 10-commit rebase can ask you to resolve conflicts 10 times. Often easier to merge in this case.
-
The “lost” commits aren’t really lost. Git’s reflog keeps every commit you’ve made for ~90 days.
git reflogshows them; you can recover withgit reset --hard <hash>. -
git pulldefaults to merge. This creates merge commits in your local branches. To make it default to rebase:git config --global pull.rebase true. -
Rebasing shared branches breaks teammates. Don’t.
-
Rebase changes commit hashes. Anything referencing the old hashes (CI links, issue comments) now points at “missing” commits.
-
Squash-merge loses the individual commit history. The squashed message is what survives on main. Write it well — it’s permanent.
-
You can mix strategies on the same project. Squash for features, regular merge for big-bang integrations, rebase to keep branches clean. Pick per situation.
-
GitHub’s “Update branch” button in PRs offers either “Merge main into branch” or “Rebase onto main.” Choose the one that fits your strategy.
-
Editor opens during interactive rebase. Your default Git editor (usually Vim) opens. If you’re not comfortable in Vim,
git config --global core.editor "code --wait"makes VS Code the editor.
See also
- Branches & merging đźź©
- Git basics đźź©
- Resolving merge conflicts đźź©
- Git rescue moves đźź©
- Pull requests đźź©