Git Rebase vs Merge: When to Use Each (With Real Examples)
What the Commands Actually Do
Both git merge and git rebase take changes from one branch and integrate them into another. They produce the same file content in the end. What differs is what they do to the commit history.
Merge creates a new "merge commit" that has two parents - one from each branch. Your history shows that two lines of development converged at this point. The original commits on the feature branch remain intact.
Rebase rewrites the commits from one branch so they appear to have started from the tip of another branch. There is no merge commit. The history is linear, as if development was always sequential.
---
A Concrete Example
Suppose you have a main branch and a feature/login branch. While you were working on the login feature, someone merged a bug fix to main.
Before either operation:
git log --oneline --graph --all
* d4e5f6g (feature/login) Add OAuth callback handler
* c3d4e5f Implement session storage
| * b2c3d4e (main) Fix: correct redirect URL on logout
| * a1b2c3d Initial login page
|/
* 9a0b1c2 (origin/main) Add user model
After git merge main on the feature branch:
* e5f6g7h (feature/login) Merge branch 'main' into feature/login
|| * b2c3d4e (main) Fix: correct redirect URL on logout
| * a1b2c3d Initial login page
* | d4e5f6g Add OAuth callback handler
* | c3d4e5f Implement session storage
|/
* 9a0b1c2 (origin/main) Add user model
The bug fix is now in your feature branch. The history shows the exact moment the two branches reconnected.
After git rebase main on the feature branch (from the original state):
* f6g7h8i (feature/login) Add OAuth callback handler
* e5f6g7h Implement session storage
* b2c3d4e (main) Fix: correct redirect URL on logout
* a1b2c3d Initial login page
* 9a0b1c2 (origin/main) Add user model
The feature commits have been rewritten to appear after the bug fix. The history is perfectly linear - no branch, no merge commit.
---
The Golden Rule of Rebasing
Never rebase commits that have been pushed to a shared repository.
When you rebase, Git rewrites commit hashes. If someone else has already pulled your original commits and you rebase and force-push, they now have commits that do not exist in the rewritten history. Their next pull will produce conflicts that are confusing and hard to untangle.
The rule in practice: rebase freely on your own local feature branch before pushing it. Once you push and someone else might have pulled it, stop rebasing and use merge instead.
There is one legitimate exception: if your team explicitly agrees to use rebase-only workflows and everyone knows to expect force-pushes on feature branches (never on main).
---
Interactive Rebase for Cleaning Up
git rebase -i (interactive) is one of the most useful commands in Git for keeping history clean before merging. It lets you reorder, squash, edit, or drop commits.
Suppose you have been working on a feature and your history looks like:
git log --oneline
abc1234 wip: debugging session manager
def5678 actually fix the bug this time
ghi9012 Fix: correct session timeout bug
jkl3456 Add: user session manager
Before you open a pull request, run:
git rebase -i HEAD~4
This opens an editor with your last 4 commits. You can squash the "wip" and "actually fix" commits into the real fix:
pick jkl3456 Add: user session manager
pick ghi9012 Fix: correct session timeout bug
squash def5678 actually fix the bug this time
squash abc1234 wip: debugging session manager
The result is a clean 2-commit history before the PR is reviewed. The "squash" commits are merged into the commit above them, and you get to write a single clean message for the combined change.
---
Which to Use When
Use merge when:
- You are integrating a long-running feature branch into
mainand you want the history to clearly show when the feature landed - The branch has already been shared with the team
- Your team uses a "merge commit" policy in the pull request process
- You want a clear record of parallel development
Use rebase when:
- You are updating a local feature branch with the latest changes from
mainbefore pushing - You want to clean up messy WIP commits before review (interactive rebase)
- Your team uses a "squash and merge" or "rebase and merge" policy in the PR tool
- You prefer linear history for easier
git bisectandgit log
---
Team Workflow Recommendations
Trunk-based development (small teams, frequent merges): Use rebase locally to stay current with main, then use the PR tool's "squash and merge" to land features with a single commit. Main stays linear and clean.
Feature branch workflow (typical for many teams): Rebase your feature branch onto main to resolve conflicts before opening a PR. Use merge (with a merge commit) when the PR is approved. You get the benefits of both: clean feature development and visible integration points.
GitFlow (release branches, hotfixes): Both are used. Feature branches rebase onto develop. Release branches merge into main. Hotfixes cherry-pick to both. This is heavier overhead - justified for products with multiple parallel release versions, overkill for most web applications.
---
Practice These in a Real Terminal
Understanding rebase and merge intellectually is the start. Getting comfortable with them under pressure is different. The ShellGenius Git Labs have challenges covering branching, merging, rebasing, and history recovery in a real Git environment in the browser.