Git Rebase vs Merge: When to Use Which
Introduction¶
merge and rebase both integrate changes from one branch into another. They
produce different histories, and the choice is mostly about whether you value an
exact record or a readable line.
What merge does¶
A---B---C feature
/ \
D---E---F---G---H main (H is a merge commit with two parents)
git merge feature creates commit H tying the two histories together. A B C
stay exactly as they were. History is truthful but can get tangled with many
parallel branches.
If main hasn't moved since feature branched, Git does a fast-forward —
just moves the pointer, no merge commit. Force a merge commit with
--no-ff (some teams want one per feature for grouping/reverting).
What rebase does¶
D---E---F---G main
\
A'--B'--C' feature (A B C replayed on top of G, as NEW commits)
git rebase main (from feature) replays your commits one at a time onto the
current tip of main. A' B' C' have new hashes — they are new commits. History
is linear, as if you'd started from G all along.
The golden rule¶
Never rebase commits that others have already pulled. Rebasing rewrites
hashes; anyone who had the old commits now has a divergent history and a painful
recovery. Safe to rebase: your own local feature branch that only you have.
Not safe: main, or a shared branch.
Interactive rebase — history cleanup¶
git rebase -i main # or: git rebase -i HEAD~5
An editor lists your commits with actions:
pick a1b2c3d Add user model
squash e4f5g6h fix typo
squash i7j8k9l fix test
reword m0n1o2p Add user API
drop q3r4s5t debug logging
squash/fixup— fold into the previous commit (fixupdiscards the message).reword— change the message.edit— stop to amend the commit contents.drop/ delete the line — remove the commit.- reorder lines — reorder commits.
Result: a clean series of logical commits instead of "wip", "wip2", "actually fix it".
Squash merge¶
git merge --squash feature # then commit once
or the "Squash and merge" button on a PR. The entire branch becomes one
commit on main. Popular because main gets one commit per PR and the messy
branch history disappears. Downside: you lose the individual commits (and
bisect granularity) for that change.
A practical workflow¶
- Branch from
main. - Commit freely while working (messy is fine).
- Before opening / updating the PR:
git fetch && git rebase origin/main, thengit rebase -ito tidy into logical commits. - Merge to
mainvia PR — squash, or a merge commit, per team convention. - Never
git rebaseor force-push a branch someone else is working on.
Resolving conflicts during rebase¶
Rebase applies commits one by one, so conflicts come one commit at a time:
git rebase origin/main
# conflict in commit A'
git status # see conflicted files
# edit files, then:
git add <files>
git rebase --continue # move to the next commit
# ...or
git rebase --abort # give up, back to pre-rebase state
git rebase --skip # drop the current commit (careful)
git config rerere.enabled true makes Git remember conflict resolutions so a
repeated rebase doesn't re-ask.
Verification and troubleshooting¶
git log --graph --oneline --all
git reflog # your safety net — every HEAD position, ~90 days
- "I rebased and lost commits" — they're in
git reflog.git reset --hard HEAD@{5}(orgit branch recover <sha>) gets them back. - Force-push rejected after rebase — expected; the remote branch history
changed.
git push --force-with-lease(safer than--force— refuses if someone else pushed). - Rebase replays a commit that's already upstream — you rebased onto the
wrong base, or the commit was cherry-picked.
git rebase --onto. - Huge conflict on every commit during rebase — a formatting/lint pass touched everything. Rebase onto the commit before that pass, or merge instead this once.
- Team disagrees on merge style — pick one, put it in
CONTRIBUTING.md, enforce with branch protection (GitHub lets you allow only squash, only merge, or only rebase).
Related tools and reading¶
- On-site: Git command reference.
- Related posts: Recovering from common Git mistakes, Trunk-based development.
Stuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.More in DevOps & CI/CD
OpenTelemetry for Distributed Tracing
A trace follows one request across services. OpenTelemetry is the vendor-neutral way to produce them — here's the model, the Collector, and sampling that won't bankrupt you.
October 24, 2026pre-commit Hooks That Actually Help
pre-commit runs checks before a commit lands, catching formatting, secrets, and syntax errors locally. Here's a config that helps without being annoying.
October 24, 2026Chaos Engineering Basics
Chaos engineering is controlled experiments that inject failure to verify your system tolerates it. Here's the method, safe blast radius, starter experiments, and game days.
October 24, 2026