The .gitattributes deep dive
2026-04-06
Tags: git
It's amazing just how many developers lament about line endings, corrupted assets, and other git-replated problems, oblivious to the fact that some simple config could solve their problems forever.
.gitattributes is a file that lives in the repository, which is far more convenient than asking all contributors to sync their personal git configurations or performing checks in a CI pipeline.
.gitattributes syntax
Each line in a .gitattributes file starts with a pattern. Note that a pattern can select multiple file types at the same time by using the syntax {a,b}. For example:
*.txt # .txt files
*.{png,jpg} # .png and .jpg files
*.{ba,fi,z}sh # .bash, .fish, and .zsh files
They are then given one or more attributes, which might be macros or key-value pairs. Macros can be prefixed with - to remove them if they were already implied implicitly.
# *.txt are text files and should have LF line endings, and should not be merged
*.txt text eol=lf -merge
Macros are shorthands for collections of attributes. They can be defined as:
[attr]mymacro -prop1 -prop2
Git has a built-in macro binary, which expands to -diff -merge -text (i.e., for binary files, do not show diffs, do not attempt to merge, and do not mess with line endings).
As demonstrated above, you can also add comments by beginning a line with "#".
Managing line endings
One of the most common plights in a git repo is line endings. On Windows, line endings have tradditionally been a carriage return followed by a line feed (CRLF), while on Linux, MacOS, and other Unix-like systems, it's just the line feed (LF).
In many repositories, even when all contributors are using the same operating system, you'll find random invisible changes, where some tool has decided to reformat the line endings in a file.
This can be configured on the user level, with config value core.autocrlf. It has the following values:
* "false" (default for Windows): git won't touch line endings at all - they'll check out as-is and commit as-is.
* "true": git will convert to CRLF on checkout, and LF on checkin.
* "input" (default for Linux and MacOS): git will change line endings to CRLF to LF on commit, and them as they are on checkout.
Instead of asking every contributor to set this config value to whatever you want your repository to use, you can specify the line endings appropriate for each file type using .gitattributes.
First, specify the pattern you want to target. Then, for text files, add the attributes text and either eol=crlf or eol=lf.
*.ps1 text eol=crlf
*.plist text eol=lf
Sometimes, you want git not to touch a file at all. You don't want to convert a "0D 0A" to "0A" in an image, for example, because that would corrupt it. To make sure git ignores a file in terms of line endings, add the binary attribute or remove the text attribute.
*.png binary
# or
*.png -text
Hinting to your difftool
Sometimes, your difftool doesn't get things quite right, and produces a nonsensical diff for certain file types. Git attributes can hint how to interpret files for the purpose of diffing, and even point git towards differing programs for different diff types.
A razor file looks very similar to a HTML file, especially when code-behind is used. To tell git to diff razor files as if they were HTML:
*.razor diff=html
You can add additional diff kinds in .gitconfig (or .git/config) by providing an executable for converting a file to text. The below example uses "exif" to extract image metadata for diffing, since you probably don't want to see the whome images' raw bytes in a diff.
[diff "image"]
textconf=exif
Configuring merge strategies
You can tell git not to attempt to merge files by using removing the merge attribute with -merge.
*.png -merge
You can also set a merge strategy by referencing the name of a defined strategy, e.g.:
# in .gitconfig
[merge "local"]
name = "Always keep our local file"
driver = true
# in .gitattributes
*.png merge=local
Filters and LFS
Gitattributes can configure actions known as "filters" that are automatically run upon files on commit ("smudging") and checkout ("cleaning").
First, define a filter in .gitconfig:
[filter.jsonFilter]
clean = "/bin/maxifyJson"
smudge = "/bin/minifyJson"
Then, apply it in .gitattributes:
*.json filter=jsonFilter
This is the core feature behind Git LFS, which compresses large files on checkin to reduce the storage requirement of a repository.