Both rebase and merge integrate changes between branches. Understanding when to use each keeps your Git history clean and your team happy.
The Difference#
Merge:
- Creates a merge commit
- Preserves complete history
- Shows when branches diverged and joined
- Non-destructive
Rebase:
- Rewrites commit history
- Creates linear history
- Moves commits to new base
- Cleaner but rewrites SHAs
Visual Comparison#
Before (both):
main: A---B---C
\
feature: D---E
After merge:
main: A---B---C-------M
\ /
feature: D---E---/
After rebase:
main: A---B---C
\
feature: D'---E'
When to Merge#
1# Merge preserves history - good for:
2# - Shared branches
3# - PR/MR completion
4# - Release branches
5# - When history matters
6
7# Merge feature into main
8git checkout main
9git merge feature-branch
10
11# Merge with no fast-forward (always create merge commit)
12git merge --no-ff feature-branch
13
14# Squash merge (combine all commits into one)
15git merge --squash feature-branch
16git commit -m "Add feature X"When to Rebase#
1# Rebase for:
2# - Updating feature branch with main
3# - Cleaning up local commits before PR
4# - Linear history preference
5# - NEVER on shared/public branches
6
7# Update feature branch with latest main
8git checkout feature-branch
9git rebase main
10
11# Interactive rebase to clean up commits
12git rebase -i HEAD~3
13
14# Rebase onto specific commit
15git rebase --onto main feature-base feature-branchInteractive Rebase#
1# Clean up last 5 commits
2git rebase -i HEAD~5
3
4# Editor opens with:
5pick abc1234 First commit
6pick def5678 Second commit
7pick ghi9012 Fix typo
8pick jkl3456 WIP
9pick mno7890 Final implementation
10
11# Commands:
12# pick = keep commit as-is
13# reword = keep commit, edit message
14# edit = stop for amending
15# squash = combine with previous
16# fixup = squash but discard message
17# drop = remove commit
18
19# Example: squash WIP commits
20pick abc1234 First commit
21pick def5678 Second commit
22fixup ghi9012 Fix typo
23fixup jkl3456 WIP
24pick mno7890 Final implementation
25
26# Result: 3 clean commits instead of 5Handling Conflicts#
1# During rebase conflict
2git rebase main
3# CONFLICT in file.js
4
5# Resolve conflict
6# Edit file.js to fix conflicts
7git add file.js
8git rebase --continue
9
10# Or abort rebase
11git rebase --abort
12
13# During merge conflict
14git merge feature
15# CONFLICT in file.js
16
17# Resolve and complete merge
18# Edit file.js
19git add file.js
20git commitTeam Workflows#
1# Workflow 1: Rebase before merge (recommended)
2# On feature branch:
3git fetch origin
4git rebase origin/main
5# Resolve any conflicts
6git push --force-with-lease # Only if already pushed
7# Then create PR/merge
8
9# Workflow 2: Squash merge
10# Complete feature work
11git checkout main
12git merge --squash feature-branch
13git commit -m "Feature: Add user authentication"
14
15# Workflow 3: Merge commits (preserve history)
16git checkout main
17git merge --no-ff feature-branchGolden Rule#
1# NEVER rebase shared branches
2# Bad - rewrites history others depend on:
3git checkout main
4git rebase feature # DON'T DO THIS
5
6# These commits are shared - don't rebase:
7# - main/master
8# - develop
9# - release branches
10# - Any branch others are working on
11
12# Safe to rebase:
13# - Your local feature branch
14# - Before pushing
15# - After pushing with --force-with-lease (solo branch)Practical Examples#
1# Example 1: Update feature branch
2git checkout feature/user-auth
3git fetch origin
4git rebase origin/main
5# Now feature branch has latest main commits
6
7# Example 2: Clean up before PR
8git checkout feature/user-auth
9git rebase -i origin/main
10# Squash/fixup WIP commits
11# Reword unclear commit messages
12git push --force-with-lease
13
14# Example 3: Fix commit message
15git rebase -i HEAD~1
16# Change 'pick' to 'reword'
17# Save and edit message
18
19# Example 4: Remove accidental commit
20git rebase -i HEAD~3
21# Change 'pick' to 'drop' for unwanted commit
22# Save
23
24# Example 5: Reorder commits
25git rebase -i HEAD~3
26# Reorder lines to reorder commitsAutosquash#
1# Create fixup commit
2git commit --fixup abc1234
3
4# Create squash commit (keeps message)
5git commit --squash abc1234
6
7# Rebase with autosquash
8git rebase -i --autosquash main
9
10# Auto-configure
11git config --global rebase.autosquash trueRecovering from Mistakes#
1# Undo last rebase
2git reflog
3# Find the commit before rebase
4git reset --hard HEAD@{2}
5
6# Recover dropped commits
7git reflog
8# Find the dropped commit
9git cherry-pick abc1234
10
11# Abort in-progress rebase
12git rebase --abortComparison Table#
| Aspect | Merge | Rebase |
|-----------------|--------------------|--------------------|
| History | Non-linear | Linear |
| Commits | Preserved + merge | Rewritten |
| Conflicts | Once | Per commit |
| Shared branches | Safe | Dangerous |
| Traceability | Complete | Simplified |
| Rollback | Easy | Harder |
Best Practices#
Rebase:
✓ Local feature branches
✓ Before creating PR
✓ To update with latest main
✓ Interactive to clean history
Merge:
✓ Completing PRs
✓ Shared branches
✓ When history matters
✓ Release branches
Avoid:
✗ Rebasing public branches
✗ Force push without --force-with-lease
✗ Rewriting shared history
✗ Complex rebases without backup
Conclusion#
Use rebase to keep feature branches updated and history clean. Use merge to integrate completed work into shared branches. Never rebase commits that others depend on. When in doubt, merge is safer—it doesn't rewrite history.