Git.gitattributes File

.gitattributes File

.gitattributes tells Git how to handle specific paths differently — things like line-ending conversion, how to diff binary files, which files should be treated as “generated” (and hidden from PR reviews), and many advanced features. Where .gitignore says “Git, leave these alone”, .gitattributes says “Git, treat these specially”.

Where it lives

Put a file called .gitattributes in the root of your repo (or in any subfolder). Commit it. Its rules apply to all contributors automatically — that is the whole point.

File format

One pattern per line + attributes

Text
pattern    attribute1 attribute2=value -attribute3
# comments start with #

The pattern follows the same rules as .gitignore. The attributes change how Git handles matching files.

The most common use: line endings

Force LF for cross-platform repos

Text
# Default — let Git decide based on content
* text=auto

# Force LF for these (shell scripts must have LF on Linux)
*.sh      text eol=lf
*.bash    text eol=lf
Makefile  text eol=lf

# Force CRLF for Windows scripts
*.bat     text eol=crlf
*.cmd     text eol=crlf

# Treat these as binary (no line-ending conversion, no diff)
*.png     binary
*.jpg     binary
*.pdf     binary
*.zip     binary
*.woff2   binary
The text and binary attributes
  • text — file is treated as text (line endings normalised on commit, converted on checkout).

  • text=auto — Git decides based on content sniffing.

  • eol=lf or eol=crlf — force a specific line ending on checkout.

  • binary — shortcut for -text -diff — never convert, never diff.

Showing better diffs for specific filetypes

Custom diff drivers

Text
*.go    diff=golang
*.py    diff=python
*.html  diff=html
*.css   diff=css
*.md    diff=markdown
*.json  diff=json

Each of these tells Git: “when diffing this file type, use the language-aware function header detector.” git diff will then show function names in hunk headers, making review easier.

Marking files as generated

linguist-generated tells GitHub to hide it

Text
# Hide minified bundles from PR diffs (GitHub-specific)
dist/**           linguist-generated=true
**/*.min.js       linguist-generated=true
**/*.bundle.js    linguist-generated=true
package-lock.json linguist-generated=true
yarn.lock         linguist-generated=true

# Override language detection — GitHub may misidentify these
*.h               linguist-language=C++
src/vendor/**     linguist-vendored=true
docs/**           linguist-documentation=true
Export-ignore — exclude files from archives

Text
# When someone runs 'git archive' to make a tarball, skip these
.github/      export-ignore
tests/        export-ignore
.editorconfig export-ignore
.eslintrc.js  export-ignore
Filters — automate content transformation

Smudge / clean filters

Text
*.lfs  filter=lfs diff=lfs merge=lfs -text   # used by Git LFS

# Strip Jupyter notebook outputs on commit
*.ipynb filter=nbstripout

Filters run a “clean” program on commit and a “smudge” program on checkout. Combined with git config filter.X.clean and filter.X.smudge, you can transparently transform files.

Merge strategies per file

Text
# Always take the version from the branch being merged in
CHANGELOG.md  merge=ours

# Refuse to auto-merge — flag a conflict instead (use sparingly)
*.config      merge=binary
Limiting language detection on GitHub

Common linguist overrides

Text
# Vendor directories — don't include in language stats
vendor/**         linguist-vendored
third_party/**    linguist-vendored

# Documentation
docs/**           linguist-documentation
*.md              linguist-documentation
Verifying attributes

What attributes apply to a path?

Bash
git check-attr -a src/app.js
# src/app.js: diff: javascript
# src/app.js: text: auto

git check-attr text eol -- src/app.js
# src/app.js: text: auto
# src/app.js: eol: unspecified
Applying attribute changes to existing files

One-time renormalization

Bash
# After editing .gitattributes
git add --renormalize .
git commit -m "Renormalize line endings per .gitattributes"
A practical .gitattributes for most repos

Drop this in your repo root

Text
# ─── Line endings ───
* text=auto

*.sh   text eol=lf
*.bash text eol=lf
*.bat  text eol=crlf
*.cmd  text eol=crlf

# ─── Binary file types ───
*.png  binary
*.jpg  binary
*.jpeg binary
*.gif  binary
*.webp binary
*.ico  binary
*.pdf  binary
*.zip  binary
*.gz   binary
*.tar  binary
*.woff binary
*.woff2 binary

# ─── Hide noisy files from PR diffs ───
package-lock.json linguist-generated
yarn.lock         linguist-generated
*.min.js          linguist-generated
dist/**           linguist-generated

# ─── Vendor / docs ───
vendor/**         linguist-vendored
third_party/**    linguist-vendored
docs/**           linguist-documentation
.gitattributes vs .gitignore
`.gitignore` decides whether a file is *seen* by Git at all. `.gitattributes` decides how Git *handles* the files it does see.
Tip
Set up `.gitattributes` on day one of a project, especially in teams with mixed operating systems. It is the cure for those confusing PRs where “every line of every file changed” — almost always a line-ending issue that this file fixes permanently.