Find Out Why Git Treats a File That Way with git check-attr
Use git check-attr to explain why Git treats a path as text, binary, mergeable or otherwise special, instead of guessing from .gitattributes. The guide uses Git 2.43.0 from the installed git-man package, version 2.43.0-1ubuntu7.3. Allow about fifteen minutes. You need a Git working tree and permission to read its files. These checks are read-only; no elevated privileges are required.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Start in the right repository
- 2. Query one attribute for one path
- 3. Inspect the attributes that actually apply
- 4. Connect the result to .gitattributes
- 5. Check staged rules instead of working-tree rules
- 6. Inspect a committed tree
- 7. Use stdin for batches and scripts
- 8. Recover from the usual wrong answer
1. Start in the right repository
Change to the working tree that contains the path you want to inspect. Use a real path in place of PATH/TO/FILE:
$ cd /path/to/repository
$ git rev-parse --show-toplevel
/path/to/repository
If git rev-parse fails, stop there. Running the check outside a work tree can give an error or inspect a different repository than the one you meant.
Tip
The command does not need sudo, and adding it can change which configuration and repository context Git sees.
Checkpoint
Confirm the installed version when the result needs to be reproducible.
$ git --version
git version 2.43.0
2. Query one attribute for one path
The simplest form takes an attribute name followed by one or more pathnames. This asks about the text attribute:
$ git check-attr text -- PATH/TO/FILE
PATH/TO/FILE: text: unspecified
The three common states mean different things:
setmeans the attribute is true.unsetmeans it is false.unspecifiedmeans no applicable rule has decided it.
A value is printed literally, such as text: auto or diff: java. Unspecified is not the same as unset: an unspecified text attribute, for example, can leave Git to consult core.autocrlf.
Do not read the final word as a generic success flag. The output is a record in the form path: attribute: info, and the useful answer is the attribute state in the third field.
3. Inspect the attributes that actually apply
Use --all when you want every attribute associated with the path:
$ git check-attr --all -- PATH/TO/FILE
PATH/TO/FILE: text: auto
PATH/TO/FILE: diff: java
This output omits unspecified attributes. That is useful for finding active rules, but it cannot prove that an omitted attribute is unset. Query a named attribute separately when the distinction matters.
To compare several known attributes, list their names before the separator:
$ git check-attr text eol diff merge -- PATH/TO/FILE
PATH/TO/FILE: text: auto
PATH/TO/FILE: eol: unspecified
PATH/TO/FILE: diff: java
PATH/TO/FILE: merge: unspecified
The -- marker makes the boundary explicit. It stops a pathname beginning with a dash being mistaken for an option, and it makes the command easier to review before you run it.
4. Connect the result to .gitattributes
Attributes come from pattern rules. A line such as this in .gitattributes marks common text files and assigns a diff driver to Java files:
* text=auto
*.java diff=java
*.jpg -text -diff
Attribute syntax has four useful forms:
namesets an attribute.-nameunsets it.name=valueassigns a string.!namereturns it to unspecified.
Later matching rules override earlier ones per attribute.
Git checks $GIT_DIR/info/attributes first, then .gitattributes files from the path's directory towards the work tree root, followed by global and system-wide attribute files. The repository-local file suits a personal rule that must not be committed. Put shared project policy in a version-controlled .gitattributes file instead.
Patterns resemble .gitignore patterns, with two traps: negative patterns are not allowed, and a pattern ending in a slash does not recursively match a directory. Use directory/** when the rule should cover its contents.
5. Check staged rules instead of working-tree rules
If you have edited .gitattributes but not staged it, the default check can reflect the working-tree file. Use --cached to ask what the index contains:
$ git check-attr --cached text diff -- PATH/TO/FILE
PATH/TO/FILE: text: auto
PATH/TO/FILE: diff: unspecified
This is a read-only comparison. If the cached result differs from the normal result, inspect the attribute file and the index before committing.
Warning
Do not 'fix' a surprising answer by staging or committing files until you understand which rule you want distributed.
6. Inspect a committed tree
Use --source with a commit, branch or tag when the question is about a tree rather than the current checkout:
$ git check-attr --source HEAD~1 text diff -- PATH/TO/FILE
PATH/TO/FILE: text: unspecified
PATH/TO/FILE: diff: unspecified
The pathname must exist in the selected tree for a meaningful comparison. This answers 'what did the previous commit say?' without checking out another revision. It does not alter HEAD, the index or the working tree.
7. Use stdin for batches and scripts
For many paths, pass them on standard input. Without -z, one pathname is read per line:
$ printf '%s\n' 'src/main.java' 'README.md' | git check-attr --stdin text
src/main.java: text: auto
README.md: text: unspecified
Use -z when pathnames may contain newlines, or when another program expects NUL-delimited records. It changes both stdin separators, when --stdin is used, and output separators:
$ printf '%s\0' 'src/main.java' 'README.md' | git check-attr --stdin -z text | od -An -t x1
73 72 63 2f 6d 61 69 6e 2e 6a 61 76 61 00 74 65
78 74 00 61 75 74 6f 00
Parse the records as path, attribute, value, each terminated by NUL. Do not split this mode on colons: a pathname can contain characters that make human-formatted output ambiguous.
8. Recover from the usual wrong answer
If the result says unspecified, work down this list:
- Check the path. It must be relative to the repository root.
- Check the pattern. It has to match the path.
- Check the repository. Make sure you are in the intended one.
- Compare normal and cached checks. Then look at nesting: a nearer matching file overrides a parent rule, while
$GIT_DIR/info/attributeshas the highest precedence.
Recovery
To undo an accidental attribute-file edit, restore it with your normal version-control workflow, and only after checking the diff. For a tracked file whose working-tree edit is unwanted, git restore -- .gitattributes discards that edit. Review git diff -- .gitattributes first.
Warning
The restore is irreversible for uncommitted content, so do not run it if you need the changes.
Done means
- Right target. You queried the intended path from the intended repository.
- Four states. You can distinguish set, unset, a string value and unspecified.
- Known source. You know whether the answer came from the working tree, index or a selected tree.
- Rules checked. You checked matching rules and precedence before changing configuration.
- Safe batching. Your batch script uses
--stdin -zwhen pathnames cannot safely be separated by newlines.