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​

Result: Preserves branch topology but creates a merge commit.

With Rebase​

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


How Rebase Works​

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