google-gemini / google-gemini/gemini-cli
Nested .gitignore: patterns with only a trailing slash (e.g. build/) are anchored instead of matching at any depth
- Dominant language
- TypeScript
- Stars
- 107k
- Forks
- 14.6k
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 45
Description
### What happened?
A pattern with only a trailing slash in a nested `.gitignore`, such as `build/`, only ignores the `build` directory sitting next to that `.gitignore`. Directories with the same name further down are still returned by file discovery.
With `pkg/.gitignore` containing `build/`:
- `pkg/build/out.js` is ignored, as expected.
- `pkg/tools/build/out.js` is **not** ignored, so it still shows up through `glob`, `ls`, `read_many_files`, ripGrep and `@` completion.
It comes from `GitIgnoreParser.processPatterns` in `packages/core/src/utils/gitIgnoreParser.ts`. For a nested `.gitignore`, the pattern is only prefixed with `**/` when it contains no `/` at all:
```ts
if (!isAnchoredInFile && !p.includes('/')) {
newPattern = path.posix.join('**', p);
}
```
A trailing slash counts as a slash here, so `build/` is rewritten to `/pkg/build/` instead of `/pkg/**/build/`. The root `.gitignore` and `.geminiignore` go through another branch and are not affected.
Minimal reproduction:
```
mkdir -p repro/pkg/build repro/pkg/tools/build
printf 'build/\n' > repro/pkg/.gitignore
touch repro/pkg/build/out.js repro/pkg/tools/build/out.js
```
`new GitIgnoreParser('repro').isIgnored('pkg/tools/build/out.js', false)` returns `false`.
This affects the common case: any subdirectory `.gitignore` listing `node_modules/`, `__pycache__/`, `build/`, `dist/`, `.venv/` and so on.
### What did you expect to happen?
The same result as git. gitignore(5) says a trailing slash only restricts the pattern to directories, and that "frotz/ matches frotz and a/frotz that is a directory". In a real repository with the same layout:
```
$ git check-ignore -v pkg/build/out.js pkg/tools/build/out.js
pkg/.gitignore:1:build/ pkg/build/out.js
pkg/.gitignore:1:build/ pkg/tools/build/out.js
```
So `pkg/tools/build/out.js` should be ignored too.
### Client information
Found by reading the code on `main` at `bfb71fd2b` and reproduced with the unit tests of `packages/core` on Linux, rather than through a released client, so I have no `/about` output to paste. The affected code is unchanged on `main` at the time of writing.
### Login information
Not relevant: this is local file filtering, no auth involved.
### Anything else we need to know?
I have a small fix with a regression test. When deciding whether to anchor the pattern, count only the slashes before the last character:
```ts
if (!isAnchoredInFile && !p.slice(0, -1).includes('/')) {
```
`build/` then becomes `/pkg/**/build/`. Patterns with a leading or middle slash (`/c`, `c/d`, `c/d/`) are anchored exactly as before.
Checks I ran: the new test fails on `main` at `pkg/deep/build/out.js` and passes with the change; `FileDiscoveryService.filterFiles` stops returning `pkg/tools/build/out.js`; the 18 core test files that use `GitIgnoreParser` or `FileDiscoveryService` pass (716 tests); eslint, prettier and `tsc --noEmit` on `packages/core` are clean. I did not run the full preflight.
The branch is here, in case it is useful: https://github.com/google-gemini/gemini-cli/compare/main...Dev-next-gen:gemini-cli:fix/nested-gitignore-trailing-slash
I read in CONTRIBUTING that pull requests need an issue with the `help wanted` label first, so I am filing this rather than opening the PR. Happy to open it if you would like the fix.
AI tools used
Contributor guide
Research direction
Start in packages/core/src/utils/gitIgnoreParser.ts, especially GitIgnoreParser.processPatterns, and run the relevant GitIgnoreParser and FileDiscoveryService unit tests in packages/core. Reproduce the nested .gitignore case described in the issue, then verify that matching directories at any depth are excluded while anchored and middle-slash patterns retain their existing behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, typescript
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100