Stop Guessing: Choose the Right Git Merge Strategy Every Time

Published  ·  LithiumGit Team  ·  12 min read

Every time you run git merge, Git silently picks a strategy to combine your branches. Most developers never think about it — and that's exactly why merges sometimes produce unexpected results. Git ships with eight merge strategies: fast-forward, ort, recursive, resolve, subtree, octopus, ours, and squash. Each one is designed for a specific situation. This guide explains all of them with clear visual examples, so you can stop guessing and start choosing the right strategy every time.

Quick Reference: All Git Merge Strategies at a Glance

StrategyMerge Commit?Rewrites History?Best For
Fast-ForwardNoNoLinear, up-to-date branches
ORT (default)YesNoEveryday two-branch merges (Git ≥ 2.33)
RecursiveYesNoEveryday two-branch merges (Git < 2.33)
ResolveYesNoSimple two-branch merges, criss-cross histories
SubtreeYesNoMerging an external project into a subdirectory
OctopusYes (multi-parent)NoMerging 2+ independent feature branches at once
OursYesNoRecording a merge while discarding incoming changes
SquashNo (staged only)Yes (commits collapsed)Condensing noisy feature-branch history

1. Fast-Forward Merge

What it doesMoves the current branch pointer forward to match the source branch — no new commit is created. Only possible when the current branch has no new commits since the source diverged.

A fast-forward merge is the simplest possible merge. When your branch has not diverged from the source — meaning every commit on your branch is already reachable from the source — Git simply advances your branch pointer. The result is a perfectly linear history with no merge commit at all.

Before and after a fast-forward merge

LithiumGit graph showing a branch eligible for fast-forward merge

Before — feature branch is directly ahead of master

LithiumGit graph after fast-forward merge showing linear history

After — master pointer moves forward, no merge commit

Git CLI command

Terminal
# Fast-forward is Git's default when possible
git checkout master
git merge feature

# To reject the merge if fast-forward is not possible, use --ff-only
git merge --ff-only feature

When to use fast-forward

  • Integrating a short-lived local branch that nobody else has used
  • When you want a clean linear history with no merge commit noise
  • Pulling the latest commits from a remote when your local branch has no new work
💡 TipUse --no-ff on important feature branches to force Git to always create a merge commit, even when a fast-forward would have been possible. In this case, Git falls back to its default merge strategy — ORT (or recursive on Git older than 2.33) — to produce the merge commit. This ensures there is always a visible merge event in the log, clearly marking when and where the branch was integrated.

2. ORT Strategy (Default since Git 2.33)

What it doesPerforms a three-way merge between two diverged branches and creates a merge commit. ORT (Ostensibly Recursive's Twin) is the modern replacement for the recursive strategy, with better performance and improved conflict handling.

Since Git 2.33 (released August 2021), ORT is the default merge strategy for merging two branches. You rarely need to specify it explicitly — it is what Git uses automatically when you run git merge on diverged branches. It resolves conflicts using a virtual merge base when histories have criss-crossed, which avoids spurious conflicts that older strategies could introduce.

Before and after an ORT merge

LithiumGit graph showing two diverged branches before ORT merge

Before — two branches have diverged from a common ancestor

LithiumGit graph after ORT merge showing a merge commit

After — a merge commit with two parents joins the branches

Git CLI command

Terminal
# ORT is used automatically — no flag needed on Git >= 2.33
git checkout master
git merge feature

# To specify ORT explicitly
git merge -s ort feature

When to use ORT

  • Virtually every standard two-branch merge — it is the default for a reason
  • Repositories that were previously experiencing spurious conflicts with the recursive strategy
  • Large repositories where merge performance matters

3. Recursive Strategy

What it doesThe predecessor to ORT. Uses a recursive three-way merge that handles criss-cross merge histories by building a virtual merge base. Was Git's default strategy before Git 2.33.

The recursive strategy was Git's default merge strategy from version 1.0 all the way until Git 2.33. It performs a three-way merge between two branch tips and their common ancestor. When there are multiple merge bases (criss-cross merges), it recursively merges them to create a virtual merge base. In modern Git, ORT supersedes it — but recursive remains available and behaves identically in most real-world scenarios.

Before and after a recursive merge

LithiumGit graph showing two diverged branches before recursive merge

Before — two branches diverged from a common base commit

LithiumGit graph after recursive merge showing merged history

After — a merge commit ties the two histories together

Git CLI command

Terminal
# Specify recursive explicitly (default on Git < 2.33)
git merge -s recursive feature

# Recursive also accepts strategy options, e.g. prefer 'ours' on conflicts
git merge -s recursive -X ours feature

When to use recursive

  • You are on Git older than 2.33 and need the default strategy behaviour
  • You need to pass strategy options (-X ours, -X theirs, -X patience) that you are used to pairing with recursive
  • In CI pipelines pinned to older Git versions

4. Resolve Strategy

What it doesA simpler three-way merge between exactly two branches. Unlike recursive, it does not attempt to build a virtual merge base when criss-cross histories exist — it just picks one base and proceeds. Faster but riskier in complex histories.

The resolve strategy is Git's older, simpler merge algorithm. It merges exactly two branch tips using a single merge base. When multiple merge bases exist (a criss-cross history pattern), resolve picks one of them and may silently discard changes that recursive or ORT would handle correctly. Use it only when you understand your repository's history structure and need the speed benefit.

Before and after a resolve merge

LithiumGit graph showing two branches before resolve merge

Before — two diverged branches, single common ancestor

LithiumGit graph after resolve merge

After — standard merge commit produced by the resolve strategy

Git CLI command

Terminal
# Use the resolve strategy explicitly
git merge -s resolve feature

When to use resolve

  • Repositories with a simple, non-criss-crossed history where you want a slightly faster merge
  • When you have previously hit edge-case bugs in the recursive strategy and want a more conservative algorithm
  • Rarely needed — prefer ORT or recursive in most cases

5. Subtree Strategy

What it doesAn extension of recursive that automatically adjusts the tree structure of the source branch to match a subdirectory in your repository. Used for merging an external project into a subdirectory of your own project.

The subtree strategy is used when you want to incorporate another repository (or branch) into a subdirectory of your current project rather than at the root. Git automatically detects the path prefix and adjusts the merge accordingly. This is an alternative to git submodules for embedding external dependencies directly into your repository tree.

Before and after a subtree merge

LithiumGit graph showing origin/master and lib-remote/master as two unrelated histories before a subtree merge

Before — two unrelated histories: origin/master and the external lib-remote/master

LithiumGit graph after subtree merge showing lib-remote history merged into origin/master

After — lib-remote history merged into origin/master under a subdirectory

Git CLI command

Terminal
# Add the external repository as a remote
git remote add lib-remote https://github.com/example/lib.git
git fetch lib-remote

# Merge using the subtree strategy, placing it under 'libs/my-lib'
git merge -s subtree --allow-unrelated-histories lib-remote/master

# Or use the subtree option with recursive to specify a prefix explicitly
git merge -s recursive -X subtree=libs/my-lib lib-remote/master

When to use subtree

  • Embedding a third-party library or tool directly into your repository
  • Importing a project from another repository into a specific subdirectory
  • When you want to avoid the overhead of git submodules while still tracking upstream changes

6. Octopus Strategy

What it doesMerges three or more branches in a single operation, producing a single merge commit with multiple parents. Automatically selected by Git when you specify more than two branch names in a merge command.

The octopus strategy is Git's way of merging many branches at once. Instead of merging them one at a time (which would create a chain of merge commits), octopus combines them all into a single multi-parent commit. It is intentionally designed for situations where the branches being merged are independent and do not conflict — if there are conflicts, Git will refuse the octopus merge and ask you to resolve them branch-by-branch.

Before and after an octopus merge

LithiumGit graph showing feature1 and feature2 branches diverged from master before an octopus merge

Before — feature1 and feature2 branches diverged from master, ready to merge

LithiumGit graph after octopus merge showing feature1 and feature2 merged into master in a single multi-parent commit

After — feature1 and feature2 merged into master in one merge commit with multiple parents

Git CLI command

Terminal
# Merge feature1 and feature2 into master (octopus is auto-selected)
git checkout master
git merge feature1 feature2

# Or specify the strategy explicitly
git merge -s octopus feature1 feature2

When to use octopus

  • Integrating multiple independent, non-conflicting feature branches in a single step
  • Topic-branch workflows where several small branches are ready to land simultaneously
  • When you want to avoid a chain of merge commits and prefer a single multi-parent node in the graph
⚠️ LimitationOctopus merges abort when any conflicts are detected. If branches have overlapping changes, merge them individually so you can resolve conflicts one at a time.

7. Ours Strategy

What it doesRecords that a merge happened — creating a proper merge commit — but entirely discardsall content from the other branch. Your branch's tree is preserved as-is.

The ours strategy is deceptively named. It does not simply prefer your changes in a conflict — it ignores all changes from the other branch completely. The merge commit is recorded in history (so tools and scripts that trace merges work correctly), but the resulting working tree is identical to what your branch already had.

This is different from the -X ours strategy option (which resolves individual file conflicts in your favour). The -s ours strategy discards the entire incoming branch, not just conflicting hunks.

Before and after an ours merge

LithiumGit graph showing feature branch diverged from master before the ours merge strategy

Before — feature branch diverged from master; feature's changes will be discarded

LithiumGit graph after ours merge — merge commit recorded on master but feature branch changes discarded

After — merge commit recorded on master; feature branch changes discarded entirely

Git CLI command

Terminal
# Merge feature into master but keep only master's content
git checkout master
git merge -s ours feature

# Tip: do NOT confuse -s ours (strategy) with -X ours (strategy option)
# -X ours resolves conflicts in your favour; -s ours discards everything from theirs

When to use ours

  • Superseding an old branch — you want to record it as merged without pulling any of its changes in
  • Keeping a release branch in sync with master while intentionally ignoring hotfixes that were already handled differently
  • Marking a branch as "done" in history without integrating its work

8. Squash Merge

What it doesCollapses all commits from the source branch into a single staged diff on your current branch. You then create one clean commit manually. No merge commit is created — the source branch history is not preserved in the target branch.

A squash merge takes every commit on the source branch and flattens them into a single uncommitted change in your working tree. You then commit that change as one tidy commit. This produces a clean, linear history on the target branch without cluttering it with every incremental "WIP" or "fix typo" commit from the feature branch.

Important: because no real merge commit is created, Git does not record that the source branch was merged. Running another squash merge from the same branch later will bring in all commits again rather than just the new ones.

Before and after a squash merge

LithiumGit graph showing a feature branch with multiple commits before squash merge

Before — feature branch has 3 incremental commits

LithiumGit graph after squash merge showing a single clean commit on master

After — all commits squashed into one(M7) clean commit on master

Git CLI command

Terminal
# Stage all changes from the feature branch as a single diff
git checkout master
git merge --squash feature

# Then commit the staged changes as one clean commit
git commit -m "feat: add my-feature"

When to use squash

  • Feature branches with a noisy commit history (many "WIP", "fix", "oops" commits)
  • Teams that enforce a one-commit-per-feature policy on the master branch
  • Pull requests where reviewers want to see one atomic change land on master
💡 Squash vs RebaseBoth squash and interactive rebase can produce a clean single commit, but they work differently. Squash creates a brand new commit with no link to the original commits; interactive rebase lets you rewrite individual commits before merging. For most teams, squash merge via a pull request is the simpler, safer choice.

Choosing the Right Strategy: Decision Guide

Not sure which strategy to use? Work through these questions:

  1. Are the histories linear (no divergence)? → Use fast-forward. If you want a merge commit anyway, use --no-ff.
  2. Merging two standard diverged branches? → Use the default (ORT) — just run git merge.
  3. Merging three or more independent branches? → Use octopus.
  4. Want a single clean commit on master? → Use squash.
  5. Want to record a merge but throw away all incoming changes? → Use ours.
  6. Merging an external project into a subdirectory? → Use subtree.
  7. On old Git or need recursive-specific options? → Use recursive explicitly.

Frequently Asked Questions

What are the different Git merge strategies?
Git supports several merge strategies: fast-forward (no merge commit when history is linear), ort (default since Git 2.33), recursive (default before Git 2.33), resolve (simpler three-way merge), subtree (merges into a subdirectory), octopus (merges 3+ branches at once), ours (keeps your branch content, discards incoming), and squash (collapses all commits into one staged diff).
What is the default Git merge strategy?
Since Git 2.33, the default merge strategy is 'ort' (Ostensibly Recursive's Twin), which replaced 'recursive'. It is faster and handles corner cases more reliably. You rarely need to specify it explicitly.
What is the difference between git merge --squash and a regular merge?
Git merge --squash collapses all commits from the source branch into a single staged change on your current branch. You then create one clean commit manually. A regular merge preserves every individual commit and creates a merge commit.
When should I use the 'ours' merge strategy?
Use 'ours' when you want to record that a merge happened for tracking purposes but intentionally want to discard all changes from the other branch, keeping only the content of your current branch.
What is the octopus merge strategy in Git?
The octopus strategy merges more than two branches in a single operation, producing a single merge commit with multiple parents. It is automatically selected when you specify more than two branch names in a git merge command.
How do I choose a Git merge strategy?
For most everyday merges, the default ort/recursive strategy is the right choice. Use fast-forward for a clean linear history. Use squash to condense noisy feature-branch commits. Use ours to record a merge without taking any changes. Use octopus to merge multiple independent branches at once. Use subtree when integrating an external project into a subdirectory.