TidyURLs
Go to Tools
BLOG
The TidyURLsJournal

Practical guides, tutorials, and insights about URLs, SEO, web development, and online marketing. Learn, troubleshoot, and get the most of the web.

Back to the Journal
TidyURLs Journal

Git Rebase vs Merge: A Visual Guide with Practical Examples

Practical insights, clear explanations, and useful ideas for understanding the web.

October 25, 20268 min read5 topics
Git Rebase vs Merge: A Visual Guide with Practical Examples
Explore the details
#Git#Version Control#Git Rebase#Git Merge#Developer Workflow
October 25, 20268 min read

Choosing between git rebase and git merge affects how a project’s commit history is recorded—not the goal of combining changes. Both can integrate work from one branch into another, but they represent that work differently. This guide compares the two with commit diagrams, a reproducible example, conflict-resolution steps, and recovery advice, including when not to rewrite shared history.

Key takeaways

  • Merge integrates branch histories and can create a merge commit that records where they joined.
  • Rebase replays commits on a new base, producing new commit IDs and a more linear history.
  • A merge can be fast-forward when the destination branch has not diverged; no merge commit is needed.
  • Both operations can have conflicts. Resolve them carefully, then continue or complete the operation.
  • Avoid rebasing commits that other people may already have based work on unless the team coordinates the rewrite.

The history before integration

Imagine a repository with a common commit, A. The main branch receives commit B, while a feature branch receives commit C:

      B  main
     /
A---+
     \
      C  feature

The branches have diverged: each has a commit the other does not. The task is to bring the feature work into main (or bring main into the feature branch before integration).

A commit records more than a file snapshot: it also has a parent relationship. That relationship is why merge and rebase produce different histories even when the final files are identical.

What git merge does

When you merge feature into the checked-out main branch, Git combines the histories. With divergent branches, it usually creates a merge commit, M, with both branch tips as parents:

      B-------M  main
     /       /
A---+       /
     \     /
      C---+    feature

The original B and C commits remain in the history. The merge commit records the integration point, which can make it clear when parallel work came together. The trade-off is that a project with frequent merges can have a less linear graph.

Fast-forward merges

A fast-forward is possible when the destination branch has not advanced since the other branch split from it. For example, if main still points to A and feature points to C, Git can move the main pointer forward:

Before:  A  main
          \
           C  feature

After:   A---C  main, feature

No merge commit is needed because there are no diverging histories to join. To allow only this outcome and refuse a merge commit when branches have diverged, use git merge --ff-only feature.

If you want a merge commit even when a fast-forward is possible, git merge --no-ff feature requests one. Whether to use that option is a team convention, not a requirement for correctness.

What git rebase does

Rebasing feature onto main takes the feature changes and replays them on top of the current main tip:

Before:      B  main
            /
       A---+
            \
             C  feature

After:       B---C'  feature
             /
        A---+
             main

C' represents the replayed change. It may look like C, but it is a new commit with a different parent—and therefore a different commit ID. Rebase does not move main in this example; it updates the feature branch. If you then check out main and merge the rebased feature branch, Git can usually fast-forward main to C' if main has not moved meanwhile.

A linear history can be easier to scan with commands such as git log --oneline, but it does not preserve the original merge point. Rebasing also changes commit identities, which matters if other people already rely on the original commits.

A reproducible example

The following Bash commands create a small local repository with one commit on main and one on feature. They work in a new directory with Git installed. The configuration is set for this repository only.

mkdir rebase-merge-demo
cd rebase-merge-demo
git init -b main
git config user.name "Demo User"
git config user.email "demo@example.invalid"
printf "starter\n" > README.md
git add README.md
git commit -m "Add starter file"

git switch -c feature
printf "feature work\n" > feature.txt
git add feature.txt
git commit -m "Add feature file"

git switch main
printf "main work\n" > main.txt
git add main.txt
git commit -m "Add main file"

Now the branches have diverged. Inspect the graph with:

git log --oneline --graph --decorate --all

The output will show one commit on each branch after their common starting point. Exact abbreviated hashes and graph formatting vary.

Try a merge

On main, merge the feature branch:

git switch main
git merge --no-ff feature

--no-ff makes this example create a merge commit even if Git could fast-forward. Inspect the result with git log --oneline --graph --decorate --all.

To compare rebase instead, start from a fresh copy of the demo directory or recreate the repository using the commands above. Then run:

git switch feature
git rebase main
git switch main
git merge --ff-only feature

The feature commit is replayed onto main; the final merge can fast-forward because the feature branch now includes the current main tip. The resulting files contain the same two additions as in the merge example, but the commit graph is different.

Resolving conflicts

A conflict occurs when Git cannot combine changes automatically—for example, when branches make incompatible edits to the same lines. Conflicts can happen during either a merge or a rebase. Review the affected files and understand both sides before choosing what to keep; blindly accepting one side can discard valid work.

During a conflict, Git marks the relevant file and reports that it needs attention. Open the file, edit the conflict markers and surrounding content into the intended result, then stage the resolved file.

For a merge:

# After editing each conflicted file:
git add path/to/file

# Finish the merge:
git merge --continue

For a rebase:

# After editing each conflicted file:
git add path/to/file

git rebase --continue

A rebase can stop more than once if separate replayed commits conflict. Resolve each stop before continuing. If you decide not to proceed, git merge --abort or git rebase --abort attempts to return to the state before that operation. Check the working tree before starting either operation, since pre-existing uncommitted changes can make recovery more complicated.

Shared branch risks and recovery

Why rebasing shared commits is risky

Suppose you push commits to a shared branch and a teammate creates work based on them. If you then rebase and replace those commits on the remote, the teammate’s branch still refers to the old commit IDs. They may have to reconcile two versions of what appears to be the same work, creating confusion or duplicated commits.

As a practical rule, rebase local commits that have not been shared. For a branch others use, prefer merging, or coordinate a rewrite with everyone who depends on it. A force push changes the remote branch pointer; --force-with-lease can help avoid overwriting a remote update you have not seen, but it does not make an uncoordinated history rewrite safe.

Recovering from an interrupted operation

If a conflict-resolution attempt goes wrong, first check the operation and working tree:

git status

If a merge or rebase is still in progress, use the corresponding --abort command described above when you want to cancel it. Do not assume aborting will preserve unrelated edits made during the operation; inspect the result with git status.

Recovering after a completed rebase

Git’s reflog records recent local movements of branch and HEAD references. It can help locate a branch tip from before a rebase:

git reflog

Find the old commit ID in the output, then create a safety branch pointing to it before changing anything:

git branch rescue-before-rebase <old-commit-id>

This preserves a reference to that commit. If you decide the current branch itself must return to the old tip, first ensure your working tree is clean and that restoring it is what you intend. A command such as git reset --hard <old-commit-id> moves the branch and discards tracked working-tree changes, so do not run it casually. The reflog is local and is not a substitute for a backup or a guarantee that every old commit will remain available indefinitely.

Common mistakes

  • Treating rebase and merge as interchangeable history operations. They can produce the same final file contents but different commit graphs and identities.
  • Assuming every merge creates a merge commit. A fast-forward can simply move a branch pointer.
  • Rebasing a branch someone else is using without telling them. Coordinate first, or avoid rewriting the shared commits.
  • Continuing without reviewing a conflict. The fact that a file is staged does not prove the resolution is correct.
  • Using reset --hard as a first recovery step. It can discard working-tree changes. Inspect git status and preserve a reference to useful commits before resetting.

Choosing a workflow

Choose merge when preserving the integration history matters, when a branch is shared, or when the team prefers explicit merge points. Choose rebase when you want to update your own local feature branch on top of a newer base and the commits have not been shared. A team may use both: rebase private work to keep it current, then merge it according to the project’s integration policy.

The important distinction is not that one command is universally better. Merge records a join between histories; rebase creates new commits on a different base. Understand who else depends on the commits, use the project’s conventions, and keep a recoverable reference before making a history rewrite you are unsure about.