Benjamin Crozat “Heard about Sevalla? They let you deploy PHP apps with ease.” Claim $50 →

Git merge vs rebase: the only mental model you need

5 minutes read

Git merge vs rebase: the only mental model you need

Introduction

I’ll be honest. Merge and rebase with Git confused me for a long time. All I knew was that rebase looked cleaner, so I did it. If that’s you, this post is for you.

TL;DR (rebase AND merge)

  • Merge keeps the true history. Safe, sometimes messy.
  • Rebase rewrites your local history so it looks linear. Clean, but only safe before you share it.
  • Golden rule: rebase your own local work, merge shared work back to main.
  • My default: rebase locally, then merge into main with --ff-only or a merge commit depending on team policy.

What Git is really tracking

Git is a history of snapshots. A merge joins two histories and records a merge commit. A rebase copies your commits and replays them on top of another base so it looks like you started later. The end state of your files can be identical either way. The difference is how the story reads.

Git merge, explained like you are new to it

Two branches exist. With merge, Git glues the two lines together and adds a merge commit that says “this is where they joined.” You keep every fork and join in the timeline. It is honest. It may be busy.

When I use merge

  • Final integration to main when multiple people have touched things.
  • When I want an explicit join point for a big feature or release.
  • When our policy requires a merge commit for traceability.

If you want a linear main but still integrate safely, merge with fast-forward only:

git checkout main
git fetch origin
git merge --ff-only feature/add-payments

--ff-only refuses to create a merge commit if a fast-forward is not possible. It keeps history straight and prevents surprise “merge bubbles.”

Git rebase, explained in plain words

With rebase, Git copies your commits and pastes them on top of the latest main. It looks like you started after everyone else and never diverged. That linear look is why people love it.

The danger: rebasing rewrites commit IDs. If others already pulled those commits, you just changed the past under their feet. That is how you get “force push” drama. So do not rebase public history that other people may depend on.

When I use rebase

  • While my branch is still private or only on my machine.
  • Right before I open or update a PR, so the diff is clean.
  • For commit cleanup with git rebase -i to squash “fix typo” noise.

A tidy pre-PR refresh:

git fetch origin
git rebase origin/main
# resolve conflicts if any
git rebase --continue

If your team agrees on it, you can make git pull rebase by default:

git config --global pull.rebase true

That tells Git to replay your local commits on top of the fetched branch instead of making a merge commit on every pull.

A simple decision flow that actually works

  1. Working alone on a feature branch Rebase freely until you push. Clean history, minimal noise.

  2. Opened a PR and people are reviewing Prefer git pull --rebase to stay fresh. Avoid rewriting commits others commented on unless your team is fine with it.

  3. Integrating into main

    • Option A: Rebase the feature on main, then fast-forward merge --ff-only. Linear main, no merge commit.
    • Option B: Merge with --no-ff to keep an explicit merge commit for the feature. Useful for auditing and revert clarity.
  4. Never rebase history that others already based work on, unless your team coordinates a forced update. If you must, use --force-with-lease to reduce collateral damage.

My opinionated default setup

I like a clean history that does not surprise teammates.

# Rebase on pull by default
git config --global pull.rebase true

# Refuse accidental non-linear merges when updating main
git config --global pull.ff only

# Make fast-forward only merges the norm (avoid accidental merge commits)
# You can also use: git merge --ff-only

Why this mix: I rebase my local work to keep it linear, then I integrate with fast-forward where possible. If a feature truly needs a merge commit for context, I use --no-ff on purpose.

Practical workflows you can copy

Keep a feature branch fresh without noise

git switch feature/refactor-cache
git fetch origin
git rebase origin/main
# Fix conflicts if any.
git push --force-with-lease  # Only if the branch was already pushed.

Integrate a finished feature with a fast-forward

git switch main
git fetch origin
git merge --ff-only feature/refactor-cache
git push

Prefer a visible merge commit for big features

git switch main
git merge --no-ff feature/billing-v2
git push

--no-ff forces an explicit merge commit even when fast-forward is possible. Use this when you want a clear “we shipped this feature here” marker.

Pitfalls and how to avoid them

  • Rebasing after you pushed: coordinate or expect pain. If you do it, use git push --force-with-lease so you do not clobber someone else’s work.
  • Perpetual conflict hell: if a feature drifts for weeks, rebase more often or slice the work into smaller PRs.
  • Dirty PR diffs: rebase on main right before you push the branch or request review.

Team policy you can paste in your README

  • Rebase freely on private branches.
  • Do not rewrite public history without agreement.
  • Keep main linear with --ff-only, except when an explicit merge commit is useful.
  • Before merging, rebase on main to reduce conflicts.
  • Use --force-with-lease when rewriting your own pushed branch.

This policy balances readability and safety using what Git actually supports out of the box.

FAQ: quick answers to common “merge vs rebase” questions

Does rebase change code differently than merge?

No. If you resolve conflicts the same way, the final snapshot can be identical. The history is what changes.

Is rebase dangerous?

Only when other people already pulled your commits. Rewriting those commits forces everyone else to reconcile. Keep rebases local or coordinate.

What about interactive rebase for cleanup?

Great for squashing fixups and editing messages before your code is public. It is the standard way to polish a branch.


Did you like this article? Then, keep learning:

Help me reach more people by sharing this article on social media!

0 comments

Guest

Markdown is supported.

Hey, you need to sign in with your GitHub account to comment. Get started →

Great tools for developers

Search for posts and links

Try to type something…