Summary
.gitattributes enables text conversion for everything, unconditionally:
The binary list underneath it only names media and archive formats. Nothing currently in the repo is affected — I checked, and it's genuinely fine today (details below) — but any binary whose extension isn't on that list will be silently corrupted the moment it is committed, with no warning and no way to recover it from history afterwards. .exe, .dll, .nupkg, .pdb, .snk, .msi, .msix and .winmd are all unlisted.
Filing this because a downstream fork hit exactly that: xammen/BetterTrumpet inherited this file and now has 351 corrupted PE files in master — see xammen/BetterTrumpet#54 for the full autopsy.
Why nothing here is broken yet
Attribute lines are last-match-wins, so *.png binary and friends correctly override the blanket * text. Verified on .chocolatey/logo.png (blob 6d2acd8):
size 17913 PNG magic ok: True IHDR CRC ok: True
That file is a good demonstration of the mechanism, actually — the PNG signature is 89 50 4e 47 0d 0a 1a 0a, so it contains a literal CRLF. It survives only because *.png is listed. The four binary types tracked here (51 .png, 3 .gif, 2 .ico, 1 .pfx) are all covered.
How the corruption works, for reference
With text conversion forced on, git add runs the clean filter (CRLF → LF) over the file. In a PE image that strips CR bytes throughout, including the one in the DOS stub, which ends mode.\r\r\n$ (0d 0d 0a 24). It becomes mode.\r\n$, one byte shorter, and the e_lfanew offset stored at 0x3C no longer points at the PE\0\0 signature:
healthy 00000070 6d 6f 64 65 2e 0d 0d 0a 24 ... mode....$
corrupted 00000070 6d 6f 64 65 2e 0d 0a 24 00 ... mode...$
^ stripped, everything after shifts by one
The damage lands in the object store, so it is permanent: there is no revision to restore from. Checkout then re-inflates the file in the other direction (LF → CRLF), which can also make the round trip lossy, leaving git status permanently dirty on a freshly cloned file.
Suggested fix
-[core]
-* text eol=crlf
+* text=auto eol=crlf
+
+*.exe binary
+*.dll binary
+*.nupkg binary
+*.msi binary
+*.pdb binary
+*.snk binary
text=auto lets git's own NUL-byte heuristic bail out on binary content instead of forcing conversion, and the explicit entries are a deterministic backstop. Text files are unaffected by the switch as long as they're already stored LF-normalized, which is the normal case.
Two smaller notes while you're in the file:
- The leading
[core] line is a no-op. .gitattributes has no INI sections, so git reads it as a pattern — a character class matching one of c, o, r, e — with no attributes attached. It looks like it drifted in from a .gitconfig.
*.woff is listed but *.woff2 isn't.
Happy to send a PR if you'd like it, but since nothing is actually broken here I didn't want to push an unsolicited change to a config file.
Summary
.gitattributesenables text conversion for everything, unconditionally:The
binarylist underneath it only names media and archive formats. Nothing currently in the repo is affected — I checked, and it's genuinely fine today (details below) — but any binary whose extension isn't on that list will be silently corrupted the moment it is committed, with no warning and no way to recover it from history afterwards..exe,.dll,.nupkg,.pdb,.snk,.msi,.msixand.winmdare all unlisted.Filing this because a downstream fork hit exactly that: xammen/BetterTrumpet inherited this file and now has 351 corrupted PE files in
master— see xammen/BetterTrumpet#54 for the full autopsy.Why nothing here is broken yet
Attribute lines are last-match-wins, so
*.png binaryand friends correctly override the blanket* text. Verified on.chocolatey/logo.png(blob6d2acd8):That file is a good demonstration of the mechanism, actually — the PNG signature is
89 50 4e 47 0d 0a 1a 0a, so it contains a literal CRLF. It survives only because*.pngis listed. The four binary types tracked here (51.png, 3.gif, 2.ico, 1.pfx) are all covered.How the corruption works, for reference
With text conversion forced on,
git addruns the clean filter (CRLF → LF) over the file. In a PE image that strips CR bytes throughout, including the one in the DOS stub, which endsmode.\r\r\n$(0d 0d 0a 24). It becomesmode.\r\n$, one byte shorter, and thee_lfanewoffset stored at0x3Cno longer points at thePE\0\0signature:The damage lands in the object store, so it is permanent: there is no revision to restore from. Checkout then re-inflates the file in the other direction (LF → CRLF), which can also make the round trip lossy, leaving
git statuspermanently dirty on a freshly cloned file.Suggested fix
text=autolets git's own NUL-byte heuristic bail out on binary content instead of forcing conversion, and the explicit entries are a deterministic backstop. Text files are unaffected by the switch as long as they're already stored LF-normalized, which is the normal case.Two smaller notes while you're in the file:
[core]line is a no-op..gitattributeshas no INI sections, so git reads it as a pattern — a character class matching one ofc,o,r,e— with no attributes attached. It looks like it drifted in from a.gitconfig.*.woffis listed but*.woff2isn't.Happy to send a PR if you'd like it, but since nothing is actually broken here I didn't want to push an unsolicited change to a config file.