Rebase Explained
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
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
| Scenario | Rebase 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
| Aspect | Merge | Rebase |
|---|---|---|
| History | Non-linear (branch bubbles) | Linear (flat) |
| Merge commits | Yes | No |
| Commit hashes | Preserved | Changed (new hashes) |
| Conflict resolution | Once | Potentially per commit |
| Safety | Always safe | Dangerous on shared branches |
| Read-ability | Shows branch topology | Clean, sequential |
| Best for | Collaborative branches, PRs | Local cleanup, personal branches |
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| Conflicts during rebase | Changes overlap | Fix conflicts, git add, then git rebase --continue |
| Lost in a rebase | Multiple conflicts | git rebase --abort to cancel entirely |
| Pushed before rebasing | Now hashes differ from remote | git push --force-with-lease (if safe) |
| Commits disappeared | Rebased and didn't push old refs | git 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
mainbefore 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-leaseinstead of--forcewhen pushing after rebase - Set
pull.rebase trueglobally to avoid unnecessary merge commits
What's Next
- Conflict Resolution — Handle conflicts during merges and rebases
- Interactive Rebase (Advanced) — Deep dive into rewriting history