.gitattributes Tester
Paste a .gitattributes and a list of paths to see every attribute git ends up setting on each path, and which line — or which macro — set it.
Paths
9
Attribute files
2
Rules
9
Findings
3
Consulted, nearest first: src/.gitattributes → .gitattributes
| Attribute | State | Set by |
|---|---|---|
| diffA value names a diff driver (hunk headers, textconv); -diff makes git treat the file as binary in every diff. | javascript | src/.gitattributes:1 *.js |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | auto | .gitattributes:1 * |
Consulted, nearest first: src/.gitattributes → .gitattributes
| Attribute | State | Set by |
|---|---|---|
| diffA value names a diff driver (hunk headers, textconv); -diff makes git treat the file as binary in every diff. | javascript | src/.gitattributes:1 *.jsalso written at .gitattributes:9 (minified) |
| linguist-generatedGitHub's, not git's: only Linguist reads it, for language statistics and collapsed diffs. | set | .gitattributes:9 *.min.jsexpanded from macro minified |
| minifiedA macro: setting it applies everything in its [attr] definition. | set | .gitattributes:9 *.min.js |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | auto | .gitattributes:1 * |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| eolThe ending written into the working tree, lf or crlf. It does nothing while text is unset. | lf | .gitattributes:3 *.sh |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | set | .gitattributes:3 *.shalso written at .gitattributes:1 |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| eolThe ending written into the working tree, lf or crlf. It does nothing while text is unset. | crlf | .gitattributes:4 *.bat |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | set | .gitattributes:4 *.batalso written at .gitattributes:1 |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| binaryNot an attribute git reads: a built-in macro standing for -diff -merge -text. | set | .gitattributes:5 *.png |
| diffA value names a diff driver (hunk headers, textconv); -diff makes git treat the file as binary in every diff. | unset | .gitattributes:5 *.pngexpanded from macro binary |
| mergeThe merge driver for conflicts; -merge makes git refuse to merge the file and keep your side. | unset | .gitattributes:5 *.pngexpanded from macro binary |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | unset | .gitattributes:5 *.pngexpanded from macro binaryalso written at .gitattributes:1 |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| diffA value names a diff driver (hunk headers, textconv); -diff makes git treat the file as binary in every diff. | lfs | .gitattributes:6 *.psd |
| filterA clean/smudge pair rewriting content between the repository and the working tree — this is how Git LFS works. | lfs | .gitattributes:6 *.psd |
| mergeThe merge driver for conflicts; -merge makes git refuse to merge the file and keep your side. | lfs | .gitattributes:6 *.psd |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | unset | .gitattributes:6 *.psdalso written at .gitattributes:1 |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| export-ignoreLeaves the path out of git archive, so it is missing from release tarballs while staying in the repository. | set | .gitattributes:11 .github/** |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | auto | .gitattributes:1 * |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| export-substgit archive expands $Format:...$ placeholders inside the file. | set | .gitattributes:12 VERSION |
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | auto | .gitattributes:1 * |
Consulted, nearest first: .gitattributes
| Attribute | State | Set by |
|---|---|---|
| textWhether git normalises line endings in the repository. text=auto converts only files it detects as text. | auto | .gitattributes:1 * |
None of these is an error git reports. They are the lines that most often do not do what they look like.
- .gitattributes:1 — a macro and an explicit line disagree about this attribute, and only the later one counts: text · binary
- .gitattributes:9 — written, matched, and beaten for every path here: diff · minified
- .gitattributes:9 — a macro and an explicit line disagree about this attribute, and only the later one counts: diff · minified
Resolved in your browser the way git resolves it — nothing is uploaded. The same answers come from git check-attr --all -- <path> in a real checkout.
How to use
- Paste your
.gitattributesinto the first box exactly as it is on disk. Comments, blank lines, trailing spaces, quoted patterns with a space in them and an escaped\!are all read the way git reads them. - To model more than one file, start each extra one with a marker line:
# path: src/.gitattributes. Everything before the first marker belongs to the root file — that is where macros have to live, because git honours[attr]nowhere else. - List the paths you want to test in the second box, one per line, relative to the repository root. Write a trailing slash only if you mean a directory; ordinary files never have one, which is why directory-only patterns almost never fire.
- Read each card. The table gives the attribute, its state — set, unset, a value, or unspecified — and the file, line and pattern that decided it, plus the macro chain when the state came out of an expansion rather than off the line itself.
- Check the findings panel underneath for the lines that read correctly and do nothing: dead patterns, always-overridden states, a macro contradicting an explicit setting, and a
[attr]definition sitting in the wrong file.
About this tool
ShowHide
A .gitattributes line is a pattern followed by attribute states, and the answer to "why is git treating this file as binary?" is almost never the line you are looking at. Precedence runs backwards: within a file the LAST matching line wins, and so does the last state written on a single line. Across files, a .gitattributes further down the tree outranks the one at the root for every path beneath it. Paste your file — or several, at different depths — plus the paths you want to test, and this tester shows the whole resolved attribute set for each path, with the file, line number and pattern that decided every entry. It is the same answer git check-attr --all -- <path> gives in a real checkout, worked out in your browser.
The other half of the confusion is macros. binary is not an attribute git reads at all; it is a built-in macro standing for -diff -merge -text, so a reader searching the file for diff finds nothing and concludes the file says nothing about diffs. Your own [attr]name ... macros behave the same way, and only count when they are defined in the top-level file — git refuses a definition in a subdirectory and quietly keeps the name as an ordinary attribute. Every expanded state here is labelled with the macro chain it came through, so an unexplained -diff traces back to the line that triggered it.
The findings panel names the lines that do not do what they look like: a pattern that matches none of the paths you listed, an attribute written and then beaten everywhere, text decided by two files with two different answers, a binary sitting next to a contradicting diff=, a [attr] definition git refused, a name git rejects (which discards the whole line with it), and a trailing slash — which restricts a pattern to directories, and git only ever asks about files.
What it cannot tell you: whether the attribute is doing anything useful. A diff=swift driver has to exist in your config before git will call it, filter=lfs needs Git LFS installed, and linguist-* is GitHub's, read by nothing in git itself. Attributes from .git/info/attributes and from core.attributesFile are outside a pasted repository and are not modelled here, and text and eol only rewrite content when a file is checked out or added, so changing the file alone changes nothing already in your working tree.
Frequently asked questions
Why is git treating my file as binary when nothing says -diff?
Because binary says it. It is not an attribute git looks up; it is a macro built into git that expands to -diff -merge -text, so one word on one line turns off diffs, turns off merging and turns off line-ending normalisation at once. Searching your .gitattributes for diff will not find it. The expansion happens exactly where the macro sits in the file, at that line's priority — which is why *.png binary followed by *.png diff=exif gives you the exif driver, while the same two lines in the opposite order give you diff: unset. This tester labels every state that arrived through a macro with the chain it came through, and flags the pair when a macro and an explicit line disagree about the same attribute.
Two patterns match my file. Which one wins?
The later one, every time, and that catches people out because so many config formats are first-match-wins. Inside one file the last matching line decides; if one line writes the same attribute twice, the state furthest to the right decides, so -text text leaves text set. Across files, the .gitattributes closest to the file wins over the one above it: a root * -diff is undone for everything under src/ by a single *.js diff=javascript in src/.gitattributes. Git also reads .git/info/attributes above all of them and core.attributesFile below them, neither of which lives in the repository, so neither is modelled here — if a resolved value surprises you and no line in your files explains it, those two are the places to look next.
What does !attr mean, and why is it different from -attr?
-attr sets the attribute to unset — a definite answer, and for something like text it means "never normalise this file". !attr says the opposite kind of thing: it makes the attribute unspecified, as though no rule had mentioned it at all. The important part is that it still occupies the slot. Once a matching !text has been read, every lower-priority rule — earlier lines in the same file, and every parent .gitattributes — stops being able to fill text in. That makes it the tool for carving one subtree out of a repository-wide default, and it is also why git check-attr --all shows nothing for that attribute: --all prints only attributes that ended up with a value, so an !attr is invisible unless you ask for the name explicitly.
Are the patterns the same as in .gitignore?
Almost. The matcher is the same one, so a pattern with no slash matches a bare filename at any depth below the file that holds it, a pattern with a slash anywhere is anchored to that file's directory, a leading / anchors it too, * and ? and [abc] stop at a separator, and ** is the only thing that crosses one. Two differences matter. A leading ! is not a negation here: git refuses the line outright, prints a warning and drops it, so you need \! for a filename that really starts with an exclamation mark. And a trailing slash, which in .gitignore restricts a rule to directories, is close to useless in .gitattributes — attributes are looked up per file, never inherited from a directory entry, so logs/ text gives an attribute to something git will not ask about.
I changed text and eol and nothing happened. Why?
Because those two attributes are applied when content moves between the repository and your working tree, not when the file sits there. Files already checked out keep the endings they have, and files already staged keep the endings that were stored. The fix after editing the rules is git add --renormalize ., which re-runs the conversion over every tracked file and stages whatever actually changed; expect a large, boring commit the first time. text=auto only converts files git decides are text by looking for NUL bytes near the start, so a UTF-16 source file is treated as binary and left alone unless you mark it text working-tree-encoding=UTF-16. And eol does nothing at all while text is unset, which is the usual reason a lone eol=lf line has no effect.
Where do I define a macro, and why was mine ignored?
Only in the top-level .gitattributes at the root of the working tree, or in .git/info/attributes. Anywhere else git prints [attr]name ... not allowed and drops the definition — but the file still parses, so a line using that name keeps working as an ordinary custom attribute and nothing looks broken. Two more rules trip people up. A macro is expanded only where it is set: -mybin and mybin=maybe record a state for the name itself and expand nothing, so they do not undo the macro. And the definition may appear anywhere in the top-level file, including after the lines that use it, because macros are collected before any path is resolved.
Related tools
.gitignore Tester
Paste a .gitignore and a list of paths to see which are ignored and exactly which line decided — the answer git check-ignore -v gives.
Developer
EditorConfig Tester
Paste an .editorconfig and some file paths: see the property set each file resolves to, and which section on which line decided every value.
Developer
Git Packfile Inspector (.pack / .idx)
Read a .pack and .idx pair in the browser: every object with its type, size, offset, delta depth and base, totals by type and the biggest objects.
File
Line Endings & BOM Converter
Convert between LF, CRLF, and CR line endings and add or remove a UTF-8 BOM — with per-style counts so you can see how mixed the input was.
File
.gitignore Generator
Tick the languages, frameworks, editors, and operating systems you use — get one combined .gitignore ready to drop into a fresh repo.
Developer
WebAssembly Module Inspector
Inspect a .wasm file in your browser: section sizes, imports and exports with full types, memory pages, the start function, name and producers sections.
Developer