mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-21 02:24:09 -07:00
fix(core): combine .gitignore and .geminiignore logic for correct precedence (#11587)
Co-authored-by: Jacob Richman <jacob314@gmail.com>
This commit is contained in:
@@ -270,4 +270,42 @@ src/*.tmp
|
||||
expect(parser.isIgnored('bar ')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Extra Patterns', () => {
|
||||
beforeEach(async () => {
|
||||
await setupGitRepo();
|
||||
});
|
||||
|
||||
it('should apply extraPatterns with higher precedence than .gitignore', async () => {
|
||||
await createTestFile('.gitignore', '*.txt');
|
||||
|
||||
const extraPatterns = ['!important.txt', 'temp/'];
|
||||
parser = new GitIgnoreParser(projectRoot, extraPatterns);
|
||||
|
||||
expect(parser.isIgnored('file.txt')).toBe(true);
|
||||
expect(parser.isIgnored('important.txt')).toBe(false); // Un-ignored by extraPatterns
|
||||
expect(parser.isIgnored('temp/file.js')).toBe(true); // Ignored by extraPatterns
|
||||
});
|
||||
|
||||
it('should handle extraPatterns that unignore directories', async () => {
|
||||
await createTestFile('.gitignore', '/foo/\n/a/*/c/');
|
||||
|
||||
const extraPatterns = ['!foo/', '!a/*/c/'];
|
||||
parser = new GitIgnoreParser(projectRoot, extraPatterns);
|
||||
|
||||
expect(parser.isIgnored('foo/bar/file.txt')).toBe(false);
|
||||
expect(parser.isIgnored('a/b/c/file.txt')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle extraPatterns that unignore directories with nested gitignore', async () => {
|
||||
await createTestFile('.gitignore', '/foo/');
|
||||
await createTestFile('foo/bar/.gitignore', 'file.txt');
|
||||
|
||||
const extraPatterns = ['!foo/'];
|
||||
parser = new GitIgnoreParser(projectRoot, extraPatterns);
|
||||
|
||||
expect(parser.isIgnored('foo/bar/file.txt')).toBe(true);
|
||||
expect(parser.isIgnored('foo/bar/file2.txt')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,9 +16,20 @@ export class GitIgnoreParser implements GitIgnoreFilter {
|
||||
private projectRoot: string;
|
||||
private cache: Map<string, string[]> = new Map();
|
||||
private globalPatterns: string[] | undefined;
|
||||
private processedExtraPatterns: string[] = [];
|
||||
|
||||
constructor(projectRoot: string) {
|
||||
constructor(
|
||||
projectRoot: string,
|
||||
private readonly extraPatterns?: string[],
|
||||
) {
|
||||
this.projectRoot = path.resolve(projectRoot);
|
||||
if (this.extraPatterns) {
|
||||
// extraPatterns are assumed to be from project root (like .geminiignore)
|
||||
this.processedExtraPatterns = this.processPatterns(
|
||||
this.extraPatterns,
|
||||
'.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private loadPatternsForFile(patternsFilePath: string): string[] {
|
||||
@@ -40,8 +51,15 @@ export class GitIgnoreParser implements GitIgnoreFilter {
|
||||
.split(path.sep)
|
||||
.join(path.posix.sep);
|
||||
|
||||
return content
|
||||
.split('\n')
|
||||
const rawPatterns = content.split('\n');
|
||||
return this.processPatterns(rawPatterns, relativeBaseDir);
|
||||
}
|
||||
|
||||
private processPatterns(
|
||||
rawPatterns: string[],
|
||||
relativeBaseDir: string,
|
||||
): string[] {
|
||||
return rawPatterns
|
||||
.map((p) => p.trimStart())
|
||||
.filter((p) => p !== '' && !p.startsWith('#'))
|
||||
.map((p) => {
|
||||
@@ -155,7 +173,10 @@ export class GitIgnoreParser implements GitIgnoreFilter {
|
||||
const relativeDir = path.relative(this.projectRoot, dir);
|
||||
if (relativeDir) {
|
||||
const normalizedRelativeDir = relativeDir.replace(/\\/g, '/');
|
||||
if (ig.ignores(normalizedRelativeDir)) {
|
||||
const igPlusExtras = ignore()
|
||||
.add(ig)
|
||||
.add(this.processedExtraPatterns);
|
||||
if (igPlusExtras.ignores(normalizedRelativeDir)) {
|
||||
// This directory is ignored by an ancestor's .gitignore.
|
||||
// According to git behavior, we don't need to process this
|
||||
// directory's .gitignore, as nothing inside it can be
|
||||
@@ -182,6 +203,11 @@ export class GitIgnoreParser implements GitIgnoreFilter {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply extra patterns (e.g. from .geminiignore) last for precedence
|
||||
if (this.processedExtraPatterns.length > 0) {
|
||||
ig.add(this.processedExtraPatterns);
|
||||
}
|
||||
|
||||
return ig.ignores(normalizedPath);
|
||||
} catch (_error) {
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user