Git: rebase vs merge
Merge preserves history exactly as it happened, with a merge commit. Rebase rewrites your branch's commits onto a new base for a linear history. Both achieve the same end state; they differ in what the log looks like and what's safe to do after.
git merge and git rebase solve the same problem — combining work from one branch into another — but they leave very different histories behind. Knowing which to reach for is mostly about who else can see your branch.
Merge — preserves history
git checkout feature
git merge main
This creates a new merge commit with two parents (your feature branch tip and main's tip). The graph forks and rejoins; every commit you made is preserved exactly as it happened, in the order it happened.
Pros: - Honest log: shows when branches actually diverged and joined - Non-destructive: existing commits keep their hashes - Always safe on shared branches
Cons:
- Lots of merge commits clutter git log on long-running branches
- The first-parent line through history is muddier
Rebase — rewrites history
git checkout feature
git rebase main
This takes every commit on your feature branch since it diverged from main, sets your branch base to main's tip, and replays each commit on top as if you had started from there originally. The result is a linear history: main commits, then your commits, no fork.
Each replayed commit gets a new SHA — the old commits still exist in the reflog briefly, but the visible history has been rewritten.
Pros: - Linear, easy-to-read log - Bisect works cleanly - No merge-commit noise on the main line
Cons: - Each replayed commit must be re-resolved if conflicts exist (one round of conflict resolution per commit, not one for the whole branch) - Dangerous on shared branches — anyone else who pulled the old commits now has a divergent local copy
The golden rule
Never rebase a branch that other people have pulled.
If you rewrite history on a shared branch, your collaborators' local copies still reference the old commits. They'll either get confused merges or, worse, push the old commits back, undoing your rebase.
Safe to rebase: your own feature branch before opening a PR, your own local branch that no one has fetched.
Not safe to rebase: main, master, develop, any branch with origin/ collaborators.
Practical workflow
- Work on a feature branch.
- Periodically
git fetch origin && git rebase origin/mainto keep up to date while the branch is private. - Open a PR.
- After approval, either:
- Merge commit (default) — preserves the PR as a discrete unit in history
- Squash and merge — collapses the whole PR to one commit on
main - Rebase and merge — replays each commit individually onto
main
The choice is project culture. This repo prefers regular merge commits with descriptive messages.
Recovering from a bad rebase
git reflog shows every position your HEAD has been at, including pre-rebase. Find the SHA from before the rebase and git reset --hard <sha> to restore. The reflog is local-only — it can't recover a botched rebase that someone else has already pulled.