Back to Blog
GitVersion ControlWorkflowBest Practices

Git Rebase vs Merge: When to Use Each

Understand Git rebase and merge. From workflows to conflict resolution to keeping history clean.

B
Bootspring Team
Engineering
January 5, 2023
5 min read

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-branch

Interactive 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 5

Handling 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 commit

Team 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-branch

Golden 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 commits

Autosquash#

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 true

Recovering 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 --abort

Comparison 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.

Share this article

Help spread the word about Bootspring