Skip to main content

Git Rebase Guide: Merge vs Rebase Explained

This guide explains the difference between git merge and git rebase, why Pivot requires rebasing, and how to avoid common mistakes.

The Golden Rule

Never use git merge to update your feature branch. Always use git rebase.

Why This Matters: A Real Example

PR #936 showed 250+ commits when it should have shown ~50. Here's what happened:

The developer kept running git merge development into their feature branch. Each merge:

  1. Created a merge commit
  2. Pulled ALL commits from development into the feature branch
  3. Made the PR show hundreds of "new" commits

Merge vs Rebase: Visual Comparison

Starting Point

You create a feature branch from development:

Now development has new commits (D, E) that you need in your branch. You have two choices:

Option 1: Merge (WRONG)

git checkout feature
git merge development # DON'T DO THIS

Result:

Problems:

  • Creates a merge commit in your branch
  • Your branch now contains D, E from development
  • PR will show D, E as "new" commits in your branch
  • Violates linear history requirement
  • PR will be blocked by GitHub

Option 2: Rebase (CORRECT)

git checkout feature
git rebase development # DO THIS
git push --force-with-lease

Result:

Benefits:

  • No merge commit
  • Your commits are replayed ON TOP of the latest development
  • PR only shows YOUR commits
  • Clean, linear history
  • PR can be merged

Step-by-Step: How to Rebase

1. Make sure your local development is up to date

git checkout development
git pull origin development

2. Switch to your feature branch

git checkout my-feature

3. Rebase onto development

git rebase development

4. Resolve conflicts if any

If there are conflicts:

# Fix the conflicts in your editor
git add .
git rebase --continue

To abort if things go wrong:

git rebase --abort

5. Force push (required after rebase)

git push --force-with-lease

--force-with-lease is safer than --force because it fails if someone else pushed to your branch.

Common Mistakes and How to Fix Them

Mistake 1: "I already merged, now what?"

If you accidentally ran git merge development:

# Find the commit before the merge
git log --oneline

# Reset to before the merge
git reset --hard HEAD~1 # Or use the specific commit hash

# Now rebase instead
git rebase development
git push --force-with-lease

Mistake 2: "My branch has many merge commits"

If you have multiple merge commits accumulated:

# Find where your branch diverged from development
git merge-base development my-feature

# Reset to that point (keeping your changes)
git reset --soft <merge-base-hash>

# Create a fresh commit with all your changes
git commit -m "My feature work"

# Rebase onto latest development
git rebase development
git push --force-with-lease

Mistake 3: "I based my branch on another feature branch"

This is actually fine! But the parent branch must be merged first.

Example: You created payroll-introduction from feat/payroll-tips

Solution:

  1. Merge feat/payroll-tips into development first (PR #795)
  2. Then your PR will automatically show only your commits
  3. Or rebase payroll-introduction onto development after step 1

Rebase Workflow Cheat Sheet

Daily workflow

# Start of day: update your branch
git checkout development
git pull
git checkout my-feature
git rebase development
git push --force-with-lease

Before opening a PR

git checkout development
git pull
git checkout my-feature
git rebase development
# Fix any conflicts
git push --force-with-lease
# Now open PR

When PR has conflicts

git checkout development
git pull
git checkout my-feature
git rebase development
# Fix conflicts
git add .
git rebase --continue
git push --force-with-lease

Why Pivot Requires Linear History

Our branch protection rules enforce required_linear_history = true. This means:

  1. No merge commits allowed in PRs
  2. Cleaner git history - easy to read and understand
  3. Easier to bisect - find bugs with git bisect
  4. Simpler rollbacks - revert individual commits cleanly

Quick Reference

ActionCommand
Update feature branchgit rebase development
Push after rebasegit push --force-with-lease
Abort failed rebasegit rebase --abort
Continue after fixing conflictsgit rebase --continue
Skip a problematic commitgit rebase --skip
See rebase progressgit status

Summary

MergeRebase
Creates merge commitsNo merge commits
Preserves exact historyRewrites history (cleaner)
PRs show extra commitsPRs show only your commits
Blocked by our branch protectionAllowed
git merge developmentgit rebase development
git pushgit push --force-with-lease

Remember: When in doubt, rebase. If you mess up, git rebase --abort and start over.