Merge vs. Rebase: A Decision Guide
AI generated
git
HEAD
Git · Merge · Rebase · Team Workflow
Merge vs. Rebase
A Decision Guide for Everyday Git Use

Treating merge and rebase as interchangeable risks lost commits, confusing history, and force-push chaos across a team. Both commands integrate diverging branches, but in fundamentally different ways. This article explains the internal mechanics of both operations and delivers a clear, practical decision framework for daily use.

14 min read git merge · git rebase · fast-forward · squash Team Workflows · Feature Branches

1. What merge and rebase both try to solve

As soon as two developers work in parallel on a repository, diverging commit histories inevitably appear. A feature branch and the main branch drift apart because new commits land on both sides. git merge and git rebase solve exactly the same underlying problem: bringing diverging branches back together so that one branch contains the other's changes. The difference is not the goal, but the path taken to get there and what remains visible in history afterward.

Without either tool, a feature branch could never be cleanly integrated into main without manually transferring changes as patches. Git tracks each commit's parent commit, and this parent relationship is exactly the core of the difference: merge adds a new commit with two parents, rebase creates a new commit with exactly one parent on the target base for every original commit. Anyone who internalizes this structural difference immediately understands why the choice between the two commands has far-reaching consequences for team workflows.

2. How git merge works internally

git merge performs a three-way merge in most cases. Git looks at three points: the common ancestor of both branches (the merge base), the current state of the target branch, and the current state of the branch being integrated. From comparing these three states, Git calculates which changes were made on which side and automatically applies both sets of changes, provided they do not overlap.

The result of a non-trivial merge is a new merge commit with two parent commits: the last commit of the target branch and the last commit of the integrated branch. This merge commit itself contains no content changes; it merely documents that and when two histories were joined. All original commits from both branches remain unchanged, including their original SHA hashes, timestamps, and authors. This immutability is exactly what makes merge the safe choice for history that has already been published.


# Standard merge workflow: integrate a feature branch into main
git checkout main
git pull origin main
git merge feature/checkout-redesign

# If Git cannot auto-resolve, conflict markers appear in files
# <<<<<<< HEAD ... ======= ... >>>>>>> feature/checkout-redesign
git status
git add resolved-file.php
git commit

3. How git rebase works internally

git rebase follows a fundamentally different approach: instead of joining the two histories with a connecting commit, Git replays the commits of a branch one by one, in their original order, onto a new base. For each commit of the branch being rebased, Git calculates its diff (the content change), applies that diff to the new base, and creates a completely new commit from it.

This new commit inevitably has a different SHA hash than the original, even if the content is identical, because the hash depends among other things on the parent commit and the timestamp. The old commits are not deleted; they remain reachable in the reflog for a while, but the branch pointer afterward refers to the new commit chain. After a successful rebase, history looks as if all feature commits had been created linearly on top of the current state of the target branch from the very start. This rewriting of history is at once rebase's greatest strength and its greatest danger.


# Rebase a local feature branch onto the latest main
git checkout feature/checkout-redesign
git fetch origin
git rebase origin/main

# Git replays each commit one by one, updating the branch pointer at the end
git log --oneline -5

4. The golden rule: never rebase shared history

The single most important rule when working with rebase is: never rebase commits that have already been pushed to a shared branch and that other developers may already have checked out. The reason lies in the nature of rewriting: a rebase creates new commits with new SHA hashes that, from Git's point of view, have no relationship to the old commits. Anyone who rebases an already-pushed branch and then overwrites it with git push --force makes the old commits disappear from the remote and replaces them with seemingly unrelated new ones.

For teammates who already have the old state checked out locally, this creates a diverging history: their local branch is still based on the old commits, but the remote branch is based on the new ones. A simple git pull then leads either to a chaotic merge with duplicate commits or to an error message. The only clean ways out are for teammates to rebase again on their side or to completely re-clone the branch, both carrying the risk of losing local changes. This golden rule is branch-specific, not command-specific: a local feature branch that has not yet been pushed can be rebased freely, but a branch already used by colleagues cannot.

5. When merge is the right choice

Merge is the right choice whenever real, shared history needs to be preserved. When joining long-lived, shared branches such as release branches into main, the merge commit documents exactly when and how a feature was integrated. This traceability is of direct practical value for audits, hotfix analysis, and answering "which release contains this commit," because git log --merges and git log --first-parent clearly reveal the integration points.

Merge is also the safer operation on any branch that multiple people have already worked on, or that has already been pushed and checked out by others, because it does not alter any existing SHA hashes. Long-running branches such as develop or release/2.4, which regularly receive updates from main, should always be updated via merge, not rebase, since repeatedly rebasing an already-shared branch violates the golden rule directly. Merge is therefore the default for anything that extends beyond a single local working copy.

6. When rebase is the right choice

Rebase shows its value mainly locally, before code is shared. A typical pattern: a developer works on a feature branch for several days, accumulating intermediate commits like "WIP," "fixed typo," or "tests green," and wants to present a clean, understandable series of commits before opening a pull request. With git rebase -i (interactive), commits can be squashed, reworded, or reordered before the branch is even pushed for the first time.

Rebase is equally suited to bringing a local feature branch up to date with the current state of main, as long as that feature branch has not yet been shared. The result is a linear history without merge commits, which reads noticeably easier with git log than an intertwined merge topology with many side branches. Teams that place high value on a linearly readable history, for example for git bisect when hunting bugs, benefit especially from consistently rebasing and cleaning up feature branches before merging into main.

For more complex cases, git rebase --onto offers additional precision: instead of moving an entire branch onto a new base, it can cut out a specific range of commits and replay just that range onto a different base, for example when a feature branch was accidentally branched off another feature branch instead of main. Cleaning up local history is also aided by interactive rebasing with git rebase -i, where individual commits can be squashed, renamed, or dropped via a text file before the branch is pushed for the first time.


# Interactive rebase: clean up local commits before opening a PR
git rebase -i HEAD~5
# In the editor: mark commits as "squash" or "reword" as needed

# rebase --onto: move a branch that was accidentally based on the wrong branch
# feature-b was branched from feature-a instead of main
git rebase --onto main feature-a feature-b
# Replays only the commits unique to feature-b onto main,
# skipping everything feature-a already contributed

Git handles conflicts during a rebase differently than during a merge: instead of a single conflict pass covering the entire change, several independent conflicts can occur one after another, one per affected commit. The rebase process pauses at each conflicting commit individually and waits for a manual resolution before continuing to the next commit.


# Resolving a conflict that occurs mid-rebase
git rebase origin/main
# CONFLICT (content): Merge conflict in src/Checkout/Cart.php
git status
# Manually edit src/Checkout/Cart.php to resolve the conflict markers
git add src/Checkout/Cart.php
git rebase --continue

# Repeats for each subsequent commit that conflicts

# Abort at any point to return to the pre-rebase state
git rebase --abort

7. Fast-forward merges, --no-ff, and how rebase relates

A fast-forward merge occurs when the target branch has not received any new commits of its own since the branch point. In this case, Git does not need to perform a three-way merge at all; it simply moves the branch pointer forward to the last commit of the feature branch. No merge commit is created, history stays fully linear, almost as if the work had happened directly on the target branch.

The --no-ff flag forces an explicit merge commit even in this situation, to visibly document the fact of the integration in history, even when a fast-forward would technically be possible. Many teams use --no-ff deliberately for feature branches so they can later identify which commits belonged to which feature. The connection to rebase: rebasing a feature branch onto the current state of main before merging ensures that the subsequent merge is almost always a fast-forward, because no divergence remains. Rebase and fast-forward merge are therefore frequently two halves of the same workflow: linearize locally first, then integrate cleanly.


# Fast-forward merge: branch pointer simply moves forward
git checkout main
git merge feature/small-fix
# Fast-forward, no merge commit created

# Force an explicit merge commit even when fast-forward is possible
git merge --no-ff feature/small-fix
# Creates a merge commit documenting the integration point

# Rebase-then-merge pattern: linearize locally, then fast-forward
git checkout feature/checkout-redesign
git rebase origin/main
git checkout main
git merge feature/checkout-redesign

8. A practical decision framework for everyday use

The decision between merge and rebase can be reduced to a single guiding question: has this branch already been shared? If the answer is yes, the golden rule applies and merge is the safe operation. If the answer is no, meaning it is purely local work in progress, rebase is a legitimate and often cleaner choice for tidying up history before publishing it.

A third option that is often overlooked in this decision framework is git merge --squash: all commits of a feature branch are combined into a single change and applied to the target branch as one commit, without preserving the original commits or a merge relationship. Squash merges are especially suited to pull request workflows where a feature's internal commit history is of no interest for posterity and only the end result per feature should be visible in main. GitHub and GitLab interfaces offer "Squash and Merge" by default as a third button next to "Merge" and "Rebase and Merge" for exactly this reason.

9. Common pitfalls and merge vs. rebase compared

The most common mistake in practice is force-pushing after a rebase on a branch that colleagues have already checked out. Instead of git push --force, teams that need to rebase an already-shared but exclusively self-used feature branch should always use git push --force-with-lease. This flag aborts the push if the remote branch has been changed by someone else since the last own fetch, preventing foreign commits from being accidentally overwritten.

A second typical mistake: rebasing onto a branch with already completed, merged pull requests, thereby duplicating commits because Git does not recognize the same content change as already present. A third pitfall is ignoring merge conflicts during a rebase with many commits: anyone who hastily runs git add . at every single conflict without a careful look risks permanently carrying flawed resolutions into history. The following table compares both operations across the most important decision dimensions.

Dimension git merge git rebase Recommendation
History Fully preserved, including merge commit Rewritten linearly, new SHA hashes Merge for traceability
Conflict handling One conflict pass for the entire merge Possible conflict per individual commit Merge simpler with many commits
Safety on shared branches Safe, does not alter existing commits Risky, forces a force-push on already-pushed branches Merge mandatory on shared branches
Readability Branched topology with side branches Linear, chronologically clear sequence Rebase for git bisect and log readability
Typical use case Release branches, long-lived shared branches Local feature branches before the first push Situation-dependent, see the golden rule

Mironsoft

Git workflows, team processes, and CI/CD integration for Magento teams

Establish a clean Git history across your whole team?

We set up branching strategies, merge and rebase conventions, and pull request rules that fit your team size and deployment rhythm, including training for your developers.

Workflow audit

Analyze existing branching and merge practices and identify risks

Team training

Teach merge, rebase, and the golden rule hands-on for development teams

CI/CD integration

Build branch protection rules and automated checks for pull requests

10. Summary

The choice between merge and rebase is not a matter of taste; it depends directly on whether the affected history has already been shared. git merge creates a merge commit with two parents and does not alter any existing commits, which makes it the safe choice for shared, long-lived branches. git rebase replays commits onto a new base and creates new SHA hashes in the process, which makes it ideal for local cleanup before the first push, but dangerous on branches that have already been pushed.

The golden rule, never rebase commits that others have already checked out, is the single most important takeaway in this entire topic. Fast-forward merges and rebase frequently complement each other: linearize locally first, then integrate cleanly via fast-forward. Anyone who additionally considers merge --squash for pull request workflows now has all three practical tools for a clean, traceable Git history within a team.

Merge vs. Rebase, the essentials at a glance

Merge preserves history

Creates a merge commit with two parents, does not alter existing SHA hashes. Safe for shared branches.

Rebase rewrites history

Replays commits onto a new base, creates new SHA hashes. Ideal for local cleanup before the push.

The golden rule

Never rebase commits that have already been pushed and checked out by others. Avoid force-push chaos.

Third option: squash

git merge --squash combines feature commits into one commit, ideal for pull request workflows.

11. FAQ: Merge vs. Rebase

1What is the fundamental difference between git merge and git rebase?
Merge creates a commit with two parents and keeps the original history. Rebase replays commits onto a new base, creating new SHA hashes for a linear history.
2What does the golden rule of rebasing say?
Never rebase commits that have already been pushed and may have been checked out by others. New SHA hashes otherwise cause diverging history and force-push chaos.
3Why does rebase change the SHA hashes of commits?
The hash depends among other things on the parent commit. Since rebase sets a new base, the parent commit changes, and so inevitably does the hash.
4When should I use merge instead of rebase?
For shared or long-lived branches such as release branches, and for any branch already used by multiple people. Merge does not alter existing commits.
5When should I use rebase instead of merge?
For local, not-yet-pushed feature branches, to clean up intermediate commits or bring the branch up to date with the current main.
6What is a fast-forward merge?
Occurs when the target branch has not received any commits of its own since the branch point. Git simply moves the pointer forward, without a merge commit.
7What is the --no-ff flag used for during a merge?
Forces an explicit merge commit even when a fast-forward is possible, to visibly document the integration in history.
8What does git merge --squash do differently from a normal merge?
Combines all feature commits into a single commit, without preserving intermediate history or a merge relationship. Handy for pull request workflows.
9How do I resolve a conflict during a rebase?
Manually edit the files, mark them with git add, then git rebase --continue. git rebase --abort cancels the entire operation.
10What do I do if I accidentally rebased a shared branch?
Do not force-push. Abort with git rebase --abort or use the reflog to return to the state before the rebase and perform a merge instead.