You ran git merge feature/design-refresh and everything looked fine until Git printed CONFLICT (content): Merge conflict in assets/logo.png and stopped. Opening the file in your editor is useless — you see garbage bytes, not the <<<<<<< HEAD markers you get on text files. git mergetool launches a hex view that doesn’t help either. Binary files (images, compiled artifacts, SQLite databases, .xlsx workbooks) can’t be line-diffed, so Git refuses to guess which side is correct and hands the decision to you.
Fastest fix: decide which whole version you want, then run one of these on the conflicted path, stage it, and continue:
git checkout --ours -- assets/logo.png # keep your current branch's version
# or
git checkout --theirs -- assets/logo.png # take the incoming branch's version
git add assets/logo.png
git merge --continue --no-edit
There is no partial merge for a binary — you keep one side or the other in full. The rest of this guide helps you (1) figure out which side you actually want and (2) handle the trickier cases: a file deleted on one side, a stray build artifact, or a Git LFS pointer mismatch.
Which bucket are you in?
Run the diagnostic in the right column, then jump to the matching cause below.
Symptom in git status / logs | Likely cause | Diagnostic command |
|---|---|---|
both modified: <file> | Both branches edited the same binary | git log --oneline --all -- <file> |
deleted by us / deleted by them | Modify/delete conflict | git status |
File matches a build-output pattern (*.pyc, dist/) | Generated artifact committed on both sides | git check-ignore -v <file> |
| One side is a tiny text pointer, the other is large | Git LFS pointer vs. real bytes | git lfs status |
| Large delta but no intended edit | App re-saved the file (metadata/compression) | git diff --stat HEAD MERGE_HEAD -- <file> |
Common causes
Ordered by hit rate, highest first.
1. Both branches modified the same binary independently
Two developers each updated assets/logo.png on their own branches — one resized it, one changed the colors. Git sees two different byte sequences for the same path and cannot auto-merge them.
How to spot it: Run git log --oneline --all -- assets/logo.png. You will see two separate commits, each on a different branch, touching the same file after the branch point.
2. One branch deleted the file, the other modified it
A developer removed an outdated .dll on main while another branch shipped a newer version of the same .dll. Git reports a “modify/delete” conflict.
How to spot it: git status shows deleted by us or deleted by them next to the file path. (deleted by us means your branch deleted it; deleted by them means the incoming branch did.)
3. Auto-generated binary was committed to both branches
Build artifacts (*.pyc, compiled .so, a bundled *.js.map) were committed to both branches at different points in history. Neither version is canonical — you actually want the file out of the repo entirely.
How to spot it: The file extension matches something a .gitignore rule should be catching. Confirm with git check-ignore -v <file> — if it prints the matching rule, the file should never have been tracked.
4. Git LFS pointer vs. actual bytes mismatch
One side stored the file as a real blob; the other stored a small Git LFS pointer (a few lines of text beginning with version https://git-lfs.github.com/spec/v1). Git sees two completely different byte sequences and marks a conflict even though logically only one person touched the real file.
How to spot it: git lfs status lists the file, and inspecting one side with git show :2:<file> | head -3 shows the version https://git-lfs.github.com/spec/v1 pointer header while the other side does not.
5. The file was re-saved and metadata changed
A .docx, .pdf, or .xlsx was re-saved by an application (or by the same app on a different OS) that rewrites internal compression or metadata, producing different bytes even though the human-visible content is identical.
How to spot it: git diff --stat HEAD MERGE_HEAD -- <file> shows a large binary delta despite no intentional edit. In this case the safest move is usually to keep --theirs (the incoming, presumably newer save) and re-verify the document by eye.
Shortest path to fix
Step 1: Back up before you touch anything
git tag backup/before-binary-fix
This is a one-command escape hatch. If you pick the wrong side, git reset --hard backup/before-binary-fix puts you back exactly where you started.
Step 2: Extract both versions so you can compare them
Git keeps the conflicting versions in the index under numbered stages: stage 2 is “ours” (your current branch), stage 3 is “theirs” (the incoming branch). Pull both out to temp files:
# "ours" — current branch (HEAD)
git show :2:assets/logo.png > ours_version.png
# "theirs" — incoming branch (MERGE_HEAD)
git show :3:assets/logo.png > theirs_version.png
Open both files in the matching application (image viewer, Excel, a PDF reader) and decide which is correct. For images, a quick git diff --stat HEAD MERGE_HEAD -- assets/logo.png also tells you which side changed more.
Step 3: Accept one side explicitly
# Keep the current branch's version
git checkout --ours -- assets/logo.png
# OR keep the incoming branch's version
git checkout --theirs -- assets/logo.png
Note: during a git rebase, --ours and --theirs are reversed — --ours is the branch you are rebasing onto, --theirs is your replayed commit. Check whether .git/rebase-merge exists (you’re rebasing) versus .git/MERGE_HEAD (you’re merging) before you choose.
Step 4: Stage the resolution and continue
git add assets/logo.png
git merge --continue --no-edit
git checkout only writes bytes to disk; git add is what tells Git the conflict is resolved. --no-edit accepts the auto-generated merge message instead of opening an editor.
Step 5: Handle the modify/delete case
If git status says deleted by us / deleted by them, you don’t pick a byte version — you decide whether the file survives:
# Keep the file (override the delete)
git checkout --theirs -- assets/logo.png # or --ours, whichever side still has it
git add assets/logo.png
git merge --continue --no-edit
# OR accept the delete (remove the file)
git rm assets/logo.png
git merge --continue --no-edit
Step 6: Confirm it’s fixed, then clean up
git status # should say "All conflicts fixed" / working tree clean
git ls-files -u # should print NOTHING (no unmerged entries left)
git show --stat HEAD # confirm the merge commit landed with the file you expect
rm -f ours_version.png theirs_version.png
git tag -d backup/before-binary-fix # only after you've verified the result
If git ls-files -u still lists the path, you skipped the git add in Step 4 — re-run it.
Prevention
- Add generated binary patterns to
.gitignorebefore anyone commits them:*.pyc,*.class,*.o,*.so,dist/**,build/**. - Move large binaries to Git LFS so each side merges as a small pointer file:
git lfs track "*.png" "*.psd" "*.ai"(then commit the updated.gitattributes). - For binaries where one branch should always win, add a merge driver in
.gitattributes— e.g.*.xlsx merge=ours, then enable it once withgit config merge.ours.driver true. Git will silently keep your current branch’s version for matching files in future merges, with no conflict markers. - Store design source files (Figma exports, PSD) in an asset-management system or shared drive instead of the repo.
- Establish a team convention that one person owns a given binary asset per branch, so two branches rarely touch it at once.
- For SQLite files, commit plaintext SQL migrations and regenerate the database from them rather than committing the binary
.db.
FAQ
Q: After git checkout --ours, the file still shows as conflicted in git status. Why?
A: git checkout only writes bytes to disk. You must run git add <file> afterward to tell Git the conflict is resolved. Verify with git ls-files -u — it should print nothing once every conflict is staged.
Q: In a rebase, did --ours and --theirs swap on me?
A: Yes. During git rebase, --ours refers to the branch you’re rebasing onto (the upstream/base) and --theirs is the commit being replayed (your work). This is the opposite of a merge and trips up almost everyone. If you’re unsure which mode you’re in, check for .git/rebase-merge (rebase) versus .git/MERGE_HEAD (merge).
Q: Is there a way to auto-resolve all binary conflicts to one side without interactive steps?
A: Two options. For a one-off merge, pass the strategy option: git merge -X ours feature/branch (or -X theirs) takes the chosen side in full for every conflict, including binaries. For a permanent per-pattern rule, add *.png merge=ours to .gitattributes and run git config merge.ours.driver true.
Q: Can I use a visual merge tool like Kaleidoscope for binaries?
A: Only if the tool understands the format. Kaleidoscope handles images and PDFs; for arbitrary binaries it falls back to a “pick one side” UI. Configure it with git config merge.tool kaleidoscope, then git mergetool launches it for each binary conflict. On macOS, git config merge.tool opendiff uses Xcode’s FileMerge, which previews some image formats.
Q: We accidentally committed a 150 MB binary and now every clone is slow. How do we fix history?
A: That’s a separate problem — rewriting history with git filter-repo. See Large File in History Blocks the Push for the step-by-step walkthrough.