Git Merge vs Rebase
Fast recall
Quick revision
Core answer
Whether you understand both approaches, their trade-offs, and — critically — when NOT to use rebase (never on shared/public branches).
Saying one is better than the other. Both serve different purposes. The dangerous mistake is rebasing a branch that other people have already pulled.
Explain the golden rule of rebase: 'never rebase commits that have been pushed to a shared branch'. Mention interactive rebase (git rebase -i) for squashing commits before a PR.
Interview-ready guide
Interview answer
Picture a river (main branch) with a tributary (your feature branch) splitting off and rejoining.
git merge is like the tributary flowing back into the river at a junction point. The river now has a Y-shaped history — you can see where the tributary split off and where it came back. The merge commit is that junction point.
After a merge, history looks like main moving A, B, C, then a merge commit M that ties in feature commits D, E, and F—you still see the branch point and the join.
git rebase is like picking up the tributary and moving its starting point downstream. Instead of branching from commit B, your commits now sit on top of C — as if you started later. The history is a straight line.
Before a rebase, feature might sit as A, B, D, E while main advanced to C. After rebasing feature onto main, you get a straight line A through C, then replayed commits—same changes as D and E but new identities—so it reads as if you had always built on top of C.
When to use merge: When integrating finished work into main/develop. The merge commit documents 'feature X was added' and preserves the full development history.
When to use rebase: Before opening a PR — rebase your feature branch onto the latest main to get a clean history. Also use interactive rebase (git rebase -i) to squash messy commits into clean, logical ones.
The golden rule: Never rebase commits that exist on a remote branch other people use. Rebasing rewrites commit hashes. If someone else already pulled those commits, their history diverges from yours — causing chaos.
Git Merge vs Rebase
Direct Answer
Both integrate changes from one branch into another, but differently. git merge creates a new 'merge commit' that combines two branch histories — the history shows exactly when and where the branches diverged and joined. git rebase replays your commits on top of the target branch — creating a clean, linear history as if you developed on the latest main from the start. Use merge for shared branches (main, develop), use rebase for cleaning up local feature branch history before merging.
Deep Dive & Architecture
Merge vs Rebase: The Visual Mental Model
The core difference is about history shape. Merge preserves the branching timeline as it actually happened. Rebase rewrites history to be a straight line. Neither is objectively better — they serve different goals.
Merge Creates a Junction Point
When you git merge feature into main, Git creates a new merge commit with two parents. The history shows exactly when the branch diverged and when it rejoined:
main: A ─ B ─ C ─────── M (merge commit, parents: C and F)
\ /
feature: D ─ E ─ F
The merge commit M has two parents: C (where main was) and F (where feature ended). This is a permanent record that work happened in parallel. git log --graph shows the branching visually.
Fast-forward merge is a special case: if main hasn't moved since the branch was created, Git just moves the main pointer forward — no merge commit needed:
Before: main → A ─ B feature → A ─ B ─ C ─ D
After: main → A ─ B ─ C ─ D (main pointer moved to D)
Use git merge --no-ff feature to force a merge commit even when fast-forward is possible — this preserves the record that a feature branch existed.
Rebase Replays Commits on a New Base
When you git rebase main while on a feature branch, Git takes your feature commits and replays them one by one on top of the latest main commit. The result is a linear history:
Before rebase:
main: A ─ B ─ C
feature: A ─ B ─ D ─ E
After rebase:
main: A ─ B ─ C
feature: A ─ B ─ C ─ D' ─ E'
Notice D' and E' — these are new commits with new SHA hashes. They have the same content changes as D and E, but different parents. The original D and E still exist in Git's object store (recoverable via git reflog) but are no longer referenced by any branch.
The Golden Rule: Never Rebase Shared History
Rebasing rewrites commit hashes. If another developer has already pulled the original commits (D and E), and you force-push the rebased versions (D' and E'), their local history diverges from the remote. They'll see confusing merge conflicts or duplicate commits when they pull.
Your repo (after rebase): A ─ B ─ C ─ D' ─ E'
Their repo (before rebase): A ─ B ─ D ─ E
When they pull: "diverged — your branch and origin have diverged"
Safe to rebase: your own feature branch that nobody else has pulled. Never rebase: main, develop, release branches, or any branch others are working on.
Interactive Rebase: Cleaning Up Before a PR
git rebase -i HEAD~5 opens an editor showing your last 5 commits. You can:
pick abc1234 Add user model
squash def5678 Fix typo in user model → combine into previous
squash ghi9012 Another typo fix → combine into previous
pick jkl3456 Add user authentication
reword mno7890 WIP: fix auth bug → change commit message
This turns 5 messy commits into 2 clean, logical commits. Your PR reviewer sees a coherent story instead of your development journey.
Common interactive rebase commands:
pick— keep the commit as-issquash— merge into the previous commit, combine messagesfixup— merge into previous, discard this commit's messagereword— keep changes, edit the commit messagedrop— delete the commit entirelyedit— pause rebase at this commit for manual changes
The Hybrid Workflow (Industry Standard)
Most production teams use both merge and rebase:
# 1. Create feature branch
git checkout -b feature/user-auth
# 2. Work, commit, push
git add . && git commit -m "Add JWT auth"
git push origin feature/user-auth
# 3. Before opening PR: rebase onto latest main
git fetch origin
git rebase origin/main
# Resolve any conflicts per-commit
# 4. Force-push the rebased branch (safe — it's YOUR branch)
git push --force-with-lease origin feature/user-auth
# 5. Open PR, get reviews
# 6. Merge PR into main (on GitHub: "Create a merge commit")
# This creates a merge commit on main — visible history of the feature
--force-with-lease is safer than --force: it refuses to push if someone else pushed to the same branch since your last fetch. This prevents accidentally overwriting a colleague's work.
Resolving Conflicts: Merge vs Rebase
In a merge conflict, you resolve everything at once — all conflicts from all divergent commits appear in one resolution step.
In a rebase conflict, you resolve per-commit. If 3 of your 5 commits conflict with main, you resolve each one separately as Git replays them. This is more granular but can be tedious. If it gets messy: git rebase --abort takes you back to before the rebase started — no damage done.
Code Example
# MERGE workflow — preserves history (use for shared branches)
git checkout main
git merge feature/login
# Creates a merge commit. History shows the branch.
# REBASE workflow — clean linear history (use for your feature branch)
git checkout feature/login
git rebase main
# Your commits are replayed on top of latest main.
# Then fast-forward merge:
git checkout main
git merge feature/login # now it's a fast-forward, no merge commit
# INTERACTIVE REBASE — clean up before PR
git rebase -i HEAD~3
# Opens editor. You can:
# pick — keep commit as-is
# squash — merge commit into previous
# reword — change commit message
# drop — delete commit entirely
# RESOLVING MERGE CONFLICTS
# When both branches edited the same line, Git can't auto-merge:
git merge feature/login
# CONFLICT (content): Merge conflict in src/Auth.java
# The file now contains conflict markers:
# <<<<<<< HEAD
# String token = generateJWT(user);
# =======
# String token = createSession(user);
# >>>>>>> feature/login
# Fix: choose the correct code, remove markers, then:
git add src/Auth.java
git commit # completes the merge
# RESOLVING REBASE CONFLICTS
# Same concept, but you resolve per-commit (not all at once):
git rebase main
# CONFLICT in commit D
git add src/Auth.java
git rebase --continue # move to next commit
# CONFLICT in commit E
git add src/Auth.java
git rebase --continue # done
# ABORT if it gets messy:
git rebase --abort # go back to before the rebase started
# THE HYBRID WORKFLOW (most common in production teams)
git checkout feature/login
git rebase main # clean up: replay on latest main
git push --force-with-lease # update remote feature branch
# → open PR → team reviews →
git checkout main
git merge --no-ff feature/login # merge with a visible merge commit
Common Mistakes & Pitfalls
Saying one is better than the other. Both serve different purposes. The dangerous mistake is rebasing a branch that other people have already pulled.
Check your understanding
Answer these without looking back at the article.
- What happens if you rebase a branch that others have already pulled?
- What is the difference between git push --force and git push --force-with-lease?
- How do you resolve conflicts during a rebase?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.