A Guide to Using git check-ignore and git check-attr: Debug File Exclusion and Attribute Rules in Git

Git tutorial - IT technology blog
Git tutorial - IT technology blog

1. Real-World Challenges in Complex Repositories

Our company’s monorepo once ballooned to over 15 microservices with dozens of nested directories. Managing files quickly turned into a nightmare. One day, a junior developer panicked because a .env.staging file was accidentally committed and pushed straight to the remote repository. Everyone had assumed the file was already ignored.

Another time, a new icon file at packages/ui/assets/icons/logo.svg failed to show up when running git status. The entire team struggled to pinpoint why. To stay on schedule, engineers often took the quickest shortcut: running git add -f to force Git to track the file.

The aftermath was messy. Repository sizes inflated, and line-ending conflicts (CRLF vs. LF) between Windows and macOS erupted across multiple pull requests. Misconfigured ignore and attribute rules remain insidious traps that are nearly impossible to catch by visual inspection alone.

2. Why Isn’t Git Ignoring Files as Expected?

Git doesn’t just read a single .gitignore file located at the repository root. When determining whether a file should be ignored or assigned specific attributes, Git evaluates multiple levels of precedence:

  • Local .gitignore files: Files scattered across subdirectories override rules defined in parent directories.
  • Repository-specific exclude file: The .git/info/exclude file applies only to your local machine and is never committed to the remote.
  • Global configuration: Global ignore rules are configured via the core.excludesFile setting in ~/.gitconfig.
  • Negation rules (!): If a parent directory is completely ignored (such as dist/), Git skips the entire directory tree. Any negation pattern inside it, like !dist/bundle.js, has no effect.

The mechanism behind .gitattributes works similarly. End-of-line conversion settings (eol=lf), Git LFS filters, and custom diff drivers are all overridden hierarchically. When a repo contains dozens of nested config files, manual lookups become virtually impossible.

3. Common Missteps to Avoid

Approach 1: Hunting Manually with Ctrl + F or Grep

Whenever a file goes missing from Git status, developers often open the root .gitignore and hit Ctrl + F. If they don’t find it there, they start digging through each subdirectory’s configuration.

Downside: This approach is tedious and error-prone, especially with complex glob patterns like **/*.log or build/*/temp. It also leaves you completely blind to global rules configured on your local machine.

Approach 2: Quick-Fixing with git add -f

When in a rush to commit changes and Git ignores the target file, many developers reflexively run:

git add -f src/services/mailer/templates/welcome.html

Downside: Force-adding only treats the symptom, not the root cause. The faulty rule remains in place, so teammates will run into the exact same issue when pulling the code and editing that file. Worse yet, this habit makes it all too easy to accidentally commit sensitive credentials and secrets.

Approach 3: Using git check-ignore and git check-attr

Git comes with two built-in diagnostic tools: git check-ignore and git check-attr. They serve as dedicated debuggers, revealing the exact configuration file and line number governing any given file.

4. Step-by-Step Debugging Workflow in Practice

Ever since we incorporated these two commands into our troubleshooting checklist, all ignore and line-ending issues get resolved in seconds.

Debugging Exclusion Rules with git check-ignore

The most effective syntax is using the -v (verbose) flag. Git outputs: Configuration file : Line number : Matching pattern.

# Check which rule ignores local.json
git check-ignore -v config/environments/local.json

Terminal output:

.gitignore:14:local.*    config/environments/local.json

The output clearly shows that line 14 of the root .gitignore matching the local.* pattern is the culprit.

To inspect multiple files at once, add the --non-matching (or -n) flag to see non-ignored files as well:

# Check a list of files
git check-ignore -v -n src/index.ts .env.local docs/setup.pdf

Sample output:

::      src/index.ts
.gitignore:3:.env*    .env.local
packages/docs/.gitignore:2:*.pdf    docs/setup.pdf

The :: prefix on the first line indicates that src/index.ts is normal and does not match any ignore rule.

Resolving the Common Negation (!) Trap

Consider a frequent scenario: you want to ignore the logs/ directory while preserving logs/important.log.

# Incorrect configuration in .gitignore:
logs/
!logs/important.log

The file important.log still fails to appear in git status. Let’s inspect it with git check-ignore:

git check-ignore -v logs/important.log
# Returns: .gitignore:1:logs/    logs/important.log

The Fix: Instead of ignoring the entire logs/ directory, ignore only its contents so Git continues traversing the folder:

# Correct configuration in .gitignore:
logs/*
!logs/important.log

Inspecting File Attributes with git check-attr

In cross-platform environments or when using Git LFS, the .gitattributes file controls text/binary definitions and CRLF/LF normalization rules.

To list all attributes applied to a file:

git check-attr -a src/scripts/deploy.sh

Output:

src/scripts/deploy.sh: text: set
src/scripts/deploy.sh: eol: lf
src/scripts/deploy.sh: diff: unspecified

To verify whether a 50MB Photoshop design file is properly tracked by Git LFS:

git check-attr filter diff merge assets/banner.psd

If the terminal prints filter: lfs, the configuration is working. If it shows unspecified, your .gitattributes is missing a pattern for *.psd files.

Quick Command Cheat Sheet

  • git check-ignore -v <path>: Pinpoint the exact file and line number ignoring a target.
  • git check-ignore -v -n <paths...>: Check the ignore status across multiple files simultaneously.
  • git check-attr -a <path>: View all EOL, diff, and LFS attributes assigned to a file.

Next time a file goes missing or runs into formatting quirks, resist the temptation to run git add -f. Run these two commands instead to diagnose the real cause and fix it for good.

Share: