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
- A blank line matches nothing. Use it as a separator for readability.
- A line starting with
#is a comment. To ignore a file whose name starts with#, add a backslash, as in\#. - Spaces at the end of a line are ignored. If you need a file name that ends with a space, protect the space with a backslash, as in
foo\. Spaces at the start of a line remain part of the pattern. - A line starting with
!is a negation pattern. For a file whose name starts with!, write\!important.txt.
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.
| Pattern | Matches | Does not match |
|---|---|---|
debug.log | debug.log, logs/debug.log | — |
/debug.log | debug.log | logs/debug.log |
logs/debug.log | logs/debug.log | app/logs/debug.log |
doc/frotz/ | doc/frotz/ directory | a/doc/frotz/ |
frotz/ | frotz/, a/frotz/ directories | frotz 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: *, ?, [ ]
*matches any string except/(including the empty string).*.logmatches bothapp.loganda/b/app.log, butlogs/*.logmatches onlylogs/app.log, notlogs/2026/app.log.?matches any single character except/.file?.txtmatchesfile1.txtbut notfile10.txt.[a-z]matches one character in the range, and[!a-z]or[^a-z]matches one character outside the range.*.py[cod]catches.pyc,.pyoand.pydat once. Brackets do not match/either.- POSIX character classes such as
[[:digit:]]can also be used.
Two asterisks **
** has a special meaning only when it is adjacent to slashes.
| Form | Meaning | Examples |
|---|---|---|
**/foo | foo at any depth | foo, a/foo, a/b/foo |
**/logs/debug.log | logs/debug.log at any depth | logs/debug.log, app/logs/debug.log |
abc/** | everything inside abc | abc/x, abc/x/y (but not abc itself) |
a/**/b | zero or more directories in between | a/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
| Path | Result | Deciding rule |
|---|---|---|
build/app.js | Ignored | /build/ |
src/build/app.js | Tracked | None |
logs/server.log | Ignored | *.log |
logs/important.log | Tracked | !important.log |
docs/api/v1.pdf | Ignored | docs/**/*.pdf |
#scratch.md | Ignored | \#scratch.md |
You can reproduce this table exactly by pasting the rules and paths into the generator's Pattern test tab.
References
- gitignore official documentation (git-scm.com)
- GitHub Docs: Ignoring files
- Verified as of: 2026-09-23 (examples checked with git 2.50.1)