Explain which gitignore rule is ignoring my file
Asked Answered
S

6

411

Is there any way to see why some file is getting ignored by git (i.e. which rule in a .gitignore file is causing the file to be ignored)?

Imagine I have this (or a much more complex scenario, with hundreds of folders and tens of .gitignore files:

/
-.gitignore
-folder/
    -.gitignore
    -subfolder/
              -.gitignore
              -file.txt

If I run git add folder/subfolder/file.txt git may complain of it being ignored:

The following paths are ignored by one of your .gitignore files:
folder/subfolder/file.txt
Use -f if you really want to add them.

Is there any way to know which of all the possible .gitignore have a rule to ignore this file, and also show the rule? Like:

The following paths are ignored by your folder/.gitignore file (line 12: *.txt)
folder/subfolder/file.txt
Use -f if you really want to add them.

Or just:

$ git why-is-ignored folder/subfolder/file.txt
folder/.gitignore:12:*.txt
Sard answered 27/8, 2012 at 15:7 Comment(3)
Note: git check-ignore will soon (git1.8.5/1.9) have a --no-index option. See my answer belowImpertinent
Note: GIT_TRACE_EXCLUDE=1 git status will soon be an additional way to debug .gitignore rules. See my edited answer belowImpertinent
Relevant blog post: danielcompton.net/2016/04/21/….Preconcerted
O
820
git check-ignore -v filename

See the man page for more details.

Original answer follows:

git does not currently provide anything like this. But after seeing your question I did some googling and found that back in 2009 this feature was requested and partially implemented. After reading the thread, I realised it would not be too much work to do it properly, so I have started work on a patch and hope to have it finished in the next day or two. I will update this answer when it is ready.

UPDATE: Wow, that was a lot harder than I expected. The innards of git's exclude handling are quite cryptic. Anyway, here's an almost finished series of commits which apply to today's upstream master branch. The test suite is 99% complete, but I haven't finished handling of the --stdin option yet. Hopefully I'll manage that this weekend, and then submit my patches to the git mailing list.

In the meantime, I'd definitely welcome testing from anyone who's able to do so - just clone from my git fork, check out the check-ignore branch, and compile it as normal.

UPDATE 2: It's done! Latest version is on github as per above, and I have submitted the patch series to the git mailing list for peer review. Let's see what they think ...

UPDATE 3: After several more months of hacking / patch reviews / discussions / waiting, I'm delighted to be able to say that this feature has now reached git's master branch, and will be available in the next release (1.8.2, expected 8th March 2013). Here's the check-ignore manual page. Phew, that was way more work than I expected!

UPDATE 4: If you're interested in the full story about how this answer evolved and the feature came to be implemented, check out episode #32 of the GitMinutes podcast.

Ot answered 28/8, 2012 at 21:58 Comment(15)
Just seen the man page. Can the type include "coreexcludes" to cover the config core.excludesfile ?Sumer
@PhilipOakley no the <type> is just to indicate whether the pattern is an include or an exclude (and will most likely get dropped in the next version anyway), but the <source> filename will reveal whether it comes from the core.excludesfile file or a per-directory .gitignore. If you want a programmatic test for that, you can test for a leading / since the former is an absolute path and the latter are relative.Ot
@AdamSpiers, "you can test for a leading / since the former is an absolute path and the latter are relative." - Good point, worth having in the documentation if it's all successful.Sumer
@PhilipOakley your wish will be granted :) (assuming the patch series is accepted...)Ot
Answer updated to say that this feature is now in master :-)Ot
I'm using 1.8.2 and git check-ignore doesn't do anything.Armalla
@yourfriendzak Without a shadow of a doubt, git check-ignore is present and working in 1.8.2. If the behaviour is not what you expect, I suggest you (re-)read the manual page, and if it still isn't, please submit a proper bug report on the git mailing list. Just saying it doesn't do anything isn't very helpful. I expect you have probably run it on a non-ignored file and are incorrectly expecting some output (although I will probably add support for --show-unmatched to the --verbose output mode in the future).Ot
@AdamSpiers you're right. I should have specified that when I execute the command, nothing is printed. No error message, no success message, no info. Just the next, blank prompt showing up. So am I correct to assume that "no output" is expected behavior in certain circumstances?Armalla
@AdamSpiers thank you so much for this! After 3 days of investigation you have just helped me track down the cause of a breaking build due to a globally ignored file in .gitignore_global! I didn't even know that was a thing!Soapwort
It is not everyday that one sees a Stack Overflow developer make so many changes to implement a developer feature. Wow and respect.Filibeg
if git check-ignore doesn't do anything it maybe because the file path is wrong (git check-ignore -v path/to/file).Prediction
One thing that the git check-ignore command does not check for is if a folder is ignored because it only contains ignored files. Running it on on the folder will return an empty result but running it on the files in the folder will show the rule that ignores it. Because git by default ignores empty folders, a folder that contains only ignored files for git is empty. I was confused as to why some new folders in my IDE were colored that they were ignored by git until I realized this.Washboard
Is there a way to find out why a file is ignored? This seems to just echo the filename if that filename is ignored, or nothing if not. ok, but why? if its not mentioned in any gitignore files or a .gitignore_globalIonogen
@Ionogen You're almost certainly missing the -v option included in the above answer.Ot
This has saved me. I saw that I have a global git ignore that's ignoring "index.js" files.Haggerty
I
20

Update git 2.8 (March 2016):

GIT_TRACE_EXCLUDE=1 git status

See "A way to validate .gitignore file"

That is complementary to the git check-ignore -v described below.


Original answer: Sept 2013 (git 1.8.2, then 1.8.5+):

git check-ignore improves again in git 1.8.5/1.9 (Q4 2013):

"git check-ignore" follows the same rule as "git add" and "git status" in that the ignore/exclude mechanism does not take effect on paths that are already tracked.
With "--no-index" option, it can be used to diagnose which paths that should have been ignored have been mistakenly added to the index.

See commit 8231fa6 from https://github.com/flashydave:

check-ignore currently shows how .gitignore rules would treat untracked paths. Tracked paths do not generate useful output.
This prevents debugging of why a path became tracked unexpectedly unless that path is first removed from the index with git rm --cached <path>.

The option --no-index tells the command to bypass the check for the path being in the index and hence allows tracked paths to be checked too.

Whilst this behaviour deviates from the characteristics of git add and git status its use case is unlikely to cause any user confusion.

Test scripts are augmented to check this option against the standard ignores to ensure correct behaviour.


--no-index::

Don't look in the index when undertaking the checks.
This can be used:

  • to debug why a path became tracked by e.g. git add . and was not ignored by the rules as expected by the user or
  • when developing patterns including negation to match a path previously added with git add -f.
Impertinent answered 23/9, 2013 at 7:33 Comment(0)
R
10

It may not be .gitignore -- 3 possible ignore reasons

A file may be ignored for the following reasons:

  1. .gitignore
  2. git update-index --skip-worktree
  3. git update-index --assume-unchanged

Additionally, a file may be UNignored by if it is in .gitignore AND already staged in the index/cache.

To check for the enumerated cases above:

  1. For the two cases of .gitignore excludes, compare the output of:

    • git check-ignore --verbose --non-matching --no-index file1 file2 file3
    • git check-ignore --verbose --non-matching file1 file2 file3
  2. git ls-files file1 file2 file3 | grep -E '^S'

  3. git ls-files file1 file2 file3 | grep -E '^[[:lower:]]'

That's too hard, just give me an alias!

The following aliases will cover all the cases listed above:

ignore = !"bash -c 'diff --unified=999999999 --color=always <(echo a; git check-ignore --verbose --non-matching --no-index . \"$@\") <(echo b; git check-ignore --verbose --non-matching . \"$@\")' - \"$@\" | tail -n+7; git hidden \"$@\" # Show ignore status of arguments. Files included by index are tagged with prepended '+'."
hidden = !"git ls-files -v -- \"$@\"| grep -E '^(S|[[:lower:]])' # S means update-index --skip-worktree, and lower first letter means --assume-unchanged."

The comment and final " are part of the line to be copied to your .gitconfig.

Usage:

git ignore file1 file2 file3
Restore answered 13/8, 2020 at 9:27 Comment(0)
F
5

I can't find anything in the man page but here's a quick and dirty script that will check your file in each parent directory to see if it can be git-add'ed. Run it in the directory containing the problem file as:

test-add.sh STOP_DIR FILENAME

where STOP_DIR is the top level directory of the Git project and FILENAME is the problem file name (without a path). It creates an empty file of the same name at each level of the hierarchy (if it doesn't exist) and tries a git add -n to see if it can be added (it cleans up after itself). It outputs something like:

FAILED:    /dir/1/2/3
SUCCEEDED: /dir/1/2

The script:

#!/usr/bin/env bash
TOP=$1
FILE=$2
DIR=`pwd`
while : ; do
  TMPFILE=1
  F=$DIR/$FILE
  if [ ! -f $F ]; then
    touch $F
    TMPFILE=0
  fi
  git add -n $F >/dev/null 2>&1
  if [ $? = 0 ]; then
    echo "SUCCEEDED: $DIR"
  else
    echo "FAILED:    $DIR"
  fi
  if [ $TMPFILE = 0 ]; then
    rm $F
  fi
  DIR=${DIR%/*}
  if [ "$DIR" \< "$TOP" ]; then
    break
  fi
done 
Felipe answered 28/8, 2012 at 19:49 Comment(0)
S
4

Context: I had a very similar problem, I couldn't find what rule was ignoring my file/folder. I tried
git check-ignore -v filename
but it gave me no result, or the result was a line number with a blank-line in my .gitignore file.
So the problem was my file was not ignored by my local .gitignore file, but by my local core.excludesfile (which is not included in my repository) .

Solution: I looked for location for the file with this command:
git config core.excludesfile
then I open it and removed the problematic line and that's it.

Reference: You can also take a look here for additional explanation.

Skylark answered 26/7, 2020 at 14:58 Comment(0)
B
2

To add to the main answer of using git check-ignore -v filename (thanks BTW) I found that my .gitignore file was blocking everything because there was a newline after a wildcard, so I had:

* .sublime-project

as an example. I just removed the newline, and voila! It was fixed.

Benefit answered 16/2, 2018 at 5:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.