Git workflow comparison
Git merge vs rebase
"Should I merge or rebase?" is one of the most discussed Git questions of all time. Both produce the same code; they differ only in how the commit history reads afterwards.
TL;DR
- Merge joins two branches with a new commit that has two parents. History preserves the fact that work happened in parallel.
- Rebase replays your commits on top of the target branch. History looks linear, as if you'd been working on the target branch all along.
- Rule of thumb: rebase your private feature branch before opening a PR for a clean review; merge shared branches.
Side-by-side comparison
| Aspect | Merge | Rebase |
|---|---|---|
| Final code | Same | Same |
| History shape | Branched graph with merge commits | Linear, no merge commits |
| Commit SHAs | Unchanged — preserves originals | Rewritten — new SHAs for replayed commits |
| Safe on shared branches | Yes | No — rewriting public history breaks others |
| Conflict resolution | Once, at merge time | Per commit being replayed |
| Bisect / blame | Merge commits can obscure | Clean — every commit stands alone |
| Default in | git pull (until you change it) | git pull --rebase |
| Reversibility | Easy: git revert -m 1 | Hard once pushed; reflog only locally |
History visualised
Merge
* m (main) Merge feature into main
|\
| * c (feature) Add user avatar
| * b (feature) Refactor profile
|/
* a (main) Initial work Rebase
* c' (main) Add user avatar
* b' (main) Refactor profile
* a (main) Initial work
# b and c are replayed as b' and c'
# with new SHAs; no merge commit Workflows side-by-side
Merge workflow
git checkout feature
# ...work, commit, commit, commit
git checkout main
git pull
git merge feature
# Creates a merge commit
git push Rebase workflow
git checkout feature
git fetch origin
git rebase origin/main
# Replays your commits on top of main
# Resolve any conflicts per commit
git push --force-with-lease
# (force needed because SHAs changed) When to use merge
- Shared / long-running branches. Other people have already pulled — rewriting history would break them.
- You want to preserve "feature group" context. Merge commits let you see "everything from PR #1234" as a unit.
- Releases / hotfixes. Merging release branches into main and develop is the standard GitFlow pattern.
- Team is junior with Git. Merge is more forgiving; rebase failures are harder to recover from.
When to use rebase
- Cleaning up your private feature branch before a PR. Squash WIP commits, fix messages, reorder logically.
- Keeping main linear. Many teams configure `git pull --rebase` and squash-merge PRs for a clean main history.
- Picking up upstream changes during long work. Rebase frequently onto main to surface conflicts early, one commit at a time.
- Open-source contributions. Maintainers often request a rebase to integrate cleanly without merge commits.
English phrases engineers use
Merge conversations
- "Let's merge it in — the PR is approved."
- "Use --no-ff so the feature branch shows up in the graph."
- "This is a merge conflict on package.json — both branches added dependencies."
- "Revert the merge commit with
git revert -m 1 <sha>." - "We do squash merges on main — one commit per PR."
Rebase conversations
- "Rebase onto main before opening the PR — your branch is 47 commits behind."
- "This is an interactive rebase — I'm squashing the WIP commits."
- "You'll need to force-push after rebasing — use
--force-with-lease." - "Don't rebase shared branches — you'll rewrite everyone else's history."
- "The rebase has 12 commits to replay — resolve conflicts step by step."
Quick decision tree
- Branch is private and not pushed → Rebase freely
- Branch is shared with the team → Merge (or coordinate before rebase)
- Cleaning up local commits before PR → Interactive rebase (git rebase -i)
- Bringing in upstream changes during long work → Rebase onto main
- Integrating PR into main → Team policy (merge, squash, or rebase merge)
- Releasing / GitFlow → Merge with --no-ff
- Open-source PR, maintainer requests it → Rebase
- Unsure → Merge (safer)
Frequently asked questions
What is the practical difference between git merge and git rebase?
Merge takes two branches and joins them with a new "merge commit" that has two parents — your branch history shows that two lines of work came together. Rebase takes your commits and replays them on top of the target branch one by one, producing a single linear history with no merge commit. Same final code; different commit history.
When should I use git merge?
Use merge when you want to preserve the exact history of how work happened, when the branch is shared with other people (rebasing rewrites history and forces everyone else to re-fetch), or when your team's policy is to keep all feature work visible as a separate "stream" on the graph.
When should I use git rebase?
Use rebase to keep a clean, linear history before merging — typically on your private feature branch before opening a pull request. Rebase is also useful for interactive history cleanup: squashing WIP commits, reordering, or editing commit messages.
Is rebase dangerous?
Only on shared branches. Rebase rewrites commit SHAs, so anyone who has already pulled the original commits will have a divergent history. The golden rule: never rebase commits that exist on a branch other people are using. Rebasing your local branch before opening a PR is safe.
What is git merge --no-ff?
A "no fast-forward" merge forces Git to create a merge commit even when the target branch could be moved forward without one. Teams use --no-ff to keep an explicit record of every feature branch in the history graph, so you can later see "everything that came from PR #1234" as a group.
What is git pull --rebase?
Instead of creating a merge commit when you pull, Git rebases your local commits on top of the freshly-fetched remote commits. Most teams configure this as the default (git config --global pull.rebase true) because it keeps history linear without thinking about it.
How do I undo a bad rebase?
Use the reflog: git reflog shows every state of HEAD, including pre-rebase. Find the SHA before the rebase, then git reset --hard <that-sha>. Git keeps reflog entries for 90 days by default, so even "lost" commits are recoverable.