Merging vs Rebasing

Merging vs Rebasing

Fundamental Distinction: Integration Philosophies

Git provides two primary mechanisms for integrating changes from one branch into another: merge and rebase. While both achieve the functional goal of incorporating commits, they represent fundamentally different philosophies about history representation and employ distinct technical approaches. Understanding when to apply each strategy is critical for maintaining effective team workflows and repository clarity.

The Core Technical Difference

Merge: Creates a new commit with multiple parents, preserving the original development timeline and explicitly documenting where branches diverged and converged.

Rebase: Rewrites commit history by replaying commits from one branch onto another, creating a linear sequence that appears as if development occurred sequentially rather than in parallel.

Visual Representation:

Before Integration (Common starting point):

      C---D (feature-branch)
     /
A---B---E---F (main)

After Merge:

      C---D
     /     \
A---B---E---F---M (main)

M is a merge commit with two parents: F and D

After Rebase:

A---B---E---F---C'---D' (main)

C’ and D’ are new commits (different SHAs) with identical changes to C and D


Merge: Explicit History Preservation

Technical Mechanics

A merge operation creates a special commit object with multiple parent references, preserving the complete graph structure of parallel development.

Command Execution:

git checkout main
git merge feature-branch

Git’s Internal Process:

  1. Identify common ancestor commit (merge base)
  2. Perform three-way merge:
    • Common ancestor state
    • Current branch tip (main)
    • Merging branch tip (feature-branch)
  3. Create merge commit with two parent references
  4. Update main branch pointer to new merge commit

Merge Commit Structure:

tree: <tree-sha>
parent: <main-tip-sha>
parent: <feature-branch-tip-sha>
author: Developer <email>
committer: Developer <email>

Merge branch 'feature-branch' into main

Merge Advantages

1. Complete Historical Accuracy

Merge commits explicitly document:

  • When branches diverged
  • When they converged
  • Who performed the integration
  • What conflicts were resolved

Practical Value:

# Understanding feature integration timing
git log --oneline --graph --all

*   abc123 Merge branch 'feature-auth' into main
|\
| * def456 Add JWT authentication
| * ghi789 Implement login endpoint
* | jkl012 Update user model
* | mno345 Fix database migration
|/
* pqr678 Previous work

Historical Query:

# Find when feature-auth was integrated
git log --merges --grep="feature-auth"

# See what was in the feature branch
git log abc123^2  # Second parent of merge commit

2. Safe for Shared Branches

Merge never rewrites existing commits—all commit SHAs remain unchanged. This makes it safe for branches where multiple developers are collaborating.

Collaborative Safety:

# Developer A merges feature into main
git checkout main
git merge feature-branch
git push origin main

# Developer B (who has the same commits) experiences no issues
git pull origin main
# Fast-forward or clean merge, no force-push required

3. Conflict Resolution Occurs Once

With merge, conflicts are resolved at the integration point. Once resolved in the merge commit, the resolution persists.

Conflict Resolution Flow:

git merge feature-branch
# CONFLICT (content): Merge conflict in src/auth.py

# Fix conflicts
vim src/auth.py
git add src/auth.py

# Single commit resolves all conflicts
git commit -m "Merge feature-branch, resolve auth conflicts"

Merge Disadvantages

1. Non-Linear History

Frequent merges create complex, difficult-to-follow history graphs.

History Clutter:

git log --oneline --graph
*   Merge branch 'feature-x'
|\
| * Feature X commit 3
| * Feature X commit 2
* | Merge branch 'feature-y'
|\|
| * Feature Y commit 2
* | Feature X commit 1
|/
* Base commit

Impact: Difficult to understand chronological project progression; git log becomes hard to parse.


2. Polluted History with “Merge Commits”

In active development with frequent integration, merge commits can outnumber actual feature commits.

Example Anti-Pattern:

git log --oneline --merges | wc -l
# 143 merge commits

git log --oneline --no-merges | wc -l
# 87 feature commits

Problem: More metadata about merging than actual development work.


Rebase: Linear History Construction

Technical Mechanics

Rebase rewrites commit history by replaying commits from one branch onto another, creating new commit objects with different parent references and SHAs.

Command Execution:

git checkout feature-branch
git rebase main

Git’s Internal Process:

  1. Find common ancestor between feature-branch and main
  2. Temporarily save commits from feature-branch (C, D)
  3. Reset feature-branch to match main tip (F)
  4. Replay saved commits one-by-one on top of main:
    • Apply commit C → creates C’ with new SHA
    • Apply commit D → creates D’ with new SHA
  5. Update feature-branch pointer to D'

SHA Change Demonstration:

# Before rebase
git log feature-branch --oneline
abc123 Feature commit 2
def456 Feature commit 1

# After rebase
git log feature-branch --oneline
ghi789 Feature commit 2  # New SHA (was abc123)
jkl012 Feature commit 1  # New SHA (was def456)

Rebase Advantages

1. Clean, Linear History

Rebased history appears as sequential development, making project progression clear and git log readable.

Linear Log Output:

git log --oneline
ghi789 Feature commit 2
jkl012 Feature commit 1
mno345 Main commit 2
pqr678 Main commit 1
stu901 Initial commit

Readability Benefit: Chronological order matches logical development sequence; easy to follow with git log or git bisect.


2. Simplified Feature Integration

When rebasing feature branches before merging to main, the final merge becomes a fast-forward—no merge commit needed.

Fast-Forward Integration:

# Rebase feature onto latest main
git checkout feature-branch
git rebase main

# Fast-forward merge into main
git checkout main
git merge feature-branch  # Creates no merge commit

Result: Main branch history remains linear, containing only feature commits without integration metadata.


3. Cleaner Commit History for Code Review

Pull requests based on rebased branches present changes in clean, logical sequence.

PR Quality:

# Before rebase: 5 commits including "fix typo", "WIP", "merge main"
# After rebase + squash: 2 well-organized commits with clear purpose

Reviewer Benefit: Logical progression of changes, not implementation artifacts.


Rebase Disadvantages

1. History Rewriting Creates Coordination Issues

Rebase changes commit SHAs, creating divergent history that conflicts with published commits.

Collaboration Problem:

# Developer A rebases and force-pushes
git rebase main
git push --force origin feature-branch

# Developer B (who had the old commits) now has conflicts
git pull origin feature-branch
# fatal: refusing to merge unrelated histories

Resolution Required: Developer B must reset to remote or rebase their work:

git fetch origin
git reset --hard origin/feature-branch
# Loses any local commits

2. Conflict Resolution Per Commit

Rebase replays commits individually, potentially requiring conflict resolution multiple times.

Conflict Sequence:

git rebase main
# Conflict in commit C
# Fix conflicts
git add resolved-files
git rebase --continue

# Conflict in commit D
# Fix conflicts again
git add resolved-files
git rebase --continue

Comparison: Merge resolves all conflicts once; rebase may require multiple resolution passes.


3. Loss of Integration Context

Linear history obscures when features were developed in parallel vs. sequentially.

Context Loss:

# Merge preserves parallelism
*   Merge feature-auth (integrated on May 15)
|\
| * Auth work (May 1-10)
* Payment work (May 1-10)

# Rebase suggests sequential development
* Auth work (appears after payment work)
* Payment work

Historical Accuracy: Rebase creates “alternate history” that didn’t actually happen.


Decision Framework: When to Use Each Strategy

Use Merge When:

Working on Shared Branches

# main, develop, release branches
git checkout main
git merge feature-branch

Rationale: Multiple developers depend on these branches; rewriting breaks their work.


Preserving Exact Development Timeline

# When integration timing matters for audit or compliance
git merge feature-x  # Documents when feature was integrated

Use Case: Regulatory environments requiring accurate development timeline.


Integrating Completed Features

# Feature is done, tested, approved
git checkout main
git merge --no-ff feature-auth  # Force merge commit even if fast-forward possible

Rationale: Explicit merge commit documents feature completion and integration point.


You Want to Show Parallel Development

# Multiple features developed simultaneously
git log --graph --all
# Visualize parallel work streams

Benefit: Historical record shows team’s actual working pattern.


Use Rebase When:

Cleaning Up Local Commits Before Pushing

# Local feature branch with messy commits
git rebase -i HEAD~5  # Interactive rebase to organize

# Then push clean history
git push origin feature-branch

Rationale: Clean up work-in-progress before code review.


Updating Feature Branch with Latest Main

# Feature branch is behind main
git checkout feature-branch
git rebase main  # Replay feature commits on latest main

# Continue development with latest changes

Rationale: Keep feature branch current without merge commits cluttering history.


Wanting Linear Project History

# Project policy: linear main branch
git checkout feature-branch
git rebase main
git checkout main
git merge feature-branch  # Fast-forward only

Result: Main branch has clean, linear history.


Before Creating Pull Request

# Prepare feature branch for review
git fetch origin
git rebase origin/main  # Ensure current with main
git push --force-with-lease origin feature-branch

# Create PR with clean, rebased history

Benefit: Reviewers see organized, conflict-free changes.


The Golden Rule of Rebasing

Never rebase commits that exist outside your local repository and that others may have based work on.

Why This Rule Exists

Technical Reason: Rebase creates new commits with different SHAs. If others have based work on original commits, rebase creates divergent history requiring manual reconciliation.

Violation Consequences:

# You rebase and force-push shared branch
git rebase main
git push --force origin shared-feature

# Teammate's state (has old commits)
git checkout shared-feature
git pull
# Conflict: divergent histories

# Teammate must either:
# 1. Reset to remote (loses local work)
git reset --hard origin/shared-feature

# 2. Rebase local work onto new history (complex)
git rebase origin/shared-feature

Identifying Safe Rebase Situations

Safe to Rebase:

  • ✅ Local commits not yet pushed
  • ✅ Personal feature branches only you work on
  • ✅ Commits you just pushed to personal branch (no one else has them)

Unsafe to Rebase:

  • ❌ main/master/develop branches
  • ❌ Commits other developers have pulled
  • ❌ Published release branches
  • ❌ Any commit someone else may have based work on

Practical Workflow Examples

Workflow 1: Feature Branch Development (Recommended Pattern)

Scenario: Develop feature while main branch continues evolving.

Step 1: Create Feature Branch

git checkout main
git pull origin main
git checkout -b feature-user-dashboard

Step 2: Develop Feature (Multiple Commits)

git commit -m "Add dashboard layout"
git commit -m "Implement data fetching"
git commit -m "Fix typo"
git commit -m "Add error handling"

Step 3: Keep Feature Current (Rebase onto Main)

# Main has new commits; update feature branch
git fetch origin
git rebase origin/main

# Resolve any conflicts
git add resolved-files
git rebase --continue

Step 4: Clean Up Commit History (Interactive Rebase)

# Organize commits before PR
git rebase -i origin/main

# Squash "Fix typo" into previous commit
# Result: Clean, logical commit sequence

Step 5: Push to Remote

git push --force-with-lease origin feature-user-dashboard

Step 6: Merge to Main After Approval

# Maintainer merges (or rebases) into main
git checkout main
git merge feature-user-dashboard  # Fast-forward possible
git push origin main

Workflow 2: Collaborative Feature Branch (Merge-Based)

Scenario: Multiple developers on same feature branch.

Coordination Strategy: Use merge to preserve all developers’ work.

Developer A:

git checkout feature-collaborative
# Make changes
git commit -am "Implement auth flow"
git pull origin feature-collaborative  # Merge if needed
git push origin feature-collaborative

Developer B (working simultaneously):

git checkout feature-collaborative
# Make changes
git commit -am "Add auth tests"
git pull origin feature-collaborative  # Merges A's changes
git push origin feature-collaborative

Final Integration to Main:

git checkout main
git merge feature-collaborative
# Single merge commit integrates entire feature
git push origin main

Rationale: Merge preserves both developers’ work without rewriting history.


Workflow 3: Open Source Contribution (Rebase Before PR)

Contributor Workflow:

# Fork and clone
git clone https://github.com/contributor/project-fork.git
git remote add upstream https://github.com/original/project.git

# Create feature branch
git checkout -b fix-critical-bug

# Develop fix
git commit -am "Fix race condition in auth module"

# Before submitting PR: rebase onto latest upstream
git fetch upstream
git rebase upstream/main

# Force-push to fork (safe because it's personal fork)
git push --force-with-lease origin fix-critical-bug

# Create pull request

Maintainer Integration:

# Review PR, test locally
git fetch origin pull/123/head:pr-123
git checkout pr-123
# Test, review

# Merge into main
git checkout main
git merge pr-123  # Or use GitHub's merge button

Advanced Techniques

Technique 1: Merge with Squash

Combines benefits of rebase (clean history) with merge (safe collaboration):

git checkout main
git merge --squash feature-branch

# Creates single commit with all feature changes
git commit -m "Add user dashboard feature

- Implement layout
- Add data fetching
- Include error handling"

Result: Main branch gets one commit instead of many; feature branch history preserved.

Use Case: Feature branches with many small commits; want single commit in main.

Advantages:

  • Clean main history (one commit per feature)
  • Preserves feature branch for reference
  • No force-push required

Disadvantage: Loses granular commit history in main branch.


Technique 2: Interactive Rebase for Commit Organization

Reorganize commits before integration:

git rebase -i HEAD~5

# Editor opens with commit list
pick abc123 Add feature foundation
pick def456 Implement core logic
pick ghi789 Fix typo
pick jkl012 Add tests
pick mno345 Update documentation

# Reorganize to:
pick abc123 Add feature foundation
pick def456 Implement core logic
squash ghi789 Fix typo         # Combine with previous
pick jkl012 Add tests
pick mno345 Update documentation

Result: Three well-organized commits instead of five with implementation artifacts.


Technique 3: Rebase with --onto (Advanced)

Transplant commits to different base:

# Current state:
main:         A---B---C
feature-old:      D---E---F
feature-new:              G---H

# Want: Move F from feature-old to feature-new
git rebase --onto feature-new feature-old~1 feature-old

# Result:
feature-new:  G---H---F'

Use Case: Restructuring branch hierarchy; moving commits between features.


Technique 4: Preserving Merge Commits During Rebase

Problem: Standard rebase flattens merge commits.

Solution: --rebase-merges (formerly --preserve-merges)

git rebase --rebase-merges main

# Preserves merge commit structure while updating base

Use Case: Complex feature branches with internal merges that should remain visible.


Conflict Resolution: Merge vs. Rebase Differences

Merge Conflict Resolution

Single Resolution Pass:

git merge feature-branch
# CONFLICT in file.py

# Fix all conflicts at once
vim file.py
git add file.py
git commit  # Single merge commit resolves everything

Characteristics:

  • All conflicts visible simultaneously
  • Single commit captures all resolutions
  • Context: comparing two branch tips

Conflict Markers:

<<<<<<< HEAD (current branch - main)
def process(data):
    return validate_v2(data)
=======
def process(data):
    return validate_v1(data)
>>>>>>> feature-branch

Rebase Conflict Resolution

Iterative Resolution:

git rebase main
# CONFLICT in file.py (commit C)

# Fix conflict in commit C
vim file.py
git add file.py
git rebase --continue

# CONFLICT in file.py (commit D)
# Fix conflict in commit D
vim file.py
git add file.py
git rebase --continue

Characteristics:

  • Conflicts resolved per commit
  • May encounter same conflict multiple times
  • Context: current commit vs. base branch

Complexity Factor: N commits = potentially N conflict resolution sessions.

Mitigation: Use git rerere (reuse recorded resolution) to automate repeated conflict resolution.


Enabling rerere (Reuse Recorded Resolution)

# Enable globally
git config --global rerere.enabled true

# First conflict resolution is recorded
git rebase main
# Conflict in file.py
# Fix conflict
git add file.py
git rebase --continue

# Second conflict (same area)
# Git automatically applies previous resolution
# Auto-merged file.py using previous resolution
git rebase --continue

Benefit: Dramatically reduces rebase conflict pain for repeated patterns.


Team Workflow Recommendations

Small Team (2-5 Developers)

Strategy: Rebase feature branches, merge into main with merge commits.

Workflow:

# Developer workflow
git checkout -b feature-x
# ... commits ...
git fetch origin
git rebase origin/main  # Keep feature current
git push --force-with-lease origin feature-x

# Integration to main
git checkout main
git merge --no-ff feature-x  # Explicit merge commit
git push origin main

Result: Linear feature history, documented integration points.


Medium Team (5-20 Developers)

Strategy: Feature branches + pull requests, squash merge for main.

Workflow:

# Developer creates PR from feature branch
# Rebase to clean up before review
git rebase -i origin/main

# Maintainer uses squash merge
git merge --squash feature-x
git commit -m "Add feature X

Detailed description of feature.

Closes #123"

Result: Clean main history (one commit per feature), detailed PR history preserved.


Large Team (20+ Developers)

Strategy: Gitflow with protected branches, merge-only for main/develop.

Workflow:

# Feature development on feature/* branches
# Can rebase feature branches

# Integration to develop: merge
git checkout develop
git merge feature-x

# Release branch: merge only
git checkout main
git merge release/v2.0.0

# No rebasing of develop or main ever

Rationale: Too many developers to coordinate rebase; merges provide safety.


Performance and Technical Considerations

Rebase Performance Characteristics

Time Complexity: O(n × m)

  • n = number of commits being rebased
  • m = average commit size (files changed)

Large Rebase Warning:

# Rebasing 100 commits onto divergent base
git rebase main  # May take several minutes

# With conflicts: potentially hours of manual resolution

Optimization: Rebase frequently (smaller diffs, fewer conflicts).


Merge Performance Characteristics

Time Complexity: O(1) for merge operation itself

  • Three-way merge algorithm
  • Single conflict resolution pass

Trade-off: Fast operation, but accumulates merge commits over time.


Common Pitfalls and Solutions

Pitfall 1: Rebasing Published Commits

Problem: Force-pushing rebased history breaks collaborators’ work.

Solution: Establish team policy:

# Feature branches: rebase allowed (personal work)
git rebase main
git push --force-with-lease origin feature-branch

# Shared branches: merge only (collaborative work)
git merge main  # Never rebase develop/main

Pitfall 2: Losing Commits During Interactive Rebase

Problem: Deleting lines in interactive rebase editor drops commits.

Prevention:

# Use 'drop' explicitly instead of deleting lines
drop abc123 Unwanted commit  # Explicit and traceable

# Recovery via reflog if commits lost
git reflog
git cherry-pick <lost-commit>

Pitfall 3: Merge Commits Obscuring History

Problem: Too many merge commits make git log unreadable.

Solution: Use merge squash for features:

git merge --squash feature-x
# Single commit in main, feature branch preserved

Pitfall 4: Rebase Conflicts on Every Commit

Problem: Rebasing long-diverged branch causes conflicts in most commits.

Solution: Consider merge instead:

# If rebase becomes painful
git rebase --abort

# Use merge
git merge main
# Single conflict resolution pass

Judgment Call: If rebase requires >5 conflict resolutions, merging may be more efficient.


Philosophical Perspectives

The “Clean History” School

Position: History should read like a book—clear, linear, logical.

Approach: Aggressive rebasing and squashing; main branch is pristine.

Benefits:

  • Easy to understand project evolution
  • git bisect works efficiently
  • New team members onboard quickly

Trade-offs:

  • Loses exact development timeline
  • More coordination overhead
  • Higher learning curve for team

The “True History” School

Position: History should record what actually happened, when it happened.

Approach: Preserve all merges; never rebase published work.

Benefits:

  • Accurate audit trail
  • No coordination issues
  • Safer for distributed teams

Trade-offs:

  • Complex, harder-to-read history
  • More merge commits than feature commits
  • Difficult to understand logical progression

The Pragmatic Middle Ground (Recommended)

Strategy: Rebase privately, merge publicly.

Implementation:

# Private work (feature branch): rebase freely
git checkout feature-branch
git rebase -i main  # Clean up commits
git push --force-with-lease origin feature-branch

# Public integration (to main): merge with commit
git checkout main
git merge --no-ff feature-branch
git push origin main

Result: Clean feature history, documented integration points, no collaboration issues.


Decision Matrix: Quick Reference

SituationRecommended StrategyRationale
Local feature branchRebase onto mainKeep current, clean history
Shared feature branchMerge from mainPreserve collaborators’ work
Integration to mainMerge (with commit)Document integration point
Private history cleanupInteractive rebaseOrganize before review
Pull request prepRebase + squashClean, reviewable history
Hotfix to productionMerge (fast-forward)Speed and safety
Long-lived featurePeriodic mergeAvoid massive conflicts
Open source contributionRebase before PRMaintainer-friendly

Advanced Configuration

Git Config for Workflow Optimization

# Always create merge commit (never fast-forward main)
git config branch.main.mergeoptions "--no-ff"

# Enable rerere for conflict resolution reuse
git config --global rerere.enabled true
git config --global rerere.autoupdate true

# Default to rebase when pulling
git config --global pull.rebase true

# Use merge for specific branches
git config branch.develop.rebase false

# Configure merge tool
git config --global merge.tool kdiff3
git config --global merge.conflictstyle diff3

Alias Shortcuts

# Rebase shortcuts
git config --global alias.rb 'rebase'
git config --global alias.rbi 'rebase -i'
git config --global alias.rbc 'rebase --continue'
git config --global alias.rba 'rebase --abort'

# Merge shortcuts
git config --global alias.mg 'merge --no-ff'
git config --global alias.mgs 'merge --squash'

# Log visualization
git config --global alias.tree 'log --graph --oneline --all'

Case Studies: Real-World Scenarios

Case Study 1: Feature Branch Rebase Disaster Recovery

Scenario: Developer rebased shared feature branch, breaking teammate’s work.

Recovery Protocol:

# Teammate A (who rebased)
git reflog feature-branch
# Find pre-rebase state: abc123

# Create recovery branch
git branch feature-branch-old abc123
git push origin feature-branch-old

# Teammate B (affected developer)
# Option 1: Cherry-pick work onto rebased branch
git checkout feature-branch  # New rebased version
git cherry-pick <their-commits>

# Option 2: Continue on old branch, merge later
git checkout feature-branch-old
# Continue working

Lesson: Coordinate before rebasing shared branches; maintain backups.


Case Study 2: Main Branch Linear History Policy

Team Policy: Main branch must remain linear (no merge commits).

Implementation:

# Feature branches rebase before integration
git checkout feature-x
git rebase main
git push --force-with-lease origin feature-x

# Integration uses fast-forward only
git checkout main
git merge --ff-only feature-x
# If not fast-forward, rebase required

# GitHub/GitLab: Require linear history setting

Result: Clean main branch, all features appear sequential.


Case Study 3: Complex Multi-Team Repository

Setup: 50+ developers, multiple feature teams, shared codebase.

Strategy: Hierarchical merge structure.

# Team feature branches: rebase internally
team-a/feature-1 (rebased frequently)
team-a/feature-2 (rebased frequently)

# Team integration branch: merge from features
team-a/integration (merge from features)

# Main develop branch: merge from team branches
develop (merge from team-a/integration, team-b/integration)

# Production branch: merge from develop
main (merge from develop for releases)

Rationale: Balances clean feature history with safe collaborative integration.


Summary: Strategic Integration Principles

Core Understanding:

  1. Merge = preserve history, document integration, safe for collaboration
  2. Rebase = clean history, linear progression, requires coordination

Golden Rules:

  1. Never rebase commits others depend on
  2. Use --force-with-lease not --force
  3. Rebase privately, merge publicly
  4. Choose strategy based on branch visibility

Decision Factors:

  • Branch Type: Private vs. shared
  • Team Size: Small teams rebase more; large teams merge more
  • History Philosophy: Clean vs. accurate
  • Collaboration Intensity: Frequent collaboration → prefer merge

Practical Approach:

  • Feature branches: Rebase to stay current, clean up commits
  • Main branch: Merge to preserve integration points
  • Release branches: Merge only (never rebase)
  • Hotfixes: Merge for speed and safety

Technical Excellence: Neither merge nor rebase is inherently superior. The optimal strategy depends on context: branch visibility, team coordination capability, and history philosophy. Master both techniques and apply them strategically.


Ready to apply integration strategies? Explore Interactive Rebase Techniques