Ignoring files
Projectile can exclude files and directories from a project in several ways, described below. Which of them apply depends on the indexing method in use - see the indexing method comparison for the full matrix of what each method honors.
Ignoring files using .projectile (a.k.a. dirconfig)
All three indexing methods honor the - ignore and ! unignore entries
below. Under alien the ignore entries are handed to the external indexing
tool as exclusion arguments (see Indexing).
The + keep entries apply everywhere too: under alien they are passed to the
indexing tool as path arguments, or applied to its output for the few tools that
can’t take them.
|
If you’d like to instruct Projectile to ignore certain files in a
project, when indexing it you can do so in the .projectile file by
adding one pattern per line, each preceded with a - sign. The
patterns follow gitignore rules and are matched against paths relative
to the project root - a leading slash anchors a pattern there.
Lines without any prefix at all are still accepted and treated
as ignore patterns for backward compatibility, but the implicit form
is being phased out and Projectile now warns about it once per
project. Prefer the explicit - prefix in new dirconfigs.
|
Here’s an example for a typical Rails application:
-/log -/tmp -/vendor -/public/uploads
This would ignore those folders only at the root of the project. Patterns without a slash match at any depth instead:
-tmp -*.rb -*.yml -models
You can also ignore everything except certain subdirectories. This is useful when selecting the directories to keep is easier than selecting the directories to ignore, although you can do both. To select directories to keep, that means everything else will be ignored.
Example:
+/src/foo +/tests/foo
Keep in mind that you can only include subdirectories, not file patterns.
If both directories to keep and ignore are specified, the directories to keep first apply, restricting what files are considered. The paths and patterns to ignore are then applied to that set.
Finally, you can override ignored files. This is especially useful when some files ignored by your VCS should be considered as part of your project by projectile:
!/src/foo !*.yml
When a path is overridden, its contents are still subject to ignore patterns. To override those files as well, specify their full path with a bang prefix.
The pattern language
Both ignore examples above are the same thing: gitignore patterns, matched against every file’s root-relative path. There’s one pattern language and one matcher, shared by every indexing method and by the global ignore variables described below.
-
A pattern without a slash matches the file name or any directory segment at any depth:
-logignores a file or directory calledloganywhere in the tree, but won’t matchxlogorlog.txt. -
A pattern containing a slash is anchored at the project root, and a leading slash anchors without naming a directory of its own:
-src/genand-/logignore onlysrc/genandlogat the root, notlib/src/genorsrc/log. Prefix the pattern with**/to match at any depth. -
A trailing slash restricts the pattern to directories:
-vendor/ignoresvendordirectories, but not files namedvendor. -
A matched directory covers its entire subtree.
-
matches within a single path segment, while*also crosses/;?matches a single character, and[…]/[!…]character classes are supported. -
Matching is case-sensitive.
The search commands (projectile-grep, projectile-ag,
projectile-ripgrep, projectile-find-references) apply the very same
patterns, translated into whatever exclusion syntax the underlying tool
speaks, so a search sees the same set of files indexing does. The one
place the translation can’t be exact is grep: in a find path
glob crosses /, so a root-anchored pattern like -/.txt also
excludes deeper matches there.
Worked examples
The rules compose, and the composition is where people trip up. All three examples below are real: the file lists are what Projectile actually returns.
A monorepo: keep two packages, drop build output
Given this tree:
packages/core/src/a.ts packages/core/dist/a.js packages/web/src/b.ts packages/web/node_modules/dep/i.js packages/legacy/src/c.ts node_modules/top/x.js
and this .projectile:
+/packages/core +/packages/web -dist/ -node_modules/
you get:
packages/core/src/a.ts packages/web/src/b.ts
The + entries run first and restrict the project to those two directories, so
packages/legacy never enters the picture. The - entries then apply to what’s
left, and because neither has a slash they match at any depth: one -dist/
handles every package’s build output, and one -node_modules/ catches both the
top-level and the nested one.
Under alien the kept directories are handed to the indexing tool as
path arguments (git ls-files — src tests, fd --search-path), so the
restriction costs nothing extra. Tools whose command is a shell pipeline can’t
take path arguments, so there the listing is filtered inside Emacs instead.
|
Rescuing a file the ignore rules would drop
Given app/main.rb, config/app.yml, log/dev.log, tmp/cache/x,
vendor/assets/lib.min.js and vendor/assets/custom.min.js, plus:
-/log/ -/tmp/ -*.min.js !/vendor/assets/custom.min.js
you get:
app/main.rb config/app.yml vendor/assets/custom.min.js
-*.min.js drops every minified file, and the ! entry pulls one specific file
back. This works under all three indexing methods, though a project with !
entries gives up alien’s push-down and is filtered inside Emacs instead - an
exclusion handed to `git or fd can’t be taken back afterwards.
Anchored and floating, side by side
The single most common surprise is forgetting that a slash changes the meaning.
Given log/a.txt, src/log/b.txt, build/d.o, src/build/c.o and keep.txt:
-/log/ -build/
leaves:
keep.txt src/log/b.txt
-/log/ is anchored, so it only removes the log directory at the project
root and src/log survives. -build/ has no leading slash, so it removes
build directories at every depth. Write -log/ if you meant both.
Comments
If you would like to include comment lines in your .projectile file,
you can customize the variable projectile-dirconfig-comment-prefix.
Assigning it a non-nil character value, e.g. #, will cause lines in
the .projectile file whose first non-whitespace character matches
that character to be treated as comments instead of patterns.
The same is true of the +, -, and ! prefixes: leading spaces
and tabs before the prefix are skipped, so accidental indentation
won’t silently turn the entry into a literal ignore pattern.
Ignored files using the project indexing tools
If you’re using the hybrid or alien indexing strategies, the simplest
way to ignore some files is just leverage the configuration of the
tool you’re using to do the project indexing.
E.g. in the case of git you can just tweak .gitignore.
Sometimes, however, you’d like to have some files as part of your project, but you don’t want to see them in Projectile for whatever reasons.
In those cases the project dirconfig file (.projectile) can be a handy
way to further adjust what you want to see in Projectile.
Global ignore and unignore settings
In addition to per-project ignores, Projectile provides several variables for
globally ignoring files and directories. Except for
projectile-global-ignore-file-patterns (see below) their entries are the
gitignore patterns described above, and they take effect
with every indexing method; under alien they are pushed down into the external
indexing tool.
Out of the box projectile-globally-ignored-directories covers the editor and
version control directories plus the dependency and build output directories of
the common ecosystems - node_modules, target, pycache, .venv,
.next, .terraform and so on. Under alien your VCS is usually ignoring
those anyway; the defaults matter for native and hybrid indexing, and for
projects that aren’t under version control at all.
Directories that projects do sometimes commit - vendor, dist, public,
build - are deliberately left out. If one of the defaults gets in your way,
add it to projectile-globally-unignored-directories, or put a ! line in
that project’s .projectile to get it back for that project alone.
;; Ignore files by name, at any depth
(setq projectile-globally-ignored-files '("TAGS" "GPATH"))
;; Ignore files by suffix (e.g. compiled artifacts)
(setq projectile-globally-ignored-file-suffixes '(".o" ".pyc" ".elc"))
;; Ignore directories - the entries are patterns, so `*' is a wildcard
(setq projectile-globally-ignored-directories
'("node_modules" ; any directory named node_modules, at any depth
"/tmp" ; only ./tmp, at the project root
"*.egg-info")) ; any directory whose name ends in .egg-info
projectile-global-ignore-file-patterns is the odd one out: its entries are
Emacs regexps, matched against absolute file names, not patterns. They can’t be
handed to an external tool or expressed as a glob, so they only apply under
native indexing.
;; Ignore files matching regexp patterns (native indexing only)
(setq projectile-global-ignore-file-patterns '("\\.min\\.js$" "\\.map$"))
Before Projectile 3.3 a leading in
projectile-globally-ignored-directories was a marker meaning "at any depth"
rather than a wildcard, and a bare entry was matched at the top level only
(under hybrid) or by directory name at any depth (under native). Now every
entry is a plain gitignore pattern, matched identically by every indexing
method: drop the prefix from entries like *node_modules, and write /tmp
if you really mean only the one at the project root.
|
You can also unignore specific files or directories that would otherwise be excluded. This is useful when your VCS ignores files that you still want Projectile to show:
;; Unignore specific files
(setq projectile-globally-unignored-files '("important.dat"))
;; Unignore specific directories
(setq projectile-globally-unignored-directories '("vendor"))