Gitignore and Override Matching
This code turns ignore files, command-line globs, and file-type selections into path-matching decisions for directory traversal. The filtering configuration includes overrides, types, parent ignore files, .ignore, .gitignore, global Git ignore files, explicit ignore files, and custom ignore filenames.
It exists because several rules can match one path, while the caller still needs one result: no match, ignore, or whitelist. The highest-precedence matching glob determines that result.
Sources: crates/ignore/src/walk.rs:646-657, crates/ignore/src/lib.rs:416-424
Core concepts
Gitignore patterns
A gitignore pattern stores its source file, original text, normalized matching text, whitelist status, and directory-only status in Glob.
Gitignore stores compiled patterns in a GlobSet, retains the ordered Vec<Glob>, and counts ignore and whitelist patterns.
Sources: crates/ignore/src/gitignore.rs:32-43, crates/ignore/src/gitignore.rs:81-88
Manual overrides
A manual override is a gitignore-format rule wrapped by Override; its builder delegates pattern parsing to GitignoreBuilder.
Sources: crates/ignore/src/overrides.rs:47
File types
A file type is a named collection of filename globs stored in FileTypeDef; Types keeps definitions, user selections, and the compiled glob set needed for matching.
Sources: crates/ignore/src/types.rs:146-149, crates/ignore/src/types.rs:165-181
Match results
A Match reports whether the highest-precedence matching rule ignored the path, whitelisted it, or matched nothing.
| Result | Meaning |
|---|---|
Match::None | No applicable glob matched. |
Match::Ignore | The highest-precedence glob says to ignore the path. |
Match::Whitelist | The highest-precedence glob says to include the path. |
Sources: crates/ignore/src/lib.rs:416-424
How ignore rules become one decision
The directory matcher keeps overrides, types, explicit ignore matchers, and custom ignore filenames as separate inputs. The supported path-based configuration includes parent ignore files, .ignore, .gitignore, global Git ignore files, explicitly added ignore files, and custom names such as .rgignore.
As traversal enters a directory, the matcher used for its children includes the ignore rules loaded through that directory. If a directory is ignored, traversal does not descend into it, so ignore files inside it are not loaded.
GitignoreBuilder::add opens an ignore file, reads it line by line, and sends each line to add_line. add_line discards comments and blank lines, recognizes ! as a whitelist marker, recognizes a leading / as anchored, and recognizes a trailing / as directory-only. It normalizes patterns without a slash by adding a **/ prefix, adjusts patterns ending in /**, compiles the result, and adds it to the builder’s glob set.
if line.starts_with("!") {
glob.is_whitelist = true;
line = &line[1..];
}
if line.starts_with("/") {
line = &line[1..];
is_absolute = true;
}The original rule remains available through original, while actual stores the normalized pattern used for compilation; is_whitelist stores the rule’s polarity.
A Gitignore is built by compiling the accumulated glob set and preserving the ordered globs; its ignore and whitelist counts are computed from those globs. For a path, matched strips the path into the matcher’s relative form and delegates to matched_stripped. matched_stripped collects matching indices, examines them in reverse order, skips directory-only globs for non-directories, and returns the first applicable whitelist or ignore result.
The following data flow shows how parsed rules become one path result.
Evidence
- ignore-filecrates/ignore/src/gitignore.rs:405
- gitignore-buildercrates/ignore/src/gitignore.rs:460
- glob-setcrates/ignore/src/gitignore.rs:259
- match-resultcrates/ignore/src/gitignore.rs:259
Sources: crates/ignore/src/dir.rs:753-764, crates/ignore/src/walk.rs:646-657, crates/ignore/src/incremental.rs:94-103, crates/ignore/src/gitignore.rs:405-434, crates/ignore/src/gitignore.rs:460-541, crates/ignore/src/gitignore.rs:32-43, crates/ignore/src/gitignore.rs:52-54, crates/ignore/src/gitignore.rs:349-366, crates/ignore/src/gitignore.rs:202-211, crates/ignore/src/gitignore.rs:259-282
How precedence and negation work
Matching indices are collected first and then examined in reverse order; the first applicable entry is therefore the highest-precedence glob.
A leading ! sets is_whitelist while parsing. The final result becomes Match::Whitelist for a matching whitelist glob and Match::Ignore for a matching non-whitelist glob.
Manual overrides use the same pattern machinery but invert the wrapped gitignore result. Override::matched calls invert and converts the result into an override Glob. If an override set contains whitelist patterns but a non-directory file matches no pattern, it returns an unmatched ignore result, making the whitelist set behave as an allow-list.
The tests show that *.foo whitelists matching paths, !*.bar ignores matching paths, and a later !*.bar.foo overrides an earlier *.foo match.
Evidence
- override-buildercrates/ignore/src/overrides.rs:142
- overridecrates/ignore/src/overrides.rs:97
- inverted-matchcrates/ignore/src/overrides.rs:97
- final-decisioncrates/ignore/src/overrides.rs:97
Traversal checks glob overrides first and stops further matching when an override matches. An override is a whitelist unless it starts with !, in which case it is an ignore glob. Command-line glob documentation likewise states that these globs override other ignore logic and that later matching globs take precedence.
The following sequence makes the override call order explicit.
Evidence
Sources: crates/ignore/src/gitignore.rs:483-491, crates/ignore/src/gitignore.rs:271-278, crates/ignore/src/overrides.rs:97-110, crates/ignore/src/overrides.rs:209-217, crates/ignore/src/overrides.rs:229-234, crates/ignore/src/walk.rs:460-466, crates/core/flags/defs.rs:2604-2608
How --type becomes glob matching
The --type and -t options select file types; --type-add adds a glob definition and --type-clear removes existing globs for a type. The default table contains named definitions and aliases mapped to filename globs, including html and bat/batch.
TypesBuilder::select records a positive Selection::Select, while TypesBuilder::negate records Selection::Negate; the special name all expands across every known type. A definition can be added as a name plus glob, or include globs from other named types.
When TypesBuilder::build runs, each selected definition’s globs is added to one GlobSet, while glob_to_selection records which selection supplied each compiled glob. During matching, directories immediately return Match::None; otherwise only the candidate filename is extracted. The matcher takes the last matching index as highest precedence, then converts a negated selection into Match::Ignore and a positive selection into Match::Whitelist.
Sources: crates/core/flags/complete/rg.zsh:290-295, crates/ignore/src/default_types.rs:12-346, crates/ignore/src/types.rs:385-394, crates/ignore/src/types.rs:399-408, crates/ignore/src/types.rs:421-435, crates/ignore/src/types.rs:446-483, crates/ignore/src/types.rs:324-366, crates/ignore/src/types.rs:263-302
How it connects
Directory traversal carries these matchers into recursive walking and applies parent rules to children. Read Parallel Directory Traversal for how filtering controls descent and file dispatch.
The compiled pattern engine is supplied by globset, whose glob sets match multiple patterns against one candidate path and report the matching globs. Read The Globset Matching Engine for compilation and set-matching details.
The resolved file-type matcher is exposed through types. Read CLI Entry and Flag Parsing for the surrounding argument model.
Sources: crates/ignore/src/incremental.rs:94-103, crates/globset/src/lib.rs:1-13, crates/core/flags/hiargs.rs:868-874
Key takeaways
- Ignore files become ordered compiled globs matched against normalized paths.
- Matching indices are examined in reverse order, so the last applicable glob has highest precedence.
!marks a gitignore whitelist and becomes an ignore under override inversion.- File types compile named definitions into filename globs and do not match directories.
- Overrides are checked before ordinary ignore rules and can stop further matching.