Server

Git Rebase vs Merge: When to Use Which

By Prabath Thalangama· October 24, 2026· 3 min read
#git#version-control#workflow

Introduction

merge and rebase both integrate changes from one branch into another. They produce different histories, and the choice is mostly about whether you value an exact record or a readable line.

What merge does

      A---B---C  feature
     /         \
D---E---F---G---H  main   (H is a merge commit with two parents)

git merge feature creates commit H tying the two histories together. A B C stay exactly as they were. History is truthful but can get tangled with many parallel branches.

If main hasn't moved since feature branched, Git does a fast-forward — just moves the pointer, no merge commit. Force a merge commit with --no-ff (some teams want one per feature for grouping/reverting).

What rebase does

D---E---F---G  main
             \
              A'--B'--C'  feature   (A B C replayed on top of G, as NEW commits)

git rebase main (from feature) replays your commits one at a time onto the current tip of main. A' B' C' have new hashes — they are new commits. History is linear, as if you'd started from G all along.

The golden rule

Never rebase commits that others have already pulled. Rebasing rewrites hashes; anyone who had the old commits now has a divergent history and a painful recovery. Safe to rebase: your own local feature branch that only you have. Not safe: main, or a shared branch.

Interactive rebase — history cleanup

git rebase -i main        # or: git rebase -i HEAD~5

An editor lists your commits with actions:

pick   a1b2c3d  Add user model
squash e4f5g6h  fix typo
squash i7j8k9l  fix test
reword m0n1o2p  Add user API
drop   q3r4s5t  debug logging
  • squash / fixup — fold into the previous commit (fixup discards the message).
  • reword — change the message.
  • edit — stop to amend the commit contents.
  • drop / delete the line — remove the commit.
  • reorder lines — reorder commits.

Result: a clean series of logical commits instead of "wip", "wip2", "actually fix it".

Squash merge

git merge --squash feature      # then commit once

or the "Squash and merge" button on a PR. The entire branch becomes one commit on main. Popular because main gets one commit per PR and the messy branch history disappears. Downside: you lose the individual commits (and bisect granularity) for that change.

A practical workflow

  1. Branch from main.
  2. Commit freely while working (messy is fine).
  3. Before opening / updating the PR: git fetch && git rebase origin/main, then git rebase -i to tidy into logical commits.
  4. Merge to main via PR — squash, or a merge commit, per team convention.
  5. Never git rebase or force-push a branch someone else is working on.

Resolving conflicts during rebase

Rebase applies commits one by one, so conflicts come one commit at a time:

git rebase origin/main
# conflict in commit A'
git status                 # see conflicted files
# edit files, then:
git add <files>
git rebase --continue      # move to the next commit
# ...or
git rebase --abort         # give up, back to pre-rebase state
git rebase --skip          # drop the current commit (careful)

git config rerere.enabled true makes Git remember conflict resolutions so a repeated rebase doesn't re-ask.

Verification and troubleshooting

git log --graph --oneline --all
git reflog                 # your safety net — every HEAD position, ~90 days
  • "I rebased and lost commits" — they're in git reflog. git reset --hard HEAD@{5} (or git branch recover <sha>) gets them back.
  • Force-push rejected after rebase — expected; the remote branch history changed. git push --force-with-lease (safer than --force — refuses if someone else pushed).
  • Rebase replays a commit that's already upstream — you rebased onto the wrong base, or the commit was cherry-picked. git rebase --onto.
  • Huge conflict on every commit during rebase — a formatting/lint pass touched everything. Rebase onto the commit before that pass, or merge instead this once.
  • Team disagrees on merge style — pick one, put it in CONTRIBUTING.md, enforce with branch protection (GitHub lets you allow only squash, only merge, or only rebase).
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.