Skip to content
Home DevOps Git Rebase vs Merge: When to Use Each and Why It Matters

Git Rebase vs Merge: When to Use Each and Why It Matters

Where developers are forged. · Structured learning · Free forever.
📍 Part of: Git → Topic 3 of 19
Git rebase vs merge explained with real-world examples, diagrams, and gotchas.
⚙️ Intermediate — basic DevOps knowledge assumed
In this tutorial, you'll learn
Git rebase vs merge explained with real-world examples, diagrams, and gotchas.
  • Merge preserves historical truth — use it for shared or long-lived branches where context of parallel work matters. Rebase rewrites for readability — use it on private feature branches before raising a pull request.
  • The Golden Rule is non-negotiable: never rebase any branch that another developer has pulled. The moment a commit is shared, its SHA must not change.
  • Interactive rebase (git rebase -i) is a pre-PR hygiene tool that lets you squash, reorder, and reword commits to tell a coherent story before your code is reviewed.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer
  • Merge: safe on shared branches, creates merge commit, preserves commit SHAs
  • Rebase: safe on private branches only, rewrites commit SHAs, produces linear history
  • Interactive rebase (rebase -i): squash, reorder, reword commits before PR
  • Golden Rule: never rebase a branch others have pulled
🚨 START HERE
Git Rebase vs Merge Triage Cheat Sheet
Fast recovery for diverged branches, rebase conflicts, and shared-branch rebase disasters.
🟡'Your branch and origin have diverged' after teammate rebased shared branch
Immediate ActionHard-reset to the rebased remote. Do not merge.
Commands
git fetch origin (get the rebased remote state)
git reset --hard origin/main (align with rebased remote)
Fix NowIf you had local commits: cherry-pick them onto the new base. Announce to team immediately.
🟡Rebase conflict at each commit — resolve, continue, conflict again
Immediate ActionThis is normal. Resolve each conflict and continue.
Commands
git status (see conflicted files)
git add <file> && git rebase --continue (resolve and continue)
Fix NowIf too complex: git rebase --abort to return to pre-rebase state.
🟡git push rejected — 'non-fast-forward' after rebasing pushed commits
Immediate ActionYou rebased commits already on the remote. Check if others have pulled them.
Commands
git log --oneline origin/main..HEAD (see your rebased commits)
git log --oneline HEAD..origin/main (see remote-only commits)
Fix NowIf nobody has pulled: git push --force. If others have pulled: coordinate, do not force-push alone.
🟡Lost commits during interactive rebase — dropped instead of squashed
Immediate ActionUse reflog to find the lost commits before they expire.
Commands
git reflog | grep 'rebase' (find the rebase operation and pre-rebase state)
git cherry-pick <hash> (recover the dropped commit)
Fix NowIf entire rebase went wrong: git reset --hard ORIG_HEAD.
🟡Extra merge commit in rebased chain — ran git commit instead of rebase --continue
Immediate ActionSquash the extra commit out with interactive rebase.
Commands
git rebase -i HEAD~N (open interactive rebase for recent commits)
squash the extra commit into its parent (change 'pick' to 'squash')
Fix NowPrevention: always use git rebase --continue, never git commit during rebase.
Production IncidentRebase on develop Branch: 6 Developers Lose 3 Hours Coordinating RecoveryA senior developer rebased develop onto main to 'clean up history' before a release. They force-pushed. Six teammates who had branched off develop got diverged branches on their next fetch. The team spent 3 hours coordinating hard-resets across all machines.
SymptomSix developers reported 'Your branch and 'origin/develop' have diverged' on their next git pull. Some developers had duplicate commits in their local history. One developer merged the diverged branches, creating a merge commit that contained the same changes twice. The CI pipeline built from develop and deployed code with duplicate logic paths.
AssumptionThe team initially assumed a GitHub issue or a corrupted linear history that makes git bisect more remote. They checked GitHub status, repository settings, and network connectivity. Nobody suspected the rebase — the senior developer had force-pushed quietly and did not announce it.
Root cause1. The senior developer ran git rebase main on the develop branch to remove merge commit clutter. 2. The rebase rewrote all commit SHAs on develop. 3. They force-pushed: git push --force origin develop. 4. Six teammates had feature branches that were branched off the old develop (with old SHAs). 5. On their next git fetch, their local origin/develop now pointed to the rebased commits (new SHAs). 6. Their feature branches were still based on the old develop (old SHAs). 7. Git saw the branches as diverged — the old commits and new commits had different parents. 8. One developer tried to merge origin/develop into their feature branch, creating a merge commit with duplicate changes. 9. The CI pipeline built from develop and deployed the duplicate-logic code.
Fix1. Immediate: all 6 developers ran git fetch origin && git reset --hard origin/develop to align with the rebased remote. 2. The developer who merged the diverged branches had to git reset --hard to the commit before the merge and re-branch from the rebased develop. 3. Team rule: never rebase develop, main, release/*, or any branch that others have branched from. 4. Added branch protection on GitHub to prevent force-pushes to develop and main. 5. Documented the Golden Rule in the team wiki with a link to this incident.
Key Lesson
The Golden Rule is non-negotiable: never rebase a branch that another developer has pulled. The moment a commit is shared, its SHA must not change.Force-pushing a rebased shared branch costs every downstream developer time to recover. One bad rebase can cost the team hours.Branch protection rules on GitHub/GitLab prevent force-pushes to protected branches. Configure them for main, develop, and release branches.If you accidentally rebase a shared branch, announce it immediately. Coordinate the recovery before anyone merges the diverged state.
Production Debug GuideSystematic recovery paths for diverged branches, rebase conflicts, and shared-branch rebase disasters.
'Your branch and origin/main have diverged' after a teammate rebased and force-pushed1. Your local branch is based on old SHAs that no longer exist on the remote. 2. Do NOT merge — this creates duplicate commits. 3. Fetch: git fetch origin to get the rebased remote state. 4. Hard-reset: git reset --hard origin/main to align with the rebased remote. 5. If you had local commits on top of the old branch: cherry-pick them onto the new base: git cherry-pick <old-commit-hash>.
Rebase conflict appears multiple times during one rebase operation1. This is normal — rebase replays commits one by one, and each can conflict independently. 2. Resolve the conflict in the file, then git add <file> and git rebase --continue. 3. Do NOT run git commit during a rebase — use git rebase --continue only. 4. If too complex: git rebase --abort to return to pre-rebase state.
git push rejected with 'non-fast-forward' after rebasing local commits1. You rebased commits that were already on the remote. The remote has old SHAs, your local has new SHAs. 2. If nobody else has pulled your commits: git push --force is safe. 3. If others have pulled: do NOT force-push. Coordinate with them first. 4. Prevention: only rebase commits that have not been pushed yet.
Interactive rebase created unexpected commits or lost changes1. You may have dropped a commit during interactive rebase (used 'drop' instead of 'pick'). 2. Run git reflog to find the commit hash before the rebase. 3. Cherry-pick the lost commit: git cherry-pick <hash>. 4. If the entire rebase went wrong: git reset --hard ORIG_HEAD to return to pre-rebase state.
Merge commit created during rebase — extra commit in rebased chain1. You ran git commit instead of git rebase --continue during conflict resolution. 2. The extra commit is now in your rebased chain. 3. Fix: git rebase -i and squash the extra commit into the correct parent. 4. Prevention: always use git rebase --continue during rebase conflict resolution.

Merge and rebase both integrate changes from one branch into another. They solve the same problem in fundamentally different ways. Merge creates a merge commit that records when two branches came together. Rebase rewrites your commits so they appear to have started from the tip of the target branch.

The choice shapes your project's Git history. Merge preserves the full context of parallel development — you can see exactly what state main was in when your feature was built. Rebase produces a clean precise and git log more readable. Neither is universally better.

Common misconceptions: that rebase is always cleaner (it rewrites SHAs, which breaks shared branches), that merge always creates noise (merge commits are meaningful integration markers), and that the choice is personal preference (it is a team decision that affects debugging and collaboration).

How Git Merge Works — and What It Leaves Behind

When you run git merge, Git finds the most recent common ancestor of the two branches (called the merge base), then combines the changes from both branches into a brand-new commit. This new commit has two parents — one from each branch — which is why it's called a merge commit. It's the only commit in your repo that points backwards at two different lines of work.

This means your history is a faithful record of reality. If you and a colleague worked in parallel for a week, the graph shows that. You can run git log --graph and literally see two lanes of traffic merging into one. That's incredibly useful when you're trying to understand why a change was made in the context of what else was happening at the time.

The downside is noise. On a busy team where a dozen feature branches merge into main every day, your history fills up with merge commits that add structural information but no actual code change. Tools like git bisect and git log --oneline become harder to read. Some teams are fine with this — it's an honest record. Others find it distracting. That tension is the whole reason rebase exists.

io/thecodeforge/git/MergeFeatureBranch.sh · BASH
1234567891011121314151617181920212223242526272829
# io.thecodeforge — Merge Feature Branch

# ─────────────────────────────────────────────────────────────
# SCENARIO: You've been building a user-authentication feature.
# A colleague just merged a critical security patch into main.
# You need those changes in your feature branch before you can continue.
# ─────────────────────────────────────────────────────────────

# Step 1Check your current position
git log --oneline --graph --all
# Output before merge:
# * f3a91bc (HEAD -> feature/user-auth) Add password hashing
# * 9d44e02 Add login endpoint
# | * c7b83af (main) SECURITY: sanitize SQL inputs in user queries
# | * 4e21d09 Add rate limiting middleware
# |/
# * 8b12cde Initial project scaffold

# Step 2Switch to your feature branch (already on it, just confirming)
git checkout feature/user-auth

# Step 3Merge main INTO your feature branch
# This brings the security patch into your work
git merge main
# Git opens your editor for a commit message, or auto-creates:
# "Merge branch 'main' into feature/user-auth"

# Step 4See what the history looks like now
git log --oneline --graph --all
▶ Output
* b9e4f01 (HEAD -> feature/user-auth) Merge branch 'main' into feature/user-auth
|\
| * c7b83af (main) SECURITY: sanitize SQL inputs in user queries
| * 4e21d09 Add rate limiting middleware
* | f3a91bc Add password hashing
* | 9d44e02 Add login endpoint
|/
* 8b12cde Initial project scaffold
Mental Model
Merge Preserves Parallel History
Merge = stapling two timelines together. The staple (merge commit) is permanent.
  • Merge commit has two parents — one from each branch
  • git log --graph shows the parallel lanes and the merge point
  • History is a faithful record of what happened in parallel
  • Merge commits add structural information but no code change — this is the noise trade-off
📊 Production Insight
Merge commits are meaningful integration markers, not noise. When debugging a regression six months later, the merge commit tells you exactly what state main was in when the feature was integrated. A linear history from rebase loses this context. The trade-off: on a team with 20 merges per day, the graph becomes hard to read. The solution is not to avoid merge commits — it is to use them deliberately for significant integration points and use rebase for daily branch synchronization.
🎯 Key Takeaway
Merge creates a merge commit with two parents — a faithful record of parallel development. The history is honest but can get noisy on active repos. Merge commits are meaningful integration markers that help future debugging. Use merge for shared branches and significant integration points.

How Git Rebase Works — and Why It Rewrites History

Rebase does something more radical: it replays your commits one by one on top of a new base commit. The word 'rebase' is literal — you're changing the base commit that your branch started from. The result looks as if you had branched off at the very tip of the target branch and written all your commits from there.

Here's the key thing to internalise: your original commits are not moved. Git creates brand-new commits with the same changes but different parent hashes, timestamps, and therefore different SHA-1 identifiers. The old commits still exist temporarily, but with nothing pointing to them, they'll be cleaned up by Git's garbage collector. This is not a cosmetic rename — it is a genuine rewrite.

The payoff is a perfectly linear history. When a reviewer reads your branch after a rebase, they see a clean sequence of purposeful commits with no structural noise. git log looks like a well-written changelog. git bisect works with surgical precision because every commit in the chain is meaningful. This is why many teams require rebase before merging feature branches via pull requests — you get the readability benefits of a linear history in the shared record.

io/thecodeforge/git/RebaseOntoMain.sh · BASH
12345678910111213141516171819202122232425262728293031323334
# io.thecodeforge — Rebase Feature Branch onto Main

# ─────────────────────────────────────────────────────────────
# Same scenario — feature branch needs the security patch from main.
# This time we'll use rebase instead of merge.
# ─────────────────────────────────────────────────────────────

# Step 1Verify starting state (same branching point as before)
git log --oneline --graph --all
# * f3a91bc (HEAD -> feature/user-auth) Add password hashing
# * 9d44e02 Add login endpoint
# | * c7b83af (main) SECURITY: sanitize SQL inputs in user queries
# | * 4e21d09 Add rate limiting middleware
# |/
# * 8b12cde Initial project scaffold

# Step 2Make sure your working tree is clean before rebasing
git status
# Should show: nothing to commit, working tree clean

# Step 3Rebase the feature branch onto the tip of main
# Git will replay 9d44e02 and f3a91bc on top of c7b83af
git rebase main
# Output during rebase:
# Successfully rebased and updated refs/heads/feature/user-auth.

# Step 4Look at the history now
git log --oneline --graph --all

# Step 5If you had already pushed the old commits, force-push the rebased ones
# ONLY if nobody else has pulled your branch
git push --force-with-lease origin feature/user-auth
# --force-with-lease is safer than --force: it fails if the remote has
# commits you don't know about (someone else pushed while you were rebasing).
▶ Output
* e7c12d3 (HEAD -> feature/user-auth) Add password hashing
* a1f84b9 Add login endpoint
* c7b83af (main) SECURITY: sanitize SQL inputs in user queries
* 4e21d09 Add rate limiting middleware
* 8b12cde Initial project scaffold

⚠ Rebase Rewrites Commit SHAs
Notice that 'Add login endpoint' was 9d44e02 before the rebase but is now a1f84b9 after it. These are completely new commits. If anyone else has already pulled your feature branch and has 9d44e02 in their local history, their branch and yours are now officially diverged. This is the Golden Rule of rebasing: never rebase a branch that others are actively working from.
📊 Production Insight
The --force-with-lease flag is a critical safety mechanism that most tutorials omit. Unlike --force, which blindly overwrites the remote, --force-with-lease checks that the remote ref matches what you expect before pushing. If someone else pushed to the branch while you were rebasing, --force-with-lease fails instead of silently overwriting their work. Every team should use --force-with-lease exclusively and never --force.
🎯 Key Takeaway
Rebase replays your commits on top of a new base — new SHAs, linear history. The Golden Rule is absolute: never rebase a branch others have pulled. Use --force-with-lease instead of --force when pushing rebased branches. Orphaned commits are recoverable via reflog for 30 days.

Three Real-World Workflows — Which Approach Fits Each

Knowing the mechanics is half the battle. Knowing which to reach for in a given situation is what separates a senior engineer from someone who just memorised the commands.

Workflow 1 — Keeping your feature branch up to date: Use rebase. While you're developing in isolation, nobody else is working off your branch. Rebasing onto main daily keeps your branch current without cluttering the future merge commit with a tangle of catch-up merges inside your PR. Your pull request shows exactly your work, nothing else.

Workflow 2 — Landing a feature into main via pull request: Both strategies are used in industry. Teams that value linear history use 'Squash and Rebase' in GitHub/GitLab so the entire feature lands as one clean commit. Teams that value commit granularity and want to see the feature's internal development use a regular merge commit. Know your team's convention before you hit the merge button.

Workflow 3 — Merging a long-lived shared branch (e.g. a release branch back into main): Always use merge, never rebase. Release branches are shared by the whole team. Rebasing would rewrite commits that everyone already has locally, causing a synchronisation disaster. A merge commit here is not noise — it's a meaningful event marker that says 'release 2.4.0 was integrated on this date'.

io/thecodeforge/git/InteractiveRebaseCleanup.sh · BASH
1234567891011121314151617181920212223242526272829303132333435363738
# io/thecodeforge — Interactive Rebase Cleanup Before PR

# ─────────────────────────────────────────────────────────────
# SCENARIO: You've been working on a checkout feature.
# Your commit history is messy with 'WIP' and 'fix typo' commits.
# Before raising a PR, you want to clean it up into meaningful commits.
# ─────────────────────────────────────────────────────────────

# Step 1See what you're working with
git log --oneline
# 8a3f21c Fix typo in promo code validator
# 7d91b4e WIP saving progress before standup
# 6c82e10 Add promo code validation logic
# 5b44a01 Add cart total calculation
# 4e30f99 (main) Set up checkout module

# Step 2Start interactive rebase for the last 4 commits
# The SHA here is the commit BEFORE the ones you want to edit
git rebase -i 4e30f99

# Git opens your editor showing:
# pick 5b44a01 Add cart total calculation
# pick 6c82e10 Add promo code validation logic
# pick 7d91b4e WIP saving progress before standup
# pick 8a3f21c Fix typo in promo code validator

# Step 3Edit the file to squash the WIP and typo fix into the promo commit
# Change 'pick' to 'squash' (or 's') for commits you want to fold in:
# pick 5b44a01 Add cart total calculation
# pick 6c82e10 Add promo code validation logic
# squash 7d91b4e WIP saving progress before standup
# squash 8a3f21c Fix typo in promo code validator

# Save and close. Git opens another editor for the combined commit message.
# Write a clean message: "Add promo code validation with edge case handling"

# Step 4Verify the clean result
git log --oneline
▶ Output
b2e94f7 Add promo code validation with edge case handling
5b44a01 Add cart total calculation
4e30f99 (main) Set up checkout module
💡Interactive Rebase Is a Pre-PR Superpower
  • squash: fold a commit into the previous one — combines changes, merges messages
  • fixup: fold a commit into the previous one — combines changes, discards message
  • reorder: move commits up or down in the editor to change the sequence
  • reword: change a commit message without changing the code
📊 Production Insight
The squash-vs-merge decision for PRs is a team convention, not a personal choice. Squash-merge produces one clean commit on main — easy to revert, easy to bisect, but loses the feature's internal development history. Regular merge preserves all commits — useful for understanding how the feature evolved, but clutters main with WIP commits. The industry trend is toward squash-merge for most features and regular merge for large features where the internal history matters.
🎯 Key Takeaway
Three workflows: rebase for keeping feature branches current, squash/rebase for landing PRs (team convention), merge for shared branches (never rebase). Interactive rebase (rebase -i) is a pre-PR hygiene tool — squash, reorder, reword commits before sharing. Know your team's convention before hitting the merge button.

Resolving Conflicts: How Merge and Rebase Differ Under Pressure

Both commands can hit conflicts when the same lines of code were changed in both branches. But the experience of resolving those conflicts is very different — and this catches a lot of developers off guard the first time they rebase a branch with multiple commits.

With merge, you get exactly one conflict-resolution session. Git combines everything in one shot and stops if it can't. You fix the conflicts, run git add, then git merge --continue (or git commit), and you're done.

With rebase, conflicts can appear multiple times — once for each commit being replayed. If you have five commits and the second one conflicts, you'll resolve it and run git rebase --continue, then potentially hit another conflict at the fourth commit. Each resolution is isolated to the changes in that specific commit, which is actually more precise but also more repetitive. If you're not prepared for this, it feels like you've broken something when the conflict reappears.

The nuclear escape hatch is git rebase --abort, which returns your branch to exactly the state it was in before you started the rebase. There's no equivalent 'undo' once a merge commit is created — you'd need git revert or git reset, both of which are more involved.

io/thecodeforge/git/HandleRebaseConflict.sh · BASH
1234567891011121314151617181920212223242526272829303132333435
# io/thecodeforge — Handle Rebase Conflict

# ─────────────────────────────────────────────────────────────
# SCENARIO: Rebasing a feature branch that modifies the same config
# file that main also modified — a conflict is guaranteed.
# ─────────────────────────────────────────────────────────────

# Start the rebase
git rebase main
# Auto-merging config/database.yml
# CONFLICT (content): Merge conflict in run "git rebase --continue".

# Step 1Open the conflicted file and look at what Git shows you
cat config/database.yml
# <<<<<<< HEAD (the tip of main, your new base)
# pool_size: 10
# timeout: 5000
# =======
# pool_size: 25
# timeout: 3000
# >>>>>>> 3c9a11f (Update database pool size for auth service)

# Step 2Decide what the correct value is and edit the file manually
# Edit config/database.yml to the correct final state
# Remove ALL conflict markers (<<<<<<<, =======, >>>>>>>)

# Step 3Stage the resolved file (do NOT git commit here)
git add config/database.yml

# Step 4Tell rebase to continue replaying the remaining commits
git rebase --continue
# Git may open your editor to confirm the commit message — save and close.

# Step 5Confirm the rebase completed cleanly
git log --oneline --graph
▶ Output
* d8e47c2 (HEAD -> feature/user-auth) Update database pool size for auth service
* a1f84b9 Add login endpoint
* c7b83af (main) SECURITY: sanitize SQL inputs in user queries
* 4e21d09 Add rate limiting middleware
* 8b12cde Initial project scaffold
⚠ Never git commit During a Rebase Conflict
When a rebase pauses for conflict resolution, git status will show 'rebase in progress'. After fixing and staging your files, run git rebase --continue — not git commit. Running git commit here creates an extra unintended commit in the middle of your rebased chain. If things go sideways, git rebase --abort is your safe ejector seat.
📊 Production Insight
The multi-conflict nature of rebase is both its precision advantage and its usability cost. Each conflict is isolated to the specific commit being replayed — you know exactly which change caused the conflict. But on a branch with 10 commits and 3 conflicts, you resolve conflicts 3 separate times. The first time developers experience this, they think something is broken. The fix is team education: explain that multiple conflict sessions during rebase are normal and expected.
🎯 Key Takeaway
Merge gives you one conflict session. Rebase gives you one per replayed commit — more precise but more repetitive. During rebase conflicts: git add + git rebase --continue, never git commit. git rebase --abort is a complete undo with no side effects. There is no equivalent clean abort for merge once the merge commit is created.
🗂 Git Merge vs Rebase
Choose based on branch state and team convention.
Feature / Aspectgit mergegit rebase
History shapeNon-linear — shows parallel development as a graphLinear — appears as a single straight chain of commits
Creates new commitsYes — one merge commit with two parentsYes — new copies of every replayed commit with new SHAs
Conflict resolutionOne session for the entire mergeOne session per replayed commit that causes a conflict
Safe on shared branchesYes — always safeNo — never rebase branches others are working from
Best forIntegrating long-lived or shared branchesUpdating private feature branches and cleaning up before PRs
ReversibilityUndo with git revert of the merge commitAbort mid-process with git rebase --abort; hard to undo after
Commit SHAs preservedYes — existing commits unchangedNo — all replayed commits get new SHAs
Readability of git logCan get noisy on active reposClean and easy to scan linearly
When to avoidWhen you want a pristine linear historyOn public/shared branches like main, develop, release/*
Interactive modeNot availablegit rebase -i for powerful history editing

🎯 Key Takeaways

  • Merge preserves historical truth — use it for shared or long-lived branches where context of parallel work matters. Rebase rewrites for readability — use it on private feature branches before raising a pull request.
  • The Golden Rule is non-negotiable: never rebase any branch that another developer has pulled. The moment a commit is shared, its SHA must not change.
  • Interactive rebase (git rebase -i) is a pre-PR hygiene tool that lets you squash, reorder, and reword commits to tell a coherent story before your code is reviewed.
  • Conflict resolution behaves differently: merge gives you one conflict session; rebase gives you one session per conflicted commit being replayed. git rebase --abort is your complete undo.
  • Always use --force-with-lease instead of --force when pushing rebased branches. --force blindly overwrites. --force-with-lease fails if the remote has commits you do not know about.
  • The squash-vs-merge decision for PRs is a team convention. Squash-merge produces one clean commit. Regular merge preserves internal development history. Know your team's convention.

⚠ Common Mistakes to Avoid

    Rebasing a shared branch like main or develop
    Symptom

    After you force-push the rebased branch, your teammates get 'Your branch and origin/main have diverged' errors and end up with duplicate commits when they pull.

    Fix

    Treat the Golden Rule as absolute — only rebase branches that exist solely in your local environment. If you've already done this, coordinate with your team to hard-reset their local copies with git fetch origin && git reset --hard origin/main.

    Running git commit instead of git rebase --continue after resolving a conflict
    Symptom

    Your rebased branch ends up with an extra unexpected commit and git log shows more commits than you intended.

    Fix

    Always use git rebase --continue after staging resolved files during a rebase. If you've already created the extra commit, use git rebase -i to squash it out before pushing.

    Assuming rebase is always 'cleaner' and using it everywhere
    Symptom

    When a production bug is discovered, git bisect and git log have no record of which features were in flight simultaneously.

    Fix

    Use merge deliberately for significant integration points — release branches, hotfixes landing in main, and any branch where the parallel development context matters for future debugging.

    Using --force instead of --force-with-lease when pushing rebased branches
    Symptom

    You overwrite a teammate's commits that were pushed while you were rebasing.

    Fix

    Always use --force-with-lease. It fails if the remote has commits you do not know about. --force blindly overwrites.

    Not checking git status before rebasing
    Symptom

    Rebase fails with uncommitted changes or creates unexpected results.

    Fix

    Always run git status before rebasing. If dirty, stash first: git stash, then git rebase, then git stash pop.

Interview Questions on This Topic

  • QWhat's the practical difference between git merge and git rebase, and how would you decide which one to use when integrating a feature branch into main at your company?
  • QWhat is the Golden Rule of rebasing, and what exactly goes wrong technically if you rebase a branch that your teammates have already pulled?
  • QYou've finished a feature branch with 12 commits, half of which are 'WIP' and 'fix linting' commits. Your team uses squash-merge for all PRs. Walk me through how you'd clean that up before opening the pull request.
  • QExplain the difference between --force and --force-with-lease. When would you use each, and what is the risk of using --force?
  • QDuring a rebase, you resolve a conflict and run git commit instead of git rebase --continue. What happens, and how do you fix it?
  • QYour team lead rebased develop and force-pushed. Six developers now have diverged branches. Walk through the recovery process for each developer.
  • QWhen would you use git rebase -i instead of a regular git rebase? What operations does interactive rebase support?
  • QA merge commit has two parents. What does that mean for git bisect, and why might a team prefer linear history for debugging?

Frequently Asked Questions

Does git rebase delete my original commits?

Not immediately — it creates new commits with identical changes but different parent hashes, leaving the originals unreferenced. Git's garbage collector removes unreferenced commits after a default grace period of 30 days. You can recover them via git reflog within that window.

Is it safe to use git pull --rebase instead of git pull?

Yes, and many senior developers set this as their default via git config --global pull.rebase true. When you pull with rebase, your local unpushed commits are replayed on top of the fetched remote commits instead of creating a merge commit. This keeps your local history linear.

If both merge and rebase end up with the same code, why does the history shape matter?

Because history is how your team debugs the future. A linear history makes git bisect more effective at pinpointing which commit introduced a bug. A non-linear history with merge commits preserves context about what was happening in parallel — valuable when understanding why a particular architectural decision was made.

What is the difference between --force and --force-with-lease?

--force blindly overwrites the remote branch with your local state. If someone pushed while you were rebasing, their commits are silently lost. --force-with-lease checks that the remote ref matches what you expect before pushing. If the remote has commits you do not know about, it fails. Always use --force-with-lease.

When should I use merge instead of rebase?

Use merge for shared branches (main, develop, release/*), for integrating long-lived branches, and for any branch where the parallel development context matters for future debugging. Use rebase only on private feature branches that nobody else has pulled.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousGit Branching and MergingNext →Git Stash and Cherry-pick
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged