Skip to content

Fix gitignore negation in child files not overriding parent/global rules#300613

Merged
dmitrivMS merged 3 commits into
microsoft:mainfrom
Dmitriusan:fix/gitignore-negation-parent-override
May 9, 2026
Merged

Fix gitignore negation in child files not overriding parent/global rules#300613
dmitrivMS merged 3 commits into
microsoft:mainfrom
Dmitriusan:fix/gitignore-negation-parent-override

Conversation

@Dmitriusan
Copy link
Copy Markdown
Contributor

@Dmitriusan Dmitriusan commented Mar 11, 2026

Problem

When explorer.excludeGitIgnore is enabled, negation patterns (e.g., !.myconfig/) in a project's .gitignore do not override matching positive patterns from parent .gitignore files (e.g., a root .gitignore for a nested workspace folder, or a chained ignore tree).

Reproduction (from #291157):

  1. Root .gitignore contains test.txt
  2. Subfolder .gitignore contains !test.txt
  3. With "explorer.excludeGitIgnore": true, the subfolder's test.txt is hidden, even though git considers it not ignored

Root Cause

In IgnoreFile.parseIgnoreFile(), the isPathIgnored function only uses negation patterns to counteract positive patterns within the same file. When the current file has no positive pattern for a path, it falls through to the parent's isPathIgnored() without checking whether the current file has a negation that should override the parent.

In git's actual behavior, the last matching rule wins, and rules in more-specific .gitignore files (child directories) take precedence over less-specific ones (parent directories).

Fix

Before delegating to the parent, check if the current file has a negation pattern that matches the path. If so, return false (not ignored) without consulting the parent. This correctly implements git's precedence semantics.

Also caches isDirIncluded / isFileIncluded matcher results within a single isPathIgnored invocation so they are not evaluated twice.

Testing

Added two test cases:

  • Child negation overrides parent ignore for directories — simulates a root ignore pattern being overridden by a project-level negation
  • Child negation overrides parent ignore for files — simulates *.log / !important.log across parent/child

Fixes #291157
Relates to #156077

@Dmitriusan
Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

When a child .gitignore contains a negation pattern (e.g., `!.claude/`),
it should override a matching positive pattern from a parent or global
.gitignore (e.g., `.claude`). Previously, negation patterns in the
current file were only checked against the current file's own positive
patterns. If the current file had no matching positive pattern, the
negation was ignored and the parent's positive pattern took effect.

This fix adds explicit checks: if a negation pattern in the current
file matches the path, return false (not ignored) without delegating
to the parent. This matches git's actual behavior where the last
matching rule wins and child gitignore files take precedence over
parent/global ones.

Fixes microsoft#156077
@Dmitriusan Dmitriusan force-pushed the fix/gitignore-negation-parent-override branch from 5b56791 to d131c2c Compare March 11, 2026 01:13
Copilot AI review requested due to automatic review settings May 9, 2026 09:38
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes .gitignore precedence handling in VS Code’s ignore parser so that negation rules in a more specific/child ignore file can override matching positive rules coming from parent/global ignore files, aligning behavior more closely with git check-ignore.

Changes:

  • Update IgnoreFile.parseIgnoreFile() to short-circuit to “not ignored” when the current ignore file contains a matching negation rule, instead of delegating to the parent.
  • Add regression tests covering child negation overriding parent ignore for both directories and files.
Show a summary per file
File Description
src/vs/workbench/services/search/common/ignoreFile.ts Ensures matching negation rules in the current ignore file override parent/global ignore decisions.
src/vs/workbench/services/search/test/common/ignoreFile.test.ts Adds tests validating that child negation patterns override parent ignore patterns for directories and files.

Copilot's findings

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

Comment thread src/vs/workbench/services/search/test/common/ignoreFile.test.ts Outdated
Review feedback:
- Cache isDirIncluded/isFileIncluded results to avoid duplicate matcher evaluations
- Defer fileIncluded computation past the dir-ignore short-circuit
- Fix test that misused a directory path with isDir=false; use a real file path under the ignored directory instead
@dmitrivMS
Copy link
Copy Markdown
Contributor

Made some cleanup for perf + test fixes.

@dmitrivMS dmitrivMS enabled auto-merge (squash) May 9, 2026 10:20
@dmitrivMS
Copy link
Copy Markdown
Contributor

Linked issue that will be fixed by this, verified fix manually.
\

@dmitrivMS dmitrivMS merged commit fc0b83a into microsoft:main May 9, 2026
25 checks passed
@vs-code-engineering vs-code-engineering Bot added this to the 1.120.0 milestone May 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

file-explorer Explorer widget issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Negation doesn't work in nested scenarios

5 participants