The Core Difference
Both git merge and git rebase integrate changes from one branch into another. The difference is in how they change history.
Merge creates a new "merge commit" that has two parents — it records that branches were joined.
Rebase moves your commits to a new base point — it rewrites history to look like you branched off a later point.
# Before: feature branched off old main
main: A - B - C
\
feature: D - E
# After merge: creates merge commit M
main: A - B - C - M
\ /
feature: D - E
# After rebase: moves D, E to tip of main
main: A - B - C - D' - E'
When to Use Merge
1. Integrating completed feature branches into main
git checkout main
git merge feature/user-auth --no-ff
Use --no-ff (no fast-forward) to always create a merge commit, even if a fast-forward is possible. This preserves the history that these commits were a feature branch.
Why: The merge commit is documentation. "These commits were a feature that was finished and merged on this date."
2. When history preservation matters
In open source projects, merge commits show the provenance of contributions. You can always git log --merges to see when features landed.
3. When your teammates have already pulled the branch
If others are working on the same branch, never rebase it. Rebase rewrites commit hashes, which will cause conflicts for everyone who has pulled the old commits.
When to Use Rebase
1. Keeping your feature branch up to date with main
git checkout feature/my-feature
git rebase main
Instead of polluting your branch history with "Merge main into feature" commits, rebase moves your commits to the tip of main. When you eventually merge, the history is clean.
2. Before opening a PR
Rebase your feature branch onto the latest main before creating the PR:
git fetch origin
git rebase origin/main
git push --force-with-lease # only safe since this is your branch
The PR reviewer sees only your changes, not a tangle of merge commits.
3. Interactive rebase: cleaning up your commits
git rebase -i HEAD~5 # rewrite the last 5 commits
This opens an editor where you can:
pick— keep the commitsquash— combine with previous commitreword— change the commit messagedrop— remove the commit entirely
Use this to turn "WIP", "fix typo", "fix typo again" into one clean commit before merging.
The Golden Rule
Never rebase commits that have been pushed to a shared branch.
Rebasing rewrites commit hashes. If someone else has pulled those commits and you rebase, their history diverges from yours. Pushing requires a force push, which can overwrite their work.
Safe to rebase: commits that only exist in your local or personal branch.
Never rebase: commits that others have pulled from a shared branch (main, develop, etc.).
Practical Workflow
# Start feature
git checkout -b feature/my-feature
# ... do work, commit frequently ...
# Keep up to date with main (clean, no merge commits)
git fetch origin
git rebase origin/main
# Clean up commits before PR
git rebase -i origin/main # squash WIP commits
# Push (force-with-lease is safer than force)
git push origin feature/my-feature --force-with-lease
# After PR approval: merge into main with merge commit
git checkout main
git merge feature/my-feature --no-ff
--force-with-lease vs --force
When you rebase and push, you need to force push. Always use --force-with-lease:
git push --force-with-lease # Safe: fails if remote has commits you haven't seen
git push --force # Dangerous: overwrites whatever is there
--force-with-lease will fail if someone else pushed to the branch since your last fetch. It prevents you from accidentally overwriting their work.
Key Takeaways
- Merge preserves history, creates merge commits, safe on shared branches
- Rebase rewrites history, creates linear log, only safe on your own branches
- Never rebase commits others have already pulled
- Use interactive rebase (
-i) to clean up commits before PRs - Always use
--force-with-leaseinstead of--forcewhen force-pushing