guide

The Complete Guide to .gitignore Pattern Syntax

gitignore pattern syntax with example tables: comments, escaping and trailing spaces, negation with !, trailing slashes, anchoring, the *, ? and [] wildcards, and ** rules.

Each line of a .gitignore is one pattern. The syntax looks similar to shell globs, but its meaning changes a lot depending on where the slashes are and where ** appears. The content below is based on the official git documentation gitignore(5), and the examples were verified directly with git 2.50.

Blank lines, comments and escaping

Trailing spaces are invisible and a common source of mistakes. If a rule does not take effect, turn on whitespace display in your editor. Also remember that only space characters are stripped; tabs are not.

Trailing slash: matches directories only

If a pattern ends with /, it matches only directories. build/ ignores a directory named build and everything inside it, but does not ignore a file named build. Conversely, writing build without a slash matches both files and directories with that name.

A slash anchors the position

If the rest of the pattern, excluding a trailing slash, contains a / at the beginning or in the middle, the pattern matches only paths relative to the directory containing the .gitignore file. This is often called anchoring. Without a /, git looks for entries with a matching name at any depth below the .gitignore.

PatternMatchesDoes not match
debug.logdebug.log, logs/debug.log
/debug.logdebug.loglogs/debug.log
logs/debug.loglogs/debug.logapp/logs/debug.log
doc/frotz/doc/frotz/ directorya/doc/frotz/
frotz/frotz/, a/frotz/ directoriesfrotz file

The third row is especially confusing. logs/debug.log has a slash in the middle, so it is anchored to the root and means the same as prefixing it with /. To catch logs/debug.log at any depth, use **/logs/debug.log.

Wildcards: *, ?, [ ]

Two asterisks **

** has a special meaning only when it is adjacent to slashes.

FormMeaningExamples
**/foofoo at any depthfoo, a/foo, a/b/foo
**/logs/debug.loglogs/debug.log at any depthlogs/debug.log, app/logs/debug.log
abc/**everything inside abcabc/x, abc/x/y (but not abc itself)
a/**/bzero or more directories in betweena/b, a/x/b, a/x/y/b

In any other position, such as the ** in foo** or **bar, it behaves like a regular * and cannot cross slashes. docs/**/*.pdf matches both docs/manual.pdf and docs/a/b/manual.pdf, because /**/ also matches zero directories.

Putting it together in one small example

# Build output (only the build directory at the root)
/build/

# Log files at any depth, but keep the important log
*.log
!important.log

# All PDFs under docs
docs/**/*.pdf

# A file whose name starts with #
\#scratch.md
PathResultDeciding rule
build/app.jsIgnored/build/
src/build/app.jsTrackedNone
logs/server.logIgnored*.log
logs/important.logTracked!important.log
docs/api/v1.pdfIgnoreddocs/**/*.pdf
#scratch.mdIgnored\#scratch.md

You can reproduce this table exactly by pasting the rules and paths into the generator's Pattern test tab.

References

← PreviousWhat to Ignore and What to Commit