Git: rebase vs merge — a practical rule of thumb
Every team argues about this. The rule that ends the argument:
Rebase commits that only you have seen. Merge commits that anyone else has already seen.
Rebase rewrites history — it takes your commits and replays them on top of a new base, creating new commits with new hashes. If a hash you rewrote exists on someone else’s machine, you now have two divergent histories and a bad afternoon. If nobody ever pulled those hashes, rewriting them costs nothing.
The daily workflow
On a feature branch, keep up to date with main:
git fetch origin
git rebase origin/main
No merge bubbles, no “Merge branch ‘main’ into feature” commits, and your diff review stays clean. When the branch is done and reviewed:
git checkout main
git merge --ff-only feature # or push and use a PR button
The two rebes that matter
git rebase -i HEAD~5 — interactive rebase of your last five commits:
squash merges a commit into the previous one, reword fixes the message,
drop removes it. Run it before pushing, on commits only you have.
git rebase --onto new-base old-base branch — moves a whole range of
commits from one base to another. Rare, but when you need it, nothing else
works.
When you’ve already pushed
You rebased, then noticed the branch was already on the remote. If it’s truly your private branch:
git push --force-with-lease
--force-with-lease instead of --force: it refuses to overwrite the
remote if someone pushed after your last fetch. That one flag has saved me
at least twice.
The honest trade-off
Merge commits are not a mistake — they record “these two lines of work
happened in parallel, and here is where they met”. For released branches
(main, release/*) that record is worth keeping. Rebase is a
history-cleaning tool for the workshop, not the warehouse.