.gitattributes: blanket `* text eol=crlf` will silently corrupt any unlisted binary on commit
Summary
.gitattributes enables text conversion for everything, unconditionally:
* text eol=crlfThe 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: TrueThat 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 oneThe 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 binarytext=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..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.
Source: File-New-Project/EarTrumpet