Git Merge Strategies: Choosing the Right Tool for Every Integration Scenario¶
A practical guide born from years of resolving merges that went sideways because I used the wrong strategy.
The Merge Is Not Just One Operation¶
Early in my career, I thought git merge was a single, simple operation. Branch A merges into branch B, conflicts get resolved, done. I was wrong. Git offers multiple merge strategies, each optimized for different scenarios. Using the wrong one leads to silent data loss, unnecessary conflicts, or history that nobody can follow.
The Default: Recursive Strategy¶
When you run git merge feature-branch, git uses the "recursive" strategy (or "ort" in newer versions). It finds the common ancestor of both branches and performs a three-way merge:
# Standard merge — uses recursive/ort by default
git merge feature-branch
# Explicitly specify the strategy
git merge -s recursive feature-branch
git merge -s ort feature-branch # Git 2.34+, faster
Recursive handles most cases well. It even handles "criss-cross merges" where branches have multiple common ancestors — it merges the ancestors first, then uses that virtual result as the base.
Fast-Forward: When History Is Linear¶
If the target branch hasn't moved since the feature branched off, git can simply move the pointer forward:
# This happens automatically when possible:
git checkout main
git merge feature-branch # fast-forward if main hasn't changed
# Force a merge commit even when fast-forward is possible:
git merge --no-ff feature-branch
# Only allow fast-forward (fail if not possible):
git merge --ff-only feature-branch
I use --no-ff in my pull request workflow. Even when fast-forward is possible, the merge commit serves as a record: "this feature was integrated at this point." It groups the feature's commits together in the log and makes reverting the entire feature trivial.
I use --ff-only when pulling from upstream: git pull --ff-only origin main. If it fails, that means my branch has diverged and I need to decide how to handle it explicitly.
Ours Strategy: Declaring Victory Without Fighting¶
The "ours" strategy resolves every conflict by taking our side. The other branch's changes are discarded. This sounds useless, but it has legitimate uses:
When I use this: - Marking a branch as "superseded" without losing the merge history - Closing a long-dead feature branch that was reimplemented differently - Retiring an approach while preserving the fact that we considered it
Important: -s ours (strategy) is different from -X ours (strategy option). The strategy discards everything from their side. The option only kicks in for conflicting hunks.
Strategy Options: Fine-Tuning Conflict Resolution¶
Within the recursive/ort strategy, options control how conflicts are handled:
# On conflict, prefer our changes
git merge -X ours feature-branch
# On conflict, prefer their changes
git merge -X theirs feature-branch
# Ignore whitespace differences when detecting conflicts
git merge -X ignore-space-change feature-branch
-X theirs is my go-to when pulling upstream changes into a branch where I want to accept whatever main has. It still merges cleanly where possible — only actual conflicts default to their version.
Octopus Merge: Integrating Multiple Branches Simultaneously¶
When merging more than two branches at once, git uses the octopus strategy:
Octopus refuses to run if there are conflicts — it only works for clean merges. I use it when integrating multiple independent features that don't overlap:
# Release branch integrating three non-conflicting features
git checkout release/2.4
git merge feature/auth feature/logging feature/metrics
This creates a single merge commit with multiple parents, which is cleaner than three sequential merges.
Subtree Merge: Integrating Another Project¶
When I need to pull another repository into a subdirectory (an alternative to submodules):
# Add the other project as a remote
git remote add library https://github.com/org/library.git
git fetch library
# Merge it into a subdirectory
git merge -s subtree --allow-unrelated-histories library/main
I've used subtree merges for vendoring dependencies and for combining formerly-separate repos into a monorepo while preserving all history.
My Decision Framework¶
After years of practice, here's how I choose:
| Scenario | Strategy | Command |
|---|---|---|
| Normal feature integration | recursive/ort + no-ff | git merge --no-ff |
| Updating my branch from main | rebase (not merge) | git rebase main |
| Pulling upstream without divergence | fast-forward only | git pull --ff-only |
| Resolving conflicting branch, prefer main | ort + theirs option | git merge -X theirs |
| Retiring a dead branch | ours strategy | git merge -s ours |
| Combining multiple clean features | octopus | git merge feat-a feat-b |
| Integrating external project | subtree | git merge -s subtree |
Merge Conflict Prevention¶
The best merge strategy is often "avoid conflicts in the first place":
- Keep branches short-lived — the longer a branch lives, the more it diverges
- Rebase on main frequently — daily if possible, so conflicts are small
- Modular architecture — teams working on different modules rarely conflict
- Communication — if two people touch the same file, talk before merging
Key Takeaway¶
git merge is not a single tool — it's a family of strategies, each designed for specific integration patterns. The default works 80% of the time, but knowing when to use --no-ff, -X theirs, -s ours, or octopus is what separates competent git users from experts. Choose deliberately, and your history tells a clear story.
Tags: git, merge, strategies, workflow, conflict-resolution