Resolving Complex Binary Conflicts in Git Jump to heading
When Git attempts to reconcile divergent histories, it relies on the 3-way merge algorithm to compute a unified diff. For text files, this enables granular conflict markers. For binary payloads, the diff engine cannot parse semantic boundaries, so it marks the file conflicted without writing any markers β the working tree simply contains one sideβs blob. Pipeline execution halts. Manual editing is impossible. This page is a focused recipe within 3-Way Merge Fundamentals, part of the broader Conflict Resolution & Safe Merge Operations topic area.
The correct approach is deterministic: classify the binary type, choose the right resolution strategy, and automate it so the same conflict never requires human intervention twice.
SAFETY WARNING: Never open a binary file in a text editor during a merge conflict. Modifying raw bytes corrupts the file header. Use
git checkout --ours/--theirsor a structured external tool β never raw editing.
When to Use This Approach Jump to heading
Apply this recipe when:
git status --porcelainreports aUUorAAcode for a non-text file (.png,.psd,.wasm,.bin,.sketch, lockfiles, etc.)git mergeexits non-zero and the message names a binary file as unmerged- Your CI pipeline fails with βbinary file β¦ needs mergeβ without producing usable conflict markers
- A lockfile merge produces hash mismatches that break dependency installation β an alternative is to use squash and fixup strategies to avoid the conflicting commits ever landing together
- A recurring binary conflict has been resolved manually more than once β at that point automation via
.gitattributesis overdue
Binary Conflict Decision Flow Jump to heading
The diagram below maps the four key questions that determine which resolution path to take.
Step-by-Step Recipe Jump to heading
Step 1 β Diagnose the conflict Jump to heading
Start with machine-readable status output. The UU code means both sides modified the same file:
# Show only unmerged files
git status --porcelain | grep '^UU' Inspect the index stages. Git maintains three object versions for every conflicted file:
- Stage 1 (
:.1:): common ancestor - Stage 2 (
:.2:): current branch - Stage 3 (
:.3:): incoming branch
# List all unmerged paths with their stage numbers
git ls-files -u Extract each blob to a temporary path for safe inspection:
# Replace "path/to/asset.psd" with the actual conflicted file path
git show :1:path/to/asset.psd > /tmp/base.bin
git show :2:path/to/asset.psd > /tmp/ours.bin
git show :3:path/to/asset.psd > /tmp/theirs.bin
# Verify actual content type β extensions frequently lie
file --mime-type /tmp/base.bin /tmp/ours.bin /tmp/theirs.bin SAFETY WARNING: Use
/tmp/for inspection blobs. Files placed in the working tree can bypass.gitignorerules and be accidentally staged. Remove them immediately:rm -f /tmp/base.bin /tmp/ours.bin /tmp/theirs.bin.
Verification: git ls-files -u lists three rows for the conflicted path (stages 1, 2, 3). If only two stages appear, one side added the file fresh β resolve by accepting the relevant side.
Step 2 β Resolve compiled artifacts and build outputs Jump to heading
Compiled binaries (.o, .class, .wasm) are derived from source and should not live in version control. Resolve by accepting the version that aligns with the authoritative build pipeline:
# Accept the incoming branch's artifact
git checkout --theirs -- dist/app.wasm
git add dist/app.wasm
git commit -m "resolve: accept build artifact from incoming branch" Or retain the current branch when it is authoritative:
# Accept the local version
git checkout --ours -- dist/app.wasm
git add dist/app.wasm SAFETY WARNING: Never attempt byte-level editing of compiled binaries. Broken symbol tables and corrupt runtime loaders result. Add them to
.gitignoreto prevent the problem from recurring.
Verification: git show HEAD:dist/app.wasm | file --mime-type - should return the expected MIME type with no merge metadata.
Step 3 β Resolve serialized lockfiles Jump to heading
Lockfiles encode resolved dependency graphs with cryptographic hashes. Manual merging introduces supply-chain drift β any hand-edited version will have inconsistent hashes. Accept one side, then regenerate:
# Accept the incoming branch version as the base
git checkout --theirs -- package-lock.json
# Regenerate using the native resolver
npm install
# Stage the freshly generated lockfile
git add package-lock.json
git commit -m "resolve: regenerate lockfile after dependency conflict" Run npm audit (or cargo audit, go mod verify) after regeneration to confirm no new vulnerabilities were introduced.
SAFETY WARNING: Never commit a partially-merged lockfile. Hash inconsistencies cause CI/CD pipeline resolution failures that are hard to diagnose. Always regenerate before staging.
Verification: npm ci (or the equivalent for your package manager) must exit 0 on a fresh clone. If it fails, the lockfile is still inconsistent.
Step 4 β Resolve media and design assets Jump to heading
Design files require parallel editing controls at the workflow level, not at the Git diff level. Restore the authoritative version and adopt Git LFS for future tracking:
# Restore the authoritative version from the branch that owns this asset
git checkout design/v2-assets -- assets/hero.psd
git add assets/hero.psd
git commit -m "resolve: restore authoritative asset from design/v2-assets" Document in the commit message which team or workflow produced the authoritative version. If Git LFS is not yet configured for this repository, this is the moment to add it β the interactive rebase workflows that follow will be cleaner once large binary blobs are stored outside the object database.
SAFETY WARNING: Standard Git delta compression performs poorly on large binary files. Configure Git LFS for design assets before the repository grows unwieldy. Once files are in LFS, their pointers are text and can be merged, but the underlying objects still require the same deterministic workflow above.
Verification: git lfs ls-files should list the resolved asset after the commit. If LFS was not set up beforehand, install it now and migrate: git lfs migrate import --include="*.psd".
Step 5 β Automate with .gitattributes and merge drivers Jump to heading
Recurring binary conflicts signal a missing repository policy. Define file patterns in .gitattributes:
# Treat as opaque blobs β no text diff, no merge attempt
*.bin binary
*.png binary
# Custom driver for design files β always keep the current branch version
*.psd merge=keep-ours Register the driver in the repository configuration. The driver receives three arguments substituted for %O (ancestor), %A (current branch β must contain the result on exit 0), and %B (incoming branch):
# "keep-ours" driver: leave %A unchanged and signal success
git config merge.keep-ours.name "Keep current-branch version"
git config merge.keep-ours.driver "true" The true command always exits 0 without modifying %A, which is exactly the keep-ours semantic. For a keep-theirs policy, copy %B over %A:
# "keep-theirs" driver: overwrite %A with %B
git config merge.keep-theirs.name "Keep incoming-branch version"
git config merge.keep-theirs.driver "cp %B %A" Commit .gitattributes to the repository root so all clones inherit the policy. Distribute driver registration via a team setup script β pair this with your local hook configuration onboarding step so new engineers get the drivers installed automatically.
SAFETY WARNING: Test driver scripts in an isolated repository before committing
.gitattributes. A driver that exits non-zero for every file will halt every merge across the team.
Verification: Create a test conflict on a scratch branch. Run git merge β the driver should resolve it automatically and git status should be clean with no unmerged paths.
Step 6 β Validate integrity and finalize Jump to heading
Resolution does not guarantee integrity. Validate before finalizing.
Confirm no residual conflict markers remain in adjacent text files:
git diff --check Verify the binary object hash matches the expected artifact from CI:
# Compare this hash against your artifact registry or the known-good CI output
git hash-object dist/app.wasm Finalize the merge:
git merge --continue SAFETY WARNING: Do not bypass CI validation gates when binary conflicts have occurred. Hash mismatches indicate incomplete resolution or toolchain drift. Enforce branch protection rules that require passing pipeline runs before merge β the pre-push validation rules pattern covers this enforcement layer.
Verification: git log --oneline -1 should show the merge commit with a clean message. git status must be empty.
Validation Checklist Jump to heading
Frequently Asked Questions Jump to heading
Why doesnβt Git write conflict markers into binary files? Jump to heading
Gitβs diff engine operates on text lines. Binary blobs have no line boundaries, so there is no meaningful position at which to insert <<<<<<< / ======= / >>>>>>> markers. Git marks the file conflicted at the index level and leaves one sideβs blob in the working tree unchanged.
Can I use git mergetool for binary conflicts? Jump to heading
Only if you configure a tool that understands the format β for example, a PSD-aware editor for Photoshop files. The default mergetool options for text (vimdiff, meld, etc.) will corrupt binary content. Use git checkout --ours / --theirs or a registered custom merge driver instead.
How do I prevent binary conflicts from recurring? Jump to heading
Commit a .gitattributes file that marks the pattern as binary and assigns a named merge driver. The driver encodes your resolution policy (keep-ours, keep-theirs, or regenerate) so every future merge on any clone resolves automatically without human intervention. For lockfiles specifically, combine the driver with a post-merge hook that re-runs the package managerβs install command.
Related Jump to heading
- 3-Way Merge Fundamentals β the parent cluster explaining how Git locates the merge base, computes diffs, and decides where conflicts occur
- Safe git rebase -i for Shared Branches β when to rewrite history instead of resolving a merge, and how to do it safely on branches others are watching
- When to Use git revert vs git reset β if a binary conflict resolution was committed incorrectly, these are the safe undo paths
- Cherry-Picking Hotfixes Across Release Branches β binary assets that differ between release branches often resurface as cherry-pick conflicts; the same classification logic applies