Git Merge vs Rebase vs Squash: Integration Strategy Compared
Fast recall
Quick revision
Core answer
develop/main integration in GitFlow. Use rebase for keeping your feature branch's history linear before opening a PR — cleaner review, no merge bubbles. Use squash-and-merge for landing PRs into main — main has one commit per feature, easy revert, clean history. The rule: rebase locally, squash when landing, never rebase shared branches.Practical Git workflow understanding and history hygiene
Rebasing a branch that someone else has based their work on — they get a divergent history that's painful to reconcile
Discuss the 'rebase locally, merge publicly' rule, semantic commits in main, the bisect implications of squash-and-merge, and merge-queue (graphite/aviator) as the modern evolution
Interview-ready guide
Interview answer
Three Git integration strategies that produce three different history shapes. The right choice depends on what you value: full history preservation, linear readability, or atomic main commits.
Merge commit — preserves branch history
- Creates an explicit merge commit joining the branches; all feature commits remain visible; main history becomes non-linear with branch bubbles
- Best for long-lived shared branches where the full commit history tells a meaningful story; typical in GitFlow integrating release branches
Rebase — linear local history
- Replays your branch commits one at a time on top of latest main; no merge commit; result looks like you did the work directly on main
- Cleanest to read, easiest to bisect; the catch: rewrites commit hashes, so never rebase a branch someone else has based work on
- Use locally to keep your feature branch on top of main before opening or updating a PR
Squash-and-merge — one commit per feature in main
- Collapses all PR commits into one new commit on main; the squashed commit message becomes the long-lived history
- Cleanest main history, easy to revert one feature, but loses sub-commit granularity for bisect within a single PR
- The modern default for landing PRs in trunk-based and GitHub-flow teams
The recommended modern workflow
- Rebase locally to clean your branch's history before review; interactive rebase to combine WIP commits, fix messages, reorder for logical flow
- Use
git push --force-with-lease— safer than--force, refuses to overwrite if someone pushed to your branch - Open the PR with clean history; reviewer can follow each commit as a logical step
- Squash-and-merge into main; main has linear, atomic history; one commit per merged feature; easily revertible
Trade-offs to call out
- Squash-and-merge loses bisect granularity within a feature — mitigation is keeping PRs small, one logical change per PR
- Merge commits make bisect noisier; squash produces the cleanest bisect experience
- Long-lived branches accumulate merge conflicts; rebase weekly to keep conflicts small; the longer you wait, the worse the rebase
The forbidden actions
- Never rebase a branch others have based their work on; their commits land on a stale base and reconciling is painful
- Never force-push to main or shared branches; use
--force-with-leaseonly on your own feature branches - Avoid
git pulldefaulting to merge; configurepull.rebase = truesogit pullrebases instead of creating a merge commit on every sync
The modern evolution
- Merge queues like Graphite, Aviator, and GitHub's built-in merge queue serialize merges into main; they prevent the race condition where two PRs each pass CI but the combined result on main breaks
- Worth adopting in high-volume repos where multiple PRs land per hour
Cultural note
- The rebase-vs-merge debate is religious; what matters more than the specific choice is consistent practice within a team; pick a convention, document it, enforce via tooling like branch protection and squash-only merges
Git Merge vs Rebase vs Squash: Integration Strategy Compared
Direct Answer
Use merge for shared/long-lived branches where preserving history matters — default for develop/main integration in GitFlow. Use rebase for keeping your feature branch's history linear before opening a PR — cleaner review, no merge bubbles. Use squash-and-merge for landing PRs into main — main has one commit per feature, easy revert, clean history. The rule: rebase locally, squash when landing, never rebase shared branches.
Common Mistakes & Pitfalls
Rebasing a branch that someone else has based their work on — they get a divergent history that's painful to reconcile
Check your understanding
Answer these without looking back at the article.
- What does `git push --force-with-lease` do that `--force` doesn't?
- How does GitHub's merge queue prevent the 'green PR breaks main' race condition?
- What are Conventional Commits and why use them with squash-and-merge?
- When does an interactive rebase become dangerous?
Related interview prep categories
1Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.