Skip to main content

Rebase Explained

Quick Summary

git rebase moves your branch's commits to start from a different point — typically the latest commit on main. This creates a linear history (no merge commits) at the cost of rewriting commit hashes. Powerful but dangerous if used on shared branches.


Merge vs Rebase

With Merge

gitGraph
commit id: "C1"
commit id: "C2"
branch feature
commit id: "C3"
commit id: "C4"
checkout main
commit id: "C5"
merge feature id: "M1"

Result: Preserves branch topology but creates a merge commit.

With Rebase

gitGraph
commit id: "C1"
commit id: "C2"
commit id: "C5"
commit id: "C3'"
commit id: "C4'"

Result: Linear history — C3 and C4 are replayed on top of C5, creating new commits (C3', C4') with different hashes.


How Rebase Works

sequenceDiagram
participant F as Feature Branch
participant M as Main Branch

Note over F,M: Step 1: Git finds common ancestor
Note over F: Step 2: Git "detaches" feature commits
Note over F,M: Step 3: Git moves to tip of main
Note over F: Step 4: Git replays feature commits<br/>one by one on top of main
Note over F: Step 5: Feature now starts from latest main

Command

# While on the feature branch
git checkout feature
git rebase main
First, rewinding head to replay your work on top of it...
Applying: feat: add login form
Applying: feat: add validation logic

After Rebase, Fast-Forward Merge

# Now main can fast-forward
git checkout main
git merge feature # Fast-forward! No merge commit

Interactive Rebase

Clean up commits before merging. Reorder, squash, edit, or drop commits.

git rebase -i HEAD~4   # Rebase last 4 commits

Git opens your editor with:

pick a1b2c3d feat: add login form
pick b2c3d4e fix: typo in login form
pick c3d4e5f feat: add validation
pick d4e5f6a WIP: debugging

# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous
# f, fixup = like squash, but discard this message
# d, drop = remove commit

Example: Squash Messy Commits

pick a1b2c3d feat: add login form
squash b2c3d4e fix: typo in login form ← merge into above
squash d4e5f6a WIP: debugging ← merge into above
pick c3d4e5f feat: add validation

Result: 4 commits become 2 clean commits.

Example: Reorder Commits

pick c3d4e5f feat: add validation          ← moved up
pick a1b2c3d feat: add login form
pick b2c3d4e fix: typo in login form

Example: Edit a Commit Message

reword a1b2c3d feat: add login form        ← will prompt for new message
pick b2c3d4e fix: typo in login form

The Golden Rule of Rebasing

Never rebase commits that have been pushed and shared

Rebase creates new commit hashes. If others have based their work on the original commits, rebasing will cause conflicts and confusion.

# This is SAFE (your local commits only)
git rebase main

# This is DANGEROUS (rewriting shared history)
git push --force # After rebasing shared commits
ScenarioRebase OK?
Your local feature branch, not yet pushed✅ Yes
Feature branch pushed but only you use it⚠️ Careful (need --force-with-lease)
Shared branch (main, develop)❌ Never
Open pull request with reviewers❌ Avoid (unless team convention)

Safe Force Push

If you must push after rebase on a branch only you use:

# Safer than --force — won't overwrite others' work
git push --force-with-lease

git pull --rebase

Instead of creating a merge commit every time you pull:

# Default pull (creates merge commits)
git pull origin main

# Rebase pull (linear history)
git pull --rebase origin main

Set as default:

git config --global pull.rebase true

Comparison: Merge vs Rebase

AspectMergeRebase
HistoryNon-linear (branch bubbles)Linear (flat)
Merge commitsYesNo
Commit hashesPreservedChanged (new hashes)
Conflict resolutionOncePotentially per commit
SafetyAlways safeDangerous on shared branches
Read-abilityShows branch topologyClean, sequential
Best forCollaborative branches, PRsLocal cleanup, personal branches

Troubleshooting

ProblemCauseFix
Conflicts during rebaseChanges overlapFix conflicts, git add, then git rebase --continue
Lost in a rebaseMultiple conflictsgit rebase --abort to cancel entirely
Pushed before rebasingNow hashes differ from remotegit push --force-with-lease (if safe)
Commits disappearedRebased and didn't push old refsgit reflog to find and recover

Resolving Rebase Conflicts

# 1. Conflict occurs during rebase
# 2. Fix the conflicted files
nano src/app.js

# 3. Stage the resolution
git add src/app.js

# 4. Continue the rebase
git rebase --continue

# Or: abort if it's too messy
git rebase --abort

Best Practices

  • Rebase local branches onto main before merging for clean history
  • Never rebase shared/public branches — it rewrites history others depend on
  • Use interactive rebase to clean up "WIP" and "typo" commits before PR
  • Use --force-with-lease instead of --force when pushing after rebase
  • Set pull.rebase true globally to avoid unnecessary merge commits

What's Next

  1. Conflict Resolution — Handle conflicts during merges and rebases
  2. Interactive Rebase (Advanced) — Deep dive into rewriting history