Back to Blog
GitVersion ControlWorkflowBest Practices

Git Rebase Strategies and Best Practices

Master Git rebase for cleaner history. From interactive rebase to conflict resolution to team workflows.

B
Bootspring Team
Engineering
March 16, 2022
6 min read

Git rebase rewrites commit history for cleaner, more linear projects. Here's how to use it effectively and safely.

Understanding Rebase#

1# Basic rebase: Move branch to new base 2git checkout feature 3git rebase main 4 5# Before rebase: 6# A---B---C feature 7# / 8# D---E---F---G main 9 10# After rebase: 11# A'--B'--C' feature 12# / 13# D---E---F---G main 14 15# Rebase creates new commits (A', B', C') with different hashes

Interactive Rebase#

1# Rebase last 3 commits interactively 2git rebase -i HEAD~3 3 4# Rebase all commits since branching from main 5git rebase -i main 6 7# Interactive rebase opens editor with: 8# pick abc123 First commit 9# pick def456 Second commit 10# pick ghi789 Third commit 11 12# Available commands: 13# pick = use commit 14# reword = edit commit message 15# edit = stop for amending 16# squash = meld into previous commit (keep message) 17# fixup = meld into previous commit (discard message) 18# drop = remove commit 19 20# Example: Squash last 3 commits into one 21# pick abc123 First commit 22# squash def456 Second commit 23# squash ghi789 Third commit 24 25# Example: Reorder and edit 26# pick def456 Second commit (now first) 27# reword ghi789 Third commit (edit message) 28# drop abc123 First commit (removed)

Common Rebase Workflows#

1# Keep feature branch up to date with main 2git checkout feature 3git fetch origin 4git rebase origin/main 5 6# Squash all branch commits before merge 7git checkout feature 8git rebase -i main 9# Then squash all commits except the first 10 11# Fixup a specific commit 12git commit --fixup abc123 13git rebase -i --autosquash main 14 15# Autosquash automatically orders fixup commits 16# Original: 17# pick abc123 Original commit 18# pick def456 Some other commit 19# fixup xyz789 fixup! Original commit 20 21# After autosquash reorders: 22# pick abc123 Original commit 23# fixup xyz789 fixup! Original commit 24# pick def456 Some other commit

Handling Conflicts#

1# During rebase, if conflicts occur: 2git status # See conflicting files 3 4# Edit files to resolve conflicts 5# Remove conflict markers (<<<<<<<, =======, >>>>>>>) 6 7# Mark as resolved 8git add <resolved-files> 9 10# Continue rebase 11git rebase --continue 12 13# Or abort and return to pre-rebase state 14git rebase --abort 15 16# Skip this commit if not needed 17git rebase --skip 18 19# Use merge strategy for specific files 20git checkout --ours <file> # Keep current branch version 21git checkout --theirs <file> # Keep incoming version

Rebase vs Merge#

1# Merge: Preserves history, creates merge commit 2git checkout main 3git merge feature 4 5# Creates: 6# A---B---C feature 7# / \ 8# D---E---F---G---H main (H is merge commit) 9 10# Rebase: Linear history, rewrites commits 11git checkout feature 12git rebase main 13git checkout main 14git merge feature # Fast-forward merge 15 16# Creates: 17# D---E---F---G---A'--B'--C' main 18 19# When to use merge: 20# - Public/shared branches 21# - Preserving complete history 22# - Collaborative branches 23 24# When to use rebase: 25# - Personal feature branches 26# - Clean, linear history 27# - Before merging to main

Safe Rebasing Practices#

1# Golden rule: Never rebase public/shared commits 2 3# Create backup before risky rebase 4git branch backup-feature feature 5 6# Use reflog to recover from mistakes 7git reflog 8# abc123 HEAD@{0}: rebase finished 9# def456 HEAD@{1}: rebase: First commit 10# ghi789 HEAD@{2}: checkout: moving from main to feature 11 12git reset --hard ghi789 # Restore to before rebase 13 14# Force push after rebase (only your branches!) 15git push --force-with-lease origin feature 16 17# --force-with-lease is safer than --force 18# It fails if remote has commits you don't have

Advanced Rebase Operations#

1# Rebase onto specific commit 2git rebase --onto main feature~3 feature 3# Rebases only last 3 commits of feature onto main 4 5# Split a commit 6git rebase -i HEAD~3 7# Mark commit as "edit" 8# When rebase stops at that commit: 9git reset HEAD~1 10git add file1.js 11git commit -m "First part" 12git add file2.js 13git commit -m "Second part" 14git rebase --continue 15 16# Rebase with merge commits 17git rebase -i --rebase-merges main 18 19# Execute command after each commit 20git rebase -i --exec "npm test" main 21# Runs tests after each rebased commit 22 23# Preserve merge commits 24git rebase -p main

Team Workflows#

1# Feature branch workflow with rebase 2# 1. Create feature branch 3git checkout -b feature/new-thing main 4 5# 2. Make commits 6git commit -m "Add feature" 7 8# 3. Before PR, update and clean up 9git fetch origin 10git rebase origin/main 11 12# 4. Interactive rebase to squash/cleanup 13git rebase -i origin/main 14 15# 5. Force push (since we rebased) 16git push --force-with-lease origin feature/new-thing 17 18# 6. Create PR, get review 19 20# 7. If main updated during review, rebase again 21git fetch origin 22git rebase origin/main 23git push --force-with-lease origin feature/new-thing 24 25# 8. Merge via PR (usually squash merge)

Git Configuration for Rebasing#

1# Auto-setup remote tracking 2git config --global push.default current 3git config --global push.autoSetupRemote true 4 5# Always use rebase when pulling 6git config --global pull.rebase true 7 8# Autosquash by default 9git config --global rebase.autosquash true 10 11# Auto-stash before rebase 12git config --global rebase.autoStash true 13 14# Show conflicts in 3-way diff 15git config --global merge.conflictstyle diff3 16 17# Use better diff algorithm 18git config --global diff.algorithm histogram

Rebase Aliases#

1# Useful aliases 2git config --global alias.rb 'rebase' 3git config --global alias.rbi 'rebase -i' 4git config --global alias.rbc 'rebase --continue' 5git config --global alias.rba 'rebase --abort' 6git config --global alias.rbs 'rebase --skip' 7 8# Rebase onto main 9git config --global alias.rom 'rebase origin/main' 10 11# Interactive rebase from main 12git config --global alias.rbim 'rebase -i origin/main' 13 14# Fixup last commit into previous 15git config --global alias.fixup 'commit --fixup HEAD' 16 17# Squash all commits since main 18git config --global alias.squash '!git reset --soft $(git merge-base HEAD origin/main) && git commit'

Troubleshooting#

1# Rebase stuck or confused 2git rebase --abort 3git reflog 4git reset --hard HEAD@{n} 5 6# Wrong commits rebased 7git reflog 8git reset --hard <commit-before-rebase> 9 10# Conflicts on every commit 11# Might be rebasing wrong way 12git rebase --abort 13# Check you're rebasing feature onto main, not main onto feature 14 15# Lost commits after rebase 16git reflog 17# All commits are still there, find and checkout 18 19# Accidentally pushed rebased commits 20# If others have pulled, you need to coordinate 21# They need to: 22git fetch origin 23git reset --hard origin/branch 24# Or: 25git pull --rebase origin branch

Best Practices#

Do: ✓ Rebase local/personal branches ✓ Clean up commits before PR ✓ Use --force-with-lease ✓ Create backup branches Don't: ✗ Rebase public/shared branches ✗ Rebase after pushing to shared repo ✗ Use --force on main/master ✗ Rebase without understanding consequences Workflow: ✓ Rebase feature onto main regularly ✓ Squash fixup commits ✓ Write meaningful commit messages ✓ Test after rebase

Conclusion#

Git rebase creates cleaner history but requires care. Use it for personal branches before merging, always use --force-with-lease for pushing, and never rebase shared commits. Master interactive rebase for cleaning up commits before pull requests. When in doubt, merge is safer.

Share this article

Help spread the word about Bootspring