Home DevOps Git Merge Strategies — The 'Wrong' Strategy Caused Silent Data Loss
Advanced 3 min · July 07, 2026
Git Merge Strategies: Recursive, Ours, Theirs and Octopus

Git Merge Strategies — The 'Wrong' Strategy Caused Silent Data Loss

A recursive merge with the wrong rename-detection setting silently dropped 40 renamed files.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Production
production tested
July 07, 2026
last updated
214
articles · all by Naren
Before you start⏱ 20 min
  • Deep devops experience in a production environment
  • Familiarity with debugging, profiling, and performance analysis
  • Understanding of distributed systems and failure modes
 ● Production Incident
Quick Answer

Git picks the merge strategy automatically. git merge -s recursive -X patience uses patience diff for cleaner renames. git merge -s ours discards the other branch entirely. git merge -X theirs auto-resolves all conflicts in favor of the incoming branch.

✦ Definition~90s read
What is Git Merge Strategies?

Git merge strategies control how Git combines divergent branches. The default 'recursive' strategy handles most cases. Others: 'resolve', 'octopus', 'ours', 'subtree'. The ort strategy (default since Git 2.33) replaced recursive for performance.

Imagine two people editing the same document.
Plain-English First

Imagine two people editing the same document. Merge strategies are the RULES for what happens when they both changed the same paragraph. 'Ours' says keep my version. 'Theirs' says keep theirs. 'Recursive' (the default) intelligently merges both. 'Ort' is like recursive but faster and better at detecting when a paragraph was renamed vs deleted and recreated.

Most developers never think about merge strategies. Git's default 'ort' strategy handles 99% of merges automatically. But when it gets the 1% wrong, the results are silent and catastrophic: files that disappear without a conflict, renamed code that's treated as deleted and recreated, and merge commits that look clean but contain wrong code. The incident that triggered this guide: a team lost 40 renamed files because the default strategy didn't detect the renames. This guide covers when to override the default, the strategy options Git provides, and how to prevent silent merge corruption.

Available Merge Strategies and When to Use Them

  • ort (default since Git 2.33): Replaced 'recursive'. Faster, handles renames better, uses less memory. Handles 99% of merges.
  • recursive: The previous default. Use -s recursive with -X options for fine control.
  • resolve: Two-way merge. Fast but can't handle complex histories. Rarely needed.
  • octopus: Merges more than two branches at once. Used for git merge feature1 feature2 feature3.
  • ours: Keeps your branch's files entirely. The other branch's changes are recorded as merged but don't affect files.
  • subtree: For merging a subproject into a subdirectory.

The -X options for recursive/ort: patience (better diff quality), ignore-space-change, renormalize, no-renames, rename-threshold=<N>.

01_merge_strategies.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Default merge (ort strategy)
git merge feature-branch

# Explicit recursive with patience diff
git merge -s recursive -X patience feature-branch

# Keep our version on conflicts
git merge -X ours feature-branch

# Accept their version on conflicts
git merge -X theirs feature-branch

# Merge with custom rename threshold
git merge -X rename-threshold=30 feature-branch

# Ours strategy (discard all incoming changes)
git merge -s ours feature-branch -m 'Merge feature (keeping our files)'

# Octopus merge (3+ branches)
git merge feature-a feature-b feature-c
Output
Merge made by the 'ort' strategy.
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit.
Merge made by the 'recursive' strategy.
Merge made by the 'ours' strategy.
Set Your Preferred Strategy in Git Config
git config merge.strategy recursive sets the default. git config merge.renameLimit 10000 increases rename detection limit. git config merge.conflictStyle diff3 shows the original (common ancestor) version in conflict markers, making resolutions easier.
Production Insight
For monorepos with frequent renames, set merge.renameLimit to a high value globally. Add a CI check that runs git diff --name-status HEAD^! | grep -c '^R' to verify rename detection worked correctly after merges. Teams using squash-merge avoid rename issues entirely because there's no merge commit — just a single diff. Consider squash-merge as the default PR strategy if rename-heavy workflows are common.
Key Takeaway
Default ort strategy handles 99% of merges. Use -X patience for better rename detection. Use -X theirs/-X ours for conflict auto-resolution. -s ours discards all incoming changes. Set merge.renameLimit high for monorepos.
● Production incidentPOST-MORTEMseverity: high

40 Renamed Files Silently Dropped by Wrong Merge Strategy

Symptom
A team refactored a monorepo, renaming 40+ files to follow a new naming convention. When they merged the refactor branch to main, Git's default strategy treated the renames as 'delete + create' instead of 'rename'. The resulting merge had all 40 files at their new paths AND the old paths — duplicated. The old paths took precedence in the build.
Assumption
The merge completed with zero conflicts. CI passed. The team assumed a clean merge. Two weeks later, a bug was traced to code running from the old (wrong) file paths.
Root cause
1. The refactor branch renamed 40+ files using git mv. 2. The main branch had no changes to those files. 3. Git's rename detection threshold (50% similarity by default) was not met for some files because they had significant content changes alongside the rename. 4. Git treated those files as 'deleted from old path, created at new path'. 5. The merge completed with no conflicts — both old and new paths existed in the result. 6. The build system picked up the old paths (which had stale content), not the new paths.
Fix
1. Identified the affected files: git diff --name-status main..merge-result | grep '^A\|^D' showed both deletes and adds. 2. Re-ran the merge with explicit rename detection: git merge -s recursive -X rename-threshold=30 refactor-branch. 3. This time Git detected the renames correctly. 4. Cleaned up the duplicate files: git rm on old paths for the 40 affected files. 5. Set git config merge.renameLimit 10000 globally.
Key lesson
  • Git's rename detection has a 50% similarity threshold by default.
  • Files that change significantly during a rename may fall below this threshold and be treated as delete+create.
  • Use -X rename-threshold=<N> or -X patience for merges with extensive renames.
  • Always verify post-merge git diff --name-status against expected changes.
Production debug guideUse this when production is on fire5 entries
Symptom · 01
Standard merge with no special needs
Fix
Use default ort strategy (Git picks it automatically)
Symptom · 02
Merge with extensive file renames
Fix
Use git merge -s recursive -X patience for better rename detection
Symptom · 03
Want to keep your version completely
Fix
Use git merge -s ours — discards all changes from the other branch
Symptom · 04
Auto-resolve all conflicts in favor of incoming
Fix
Use git merge -X theirs — risky, use only when you're certain
Symptom · 05
Two branches with completely different files
Fix
Use git merge -s subtree for merging subprojects

Key takeaways

1
ort is the default strategy since Git 2.33
faster and better than recursive
2
Rename detection has a 50% similarity threshold
use -X rename-threshold for heavy renames
3
Use -X theirs/-X ours for automatic conflict resolution (with caution)
4
Always verify post-merge diff when renames are involved
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 07, 2026
last updated
214
articles · all by Naren
🔥

That's Git. Mark it forged?

3 min read · try the examples if you haven't

Previous
Git Reflog: Recover Lost Commits and Branches
31 / 47 · Git
Next
Signed Commits with GPG: Verify Your Identity in Git