If you’ve worked with Git for a while, you’ve probably seen commands like:
git merge main
and:
git rebase main
Both commands can be used to bring changes from one branch into another.
So a common question is:
If both merge and rebase bring my branch up to date, what’s actually the difference?
The short answer is:
Merge combines histories. Rebase rewrites your branch’s history by replaying your commits on top of another branch.
But that sentence alone isn’t enough to really understand the difference.
Let’s start from the beginning.
First, understand what a Git branch really is
Before comparing merge and rebase, let’s understand one important thing.
A Git branch is basically a movable pointer to a commit.
Suppose we have:
A → B → C
↑
main
main currently points to commit C.
Now we create a feature branch:
A → B → C
↑
main
↑
feature
Both branches initially point to the same commit.
Now you make a change on the feature branch:
A → B → C → D
↑ ↑
main feature
feature has moved forward, while main has stayed at C.
This is the basic idea behind Git branching.
Now let’s create a real situation
Imagine you’re working on a Rails application.
You create a branch:
git switch -c feature/login
At that moment:
A → B → C
↑
main
↑
feature/login
You make two commits:
A → B → C → D → E
↑ ↑
main feature/login
Maybe:
D = Add login form
E = Add authentication logic
Meanwhile, another developer adds a change to main:
A → B → C → F
↑ ↑
main
Now your repository looks like this:
D → E
↗ ↑
A → B → C
↘
F
↑
main
More clearly:
D → E feature/login
/
A → B → C
\
F main
This is called diverged history.
Your feature branch and main have moved in different directions.
Now you need to bring main's changes into your feature branch.
That’s where merge and rebase come in.
Option 1: Git Merge
You are on your feature branch:
git switch feature/login
Then:
git merge main
Git takes the histories from both branches and creates a new merge commit when a true merge is required.
The history becomes:
D → E
/ \
A → B → C M
\ /
F
M is the merge commit.
The important thing is:
Git keeps both histories as they actually happened.
Your feature work happened on one branch.
The main work happened on another branch.
The merge commit records that those two lines were brought together.
Git’s documentation describes merge as joining development histories and, for a true merge, creating a commit with both branch tips as parents.
What does the merge commit mean?
This is important for beginners.
Suppose you see:
A → B → C → F
\
D → E
\
M
You might wonder:
“What code is inside M?”
The merge commit represents the combined result of both branches.
Conceptually:
main changes
+
feature changes
↓
combined result
The merge commit has two parents:
M
/ \
E F
That is one of the important differences between merge and rebase.
Fast-forward merge
There is another situation beginners should know about.
Suppose main hasn't changed since you created your feature branch:
A → B → C
↑
main
\
D → E
↑
feature
If you run:
git switch main
git merge feature
Git doesn’t need to create a merge commit.
It can simply move main forward:
A → B → C → D → E
↑
main
feature
This is called a fast-forward merge.
There is no divergent history to combine.
Git simply moves the branch pointer forward. Git’s merge documentation distinguishes this from a true merge commit.
Option 2: Git Rebase
Now let’s go back to our original situation.
We have:
D → E
/
A → B → C
\
F
↑
main
You are on:
feature/login
Instead of merging main, you can run:
git rebase main
This sounds complicated, but the basic idea is surprisingly simple.
Rebase takes your feature commits and replays them on top of the latest ** main.**
Git’s official documentation describes rebase as taking the changes introduced by commits on one branch and replaying them on another base.
So originally:
D → E
/
A → B → C
\
F
After:
git rebase main
Git effectively does this:
A → B → C → F → D' → E'
Now:
A → B → C → F → D' → E'
↑ ↑
new D new E
Your feature commits have been replayed on top of the latest main.
Wait… why are D and E now D’ and E’?
This is one of the most important concepts in understanding rebase.
You originally had:
D
E
After rebase, Git creates new commits based on the same changes:
D'
E'
They may contain the same code changes, but they are different Git commits.
Why?
Because a Git commit is not identified only by its code changes.
Its identity also depends on its parent and other metadata.
Before rebase:
C → D → E
After rebase:
C → F → D' → E'
Since D' has a different parent than D, its commit identity changes.
This is why rebase rewrites history.
The easiest way to remember it
Think of merge as:
“Let’s combine these two roads.”
And rebase as:
“Let’s move my road so it starts from the latest point on the other road.”
Merge
D → E
/ \
A → B → C M
\ /
F ──
Rebase
Before:
D → E
/
A → B → C → F
After:
A → B → C → F → D' → E'
A real-world analogy
Imagine you’re writing a book.
You started writing Chapter 5 based on version 1 of the book.
Meanwhile, another writer updated Chapters 1–4.
You now have two choices.
Merge
You say:
“Let’s keep both versions of history and combine the work.”
The final history shows that two people worked in parallel and their work was combined later.
Rebase
You say:
“Let’s take my Chapter 5 changes and apply them to the newest version of Chapters 1–4.”
Now the history looks as though your Chapter 5 work happened after the latest version.
That’s essentially what rebase does to commits.
Merge vs Rebase at a glance
MergeRebaseCombines branchesYesYesCreates merge commitUsually, when histories divergeNo, not for the rebased commitsKeeps original branch historyYesNoRewrites commitsNoYesProduces linear historyNot necessarilyUsuallySafer for shared/published historyGenerallyRequires cautionUseful for cleaning local historyLess soYes
The biggest difference is history.
Does rebase change the final code?
This is another common beginner question.
Suppose both operations are performed correctly and conflicts are resolved consistently.
The final project files can be the same.
For example:
Merge result:
A → B → C → F → M
Rebase result:
A → B → C → F → D' → E'
The commit history is different, but the final snapshot can represent the same combined code.
Git’s documentation explicitly notes that the final snapshot can be the same while the history differs.
So the major difference isn’t:
“Which one gives me the correct code?”
It is:
“How do I want the project’s history to look?”
Why do developers like rebase?
One major reason is cleaner history.
Imagine your project has:
main
├── feature A
├── feature B
├── feature C
└── feature D
If everyone frequently merges main into their feature branches, history can become filled with commits like:
Merge branch 'main' into feature/login
Merge branch 'main' into feature/login
Merge branch 'main' into feature/login
The history becomes harder to read.
With rebase, a developer can update their local feature branch:
git fetch origin
git rebase origin/main
and keep their own commits on top of the latest main.
The result can look much more linear:
A → B → C → D → E → F → G
rather than:
A → B → C ─────────────── M
\ /
D → E → F → G
Git’s documentation specifically notes that rebasing can produce a cleaner, linear history.
But if rebase is cleaner, why not always use it?
Because rebase rewrites history.
This is where beginners can get into trouble.
Imagine you’ve already pushed:
A → B → C → D
to GitHub.
Another developer has based their work on commit D.
Now you rebase your branch.
Git may create:
A → B → C → D'
D' is a new commit.
From Git’s perspective:
D ≠ D'
You have rewritten the branch’s history.
If you force-push that rewritten history, the other developer may have to deal with the changed commit ancestry.
That’s why rebasing shared/published branches requires caution. Git’s documentation recommends avoiding rebasing commits that have already been published unless you understand the consequences.
A simple rule for beginners
Remember this:
Rebase your private/local work. Be careful rebasing shared work.
For example:
feature/login
is your personal branch.
You can generally rebase it while you’re developing.
But:
main
is shared by the whole team.
Rewriting main history is usually a very different situation.
What happens when there is a conflict?
Both merge and rebase can have conflicts.
Suppose you changed:
def welcome_message
"Welcome!"
end
and another branch changed the same lines:
def welcome_message
"Welcome back!"
end
Git may not know which version you want.
You’ll see a conflict.
With merge:
git merge main
you resolve the conflict and then:
git add .
git commit
or use:
git merge --continue
depending on the merge state/workflow.
With rebase:
git rebase main
you resolve the conflict and then:
git add .
git rebase --continue
If you decide that you don’t want to continue the rebase:
git rebase --abort
Git provides --abort specifically to stop a rebase and attempt to restore the state from before the rebase.
Why can rebase feel harder during conflicts?
Because rebase processes your commits one at a time.
Imagine your feature has:
D → E → F
During rebase, Git essentially replays:
D'
E'
F'
If D conflicts, Git stops.
You resolve it.
Then:
git rebase --continue
Git moves to the next commit.
If E also conflicts, you resolve that one too.
So you may encounter multiple conflict resolutions during one rebase.
This happens because rebase replays the commits individually.
What happens during a merge conflict?
Merge generally considers the two branch tips and their common ancestor to combine the changes.
For example:
D → E
/
A → B → C
\
F → G
Git compares the relevant histories and attempts to produce one combined result.
If Git can’t automatically determine the correct result, it stops and asks you to resolve the conflict.
Git’s normal merge strategy is a three-way merge using the two branch tips and their merge base.
Rebase is not “better merge”
This is an important point.
You will sometimes hear:
“Always rebase. Merge is bad.”
That’s an oversimplification.
Merge and rebase solve related problems but have different effects on history.
Git’s own documentation doesn’t say that one is universally better. The appropriate choice depends on the project and team workflow.
A team may deliberately prefer merge commits because they want to preserve the history of how branches were integrated.
Another team may prefer a linear history because it makes the project easier to navigate.
Neither approach is universally correct.
Example: working on a feature in a team
Suppose you’re working on:
feature/payment
Meanwhile, main has received new changes.
You want the latest main changes before opening/updating your PR.
You can do:
git fetch origin
git rebase origin/main
Your history becomes:
main:
A → B → C → D
feature:
E → F
After:
git rebase origin/main
you get:
A → B → C → D → E' → F'
Your feature commits now sit on top of the latest main.
What if you use merge instead?
You could instead do:
git fetch origin
git merge origin/main
The history could become:
E → F
/ \
A → B → C → D → M
The branch contains the same combined work, but the history records the merge explicitly.
What does git pull --rebase mean?
You may also have seen:
git pull --rebase
This is another source of confusion.
Normally:
git pull
essentially does:
git fetch
+
integration
Depending on your configuration/options, that integration can use merge or rebase. Git documents git pull --rebase as fetching and then rebasing the local work onto the fetched branch.
So:
git pull --rebase
roughly means:
Get latest remote changes
↓
Put my local commits on top of them
For example:
Before:
remote: A → B → C
local: \
D → E
After:
A → B → C → D' → E'
git pull vs git pull --rebase
This is a useful distinction:
Regular pull
Depending on configuration:
git pull
can integrate remote changes using merge.
You might get:
A → B → C ───── M
\ /
D → E
Pull with rebase
git pull --rebase
gives you:
A → B → C → D' → E'
Again, the goal is usually to avoid unnecessary merge commits in your local branch history.
What should a fresher use?
For someone new to Git, I recommend first becoming comfortable with merge.
Understand:
git switch
git fetch
git merge
git pull
git push
Once you understand branches and commits, learn rebase.
Why?
Because merge is conceptually closer to:
“These two branches have changes. Combine them.”
Rebase requires understanding:
“Take these existing commits and recreate them on a different base.”
Once that mental model clicks, rebase becomes much easier.
A simple decision guide
Use merge when:
- You want to preserve the actual branching history.
- The branch is shared by multiple developers.
- You don’t want to rewrite existing history.
- Your team prefers merge commits.
- You are integrating a completed branch into another branch.
Example:
git switch main
git merge feature/login
Use rebase when:
- You’re updating your own feature branch.
- You want a cleaner linear history.
- Your commits haven’t been shared yet.
- Your team follows a rebase-based workflow.
- You want to replay your work on top of the latest main.
Example:
git switch feature/login
git fetch origin
git rebase origin/main
One important warning about force push
After rebasing a branch that has already been pushed, you may need:
git push --force-with-lease
Notice that I used:
--force-with-lease
rather than:
--force
--force-with-lease provides an additional safety check so you don't blindly overwrite a remote branch that has moved unexpectedly.
But even with --force-with-lease, you should understand that you're rewriting the remote branch's history.
Don’t casually rebase and force-push a branch that other developers are actively using.
The easiest mental model
If you remember only one thing from this article, remember this:
Merge
Keep both histories and connect them.
D → E
/ \
A → B M
\ /
F → G
Rebase
Take my commits and replay them on top of the latest base.
Before:
D → E
/
A → B → C → F
After:
A → B → C → F → D' → E'
Final comparison
Imagine you and another developer start from the same point:
A → B
You create:
C → D
They create:
E → F
With merge
Git says:
“You both worked from B. I’ll combine the two histories.”
C → D
/ \
A → B M
\ /
E → F
With rebase
Git says:
“I’ll take your C and D changes and replay them after E and F.”
A → B → E → F → C' → D'
That’s the core difference.
Conclusion
git merge and git rebase aren't simply two commands that do the same thing.
They represent two different approaches to history.
Merge preserves the branching story.
Rebase rewrites your branch so your commits appear on top of another branch.
If you’re working on your own feature branch, rebasing onto the latest main can give you a clean, linear history.
If you’re working with shared history where preserving existing commits and branch relationships matters, merging can be the safer choice.
The most important thing isn’t memorizing:
git merge
versus:
git rebase
It’s understanding what Git is doing to your commit graph.
Once you understand the graph, the commands become much easier to remember.
Quick cheat sheet
# Merge
git switch feature/login
git merge main
# Rebase
git switch feature/login
git rebase main
# Update remote information first
git fetch origin
# Rebase your feature onto remote main
git rebase origin/main
# Continue after resolving a rebase conflict
git add .
git rebase --continue
# Cancel an ongoing rebase
git rebase --abort
# Push rewritten history safely
git push --force-with-lease
Remember:
MERGE → combine histories
REBASE → replay commits on a new base
Top comments (0)