Module 4 Lesson 4: Fast-forward vs three-way merges
·DevOps

Module 4 Lesson 4: Fast-forward vs three-way merges

Not all merges are the same. Learn the difference between a simple 'Fast-Forward' and the more complex 'Merge Commit' (Three-Way Merge) and when to use each.

Module 4 Lesson 4: Fast-forward vs three-way merges

When you run git merge, you might see a message saying "Fast-forward" or you might be asked to write a "Merge commit message." Why the difference?

Understanding these two types of merges helps you control how your project history looks.


1. Fast-Forward Merge

A fast-forward merge happens when the target branch hasn't changed since you created your feature branch.

Git doesn't need to do any complex calculations. It simply moves the main pointer forward to the same commit as your feature branch.

Characteristics:

  • No new commit is created.
  • The history looks like a straight line.
  • It is clean and simple.
graph LR
    subgraph "Before Merge"
        C1 --> C2[main]
        C2 --> F1
        F1 --> F2[feature]
    end
    
    subgraph "After Fast-Forward"
        C1_A --> C2_A
        C2_A --> F1_A
        F1_A --> F2_A["main / feature"]
    end

2. Three-Way Merge (Merge Commit)

A three-way merge happens when both branches have new commits since they diverged.

Git must compare three things:

  1. The common ancestor.
  2. The latest commit in main.
  3. The latest commit in your feature.

Git then creates a new commit (called a "Merge Commit") that has two parents.

Characteristics:

  • A new "Merge Commit" appears in the log.
  • History has a "diamond" or "fork" shape.
  • Preserves the fact that a feature was developed separately.

3. Which one is better?

The "Clean History" View (Fast-Forward Only)

Some teams prefer a perfectly straight line of history. They use a technique called Rebase (Module 6) to ensure every merge is a fast-forward.

The "Traceable History" View (Merge Commits)

Other teams want to see when a specific branch was merged. They even force Git to create a merge commit even if a fast-forward is possible:

git merge --no-ff feature-a
graph TD
    Ancestor["Commit 1"] --> MainCommit["Commit 2 (main)"]
    Ancestor --> FeatureCommit["Commit A (feature)"]
    
    MainCommit --> MergeCommit["Merge Commit (3-way)"]
    FeatureCommit --> MergeCommit
    
    style MergeCommit fill:#f9f

Lesson Exercise

Goal: Force a non-fast-forward merge.

  1. Go to your main branch and create a new branch quick-fix.
  2. Add a commit to quick-fix.
  3. Switch back to main. Do not add any commits to main.
  4. Merge quick-fix into main using git merge --no-ff quick-fix.
  5. Run git log --oneline --graph.
  6. Do you see a new commit starting with "Merge branch..."? Even though Git could have just fast-forwarded the pointer?

Observation: By using --no-ff, you made it clear in the history that the quick-fix branch existed and was integrated at this specific point.


Summary

In this lesson, we established:

  • Fast-Forward: Just moves the pointer (straight-line history).
  • Three-Way Merge: Creates a merge commit (branching history).
  • --no-ff allows you to force a merge commit even when a fast-forward is possible.

Next Lesson: Everything we’ve done so far assumes Git could handle the merge automatically. But what if two people change the same line of the same file? Welcome to Lesson 5: Handling merge conflicts.

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn