Git Merge Strategies — The 'Wrong' Strategy Caused Silent Data Loss
A recursive merge with the wrong rename-detection setting silently dropped 40 renamed files.
20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.
- ✓Deep devops experience in a production environment
- ✓Familiarity with debugging, profiling, and performance analysis
- ✓Understanding of distributed systems and failure modes
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.
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
Git provides several merge strategies via -s <strategy>:
- 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 recursivewith-Xoptions 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>.
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.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.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.40 Renamed Files Silently Dropped by Wrong Merge Strategy
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.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.- 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 patiencefor merges with extensive renames. - Always verify post-merge
git diff --name-statusagainst expected changes.
ort strategy (Git picks it automatically)git merge -s recursive -X patience for better rename detectiongit merge -s ours — discards all changes from the other branchgit merge -X theirs — risky, use only when you're certaingit merge -s subtree for merging subprojectsKey takeaways
-X rename-threshold for heavy renames-X theirs/-X ours for automatic conflict resolution (with caution)20+ years shipping production infrastructure and CI/CD at scale. Notes here come from systems that actually shipped.
That's Git. Mark it forged?
3 min read · try the examples if you haven't