Git Merge vs. Git Rebase

Have you ever opened your Git history and felt lost in a maze of branches and commits? It can look like a plate of tangled spaghetti! When you’re trying to find where a bug started, a messy history makes your job much harder.

In Git, you often need to combine work from different branches. The two main ways to do this are merge and rebase.

Both can bring changes from one branch into another, but they do it in very different ways—and that difference changes how your project’s story is told.

Let’s explore both.

Git Merge

git merge joins two branches together. It takes all the changes from another branch and combines them into the one you’re on. To tie everything together, it creates a special merge commit.

# While on your main branch
git merge <feature-branch-name>

Merging keeps the entire history of both branches, exactly as it happened. Git simply connects their timelines with this new merge commit, which shows where the two histories came together.

Example: You are on your main branch and want to bring in work from your user-auth branch.

You run:

git checkout main
git merge user-auth

Now main includes everything from user-auth, and Git adds a merge commit to record this event.

Before the merge: 'main' is at C, 'user-auth' is at E.

      C  <--(main)
     /
A---B
     \
      D---E  <--(user-auth)


After the merge: A new commit 'M' is created. 'M' has TWO parents: C and E. 'main' now points to M.

      C-------M  <--(main)
     /       /
A---B-------/
     \     /
      D---E  <--(user-auth)

Important: Merge preserves the exact history and adds a new commit to show where the branches were joined.

Advantages:

Disadvantages:

Git Rebase

git rebase also combines changes, but in a completely different way. Instead of joining two histories, it rewrites your branch’s history.

It temporarily “lifts” your new commits, fetches the latest changes from the other branch, and then “re-plays” your commits one-by-one on top of those new changes.

# While on your feature branch
git rebase <base-branch-name>

Rebasing makes your history look like you started your work after the latest changes were added, resulting in a clean, straight-line (linear) history.

Example: You’re working on a branch called payment-flow. While you were coding, your teammates added new commits (C and D) to main. Your payment-flow branch is now out of date.

To update your branch, you run:

git checkout payment-flow
git rebase main
Before rebase: 'payment-flow' started after B. 'main' has moved on.

      E---F  <--(payment-flow)
     /
A---B---C---D  <--(main)


After rebase: Your commits (E, F) are re-applied as new commits (E', F')
on top of 'main'. Your 'payment-flow' branch pointer is moved.

                  E'---F'  <--(payment-flow)
                 /
A---B---C---D          <--(main)

Important: Rebase rewrites your branch history to appear after the latest commits. The original commits (E, F) are gone, and new ones (E', F') are created. The main branch itself is not changed.

Advantages:

Disadvantages:

Merge vs. Rebase: At a Glance

Aspect Git Merge Git Rebase
History Preserves original history. Adds a merge commit. Rewrites history. Creates new commits for a linear log.
Commit Log Can be “noisy” or “spaghetti-like” with many joins. Clean, straight-line, and easy to read.
Conflicts Solved once at the moment of the merge. Can be solved multiple times, once for each re-played commit.
Safety Safe for shared/public branches. Risky for shared branches. Safe for your local-only branches.
Best For Integrating a feature into main; shared branches. Cleaning up your own feature branch before merging.

When to Use Each

This isn’t an “either/or” choice. The best practice is to use both tools for what they do best.

The Best of Both: A Professional Workflow

This leads to the “Rebase, then Merge” workflow, which is used by most professional teams. It gives you a clean history and a safe, traceable record.

Imagine:

Your branch is now behind main. Here’s the clean way to handle it:

Step 1: Rebase Your Feature Branch (Locally)

First, update your local main branch, then “re-play” your dark-mode changes on top of it.

# 1. Get the latest changes from the remote
git checkout main
git pull

# 2. Go back to your feature branch
git checkout dark-mode

# 3. Rebase your changes on top of the new main
git rebase main

Now your dark-mode branch includes all the latest main commits, and your new commits are neatly placed at the end. It’s as if you just started your feature today!

If conflicts appear: Git will stop and let you fix them.

  1. Fix the files listed.
  2. Run git add . to mark them as resolved.
  3. Run git rebase --continue to keep going.

Step 2: Merge Your Clean Branch into main

Once your branch is rebased and working, you’re ready to merge it into main.

# 1. Switch to the main branch
git checkout main

# 2. Merge your feature branch
git merge dark-mode

Because you rebased first, dark-mode is now a direct descendant of main. This merge will be a “fast-forward“—Git simply moves the main pointer forward to include your new commits. No “merge commit” is needed, and the history stays perfectly linear.

Optional (but recommended): Keep a Record with --no-ff

A fast-forward merge is clean, but it erases the context of which commits belonged to your feature. It can look like all that work was done directly on main.

To get the best of both worlds, use the --no-ff (no fast-forward) flag.

git checkout main
git merge --no-ff dark-mode

This forces Git to create a merge commit, even though it could fast-forward. This gives you a clean, linear history plus a single merge commit that groups your feature’s work together.

It’s like putting a label on your history that says, “The ‘dark-mode’ feature was completed and added right here.”

The Golden Rule of Rebase

Never rebase a branch that other people are also using.

Rebasing rewrites history. If you rebase a shared branch, you are creating a new, different history. When your teammates pull your rebased branch, Git will see their local history and your new history as two different things, leading to massive confusion and “spaghetti” problems.

Only rebase your own local branches that you haven’t shared yet.

Your New Workflow: A 3-Step Summary

Step Command What It Does
1️⃣ git rebase main (On your feature branch) Updates your branch with the newest changes from main.
2️⃣ git checkout main Switch to the main branch.
3️⃣ git merge --no-ff <feature> Merge your feature, keeping its commits grouped together.