mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-26 04:54:25 -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:
@@ -245,4 +245,87 @@ describe('FileDiscoveryService', () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('precedence (.geminiignore over .gitignore)', () => {
|
||||
beforeEach(async () => {
|
||||
await fs.mkdir(path.join(projectRoot, '.git'));
|
||||
});
|
||||
|
||||
it('should un-ignore a file in .geminiignore that is ignored in .gitignore', async () => {
|
||||
await createTestFile('.gitignore', '*.txt');
|
||||
await createTestFile('.geminiignore', '!important.txt');
|
||||
|
||||
const service = new FileDiscoveryService(projectRoot);
|
||||
const files = ['file.txt', 'important.txt'].map((f) =>
|
||||
path.join(projectRoot, f),
|
||||
);
|
||||
|
||||
const filtered = service.filterFiles(files);
|
||||
expect(filtered).toEqual([path.join(projectRoot, 'important.txt')]);
|
||||
});
|
||||
|
||||
it('should un-ignore a directory in .geminiignore that is ignored in .gitignore', async () => {
|
||||
await createTestFile('.gitignore', 'logs/');
|
||||
await createTestFile('.geminiignore', '!logs/');
|
||||
|
||||
const service = new FileDiscoveryService(projectRoot);
|
||||
const files = ['logs/app.log', 'other/app.log'].map((f) =>
|
||||
path.join(projectRoot, f),
|
||||
);
|
||||
|
||||
const filtered = service.filterFiles(files);
|
||||
expect(filtered).toEqual(files);
|
||||
});
|
||||
|
||||
it('should extend ignore rules in .geminiignore', async () => {
|
||||
await createTestFile('.gitignore', '*.log');
|
||||
await createTestFile('.geminiignore', 'temp/');
|
||||
|
||||
const service = new FileDiscoveryService(projectRoot);
|
||||
const files = ['app.log', 'temp/file.txt'].map((f) =>
|
||||
path.join(projectRoot, f),
|
||||
);
|
||||
|
||||
const filtered = service.filterFiles(files);
|
||||
expect(filtered).toEqual([]);
|
||||
});
|
||||
|
||||
it('should use .gitignore rules if respectGeminiIgnore is false', async () => {
|
||||
await createTestFile('.gitignore', '*.txt');
|
||||
await createTestFile('.geminiignore', '!important.txt');
|
||||
|
||||
const service = new FileDiscoveryService(projectRoot);
|
||||
const files = ['file.txt', 'important.txt'].map((f) =>
|
||||
path.join(projectRoot, f),
|
||||
);
|
||||
|
||||
const filtered = service.filterFiles(files, {
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: false,
|
||||
});
|
||||
|
||||
expect(filtered).toEqual([]);
|
||||
});
|
||||
|
||||
it('should use .geminiignore rules if respectGitIgnore is false', async () => {
|
||||
await createTestFile('.gitignore', '*.txt');
|
||||
await createTestFile('.geminiignore', '!important.txt\ntemp/');
|
||||
|
||||
const service = new FileDiscoveryService(projectRoot);
|
||||
const files = ['file.txt', 'important.txt', 'temp/file.js'].map((f) =>
|
||||
path.join(projectRoot, f),
|
||||
);
|
||||
|
||||
const filtered = service.filterFiles(files, {
|
||||
respectGitIgnore: false,
|
||||
respectGeminiIgnore: true,
|
||||
});
|
||||
|
||||
// .gitignore is ignored, so *.txt is not applied.
|
||||
// .geminiignore un-ignores important.txt (which wasn't ignored anyway)
|
||||
// and ignores temp/
|
||||
expect(filtered).toEqual(
|
||||
['file.txt', 'important.txt'].map((f) => path.join(projectRoot, f)),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ export interface FilterReport {
|
||||
export class FileDiscoveryService {
|
||||
private gitIgnoreFilter: GitIgnoreFilter | null = null;
|
||||
private geminiIgnoreFilter: GeminiIgnoreFilter | null = null;
|
||||
private combinedIgnoreFilter: GitIgnoreFilter | null = null;
|
||||
private projectRoot: string;
|
||||
|
||||
constructor(projectRoot: string) {
|
||||
@@ -32,6 +33,15 @@ export class FileDiscoveryService {
|
||||
this.gitIgnoreFilter = new GitIgnoreParser(this.projectRoot);
|
||||
}
|
||||
this.geminiIgnoreFilter = new GeminiIgnoreParser(this.projectRoot);
|
||||
|
||||
if (this.gitIgnoreFilter) {
|
||||
const geminiPatterns = this.geminiIgnoreFilter.getPatterns();
|
||||
// Create combined parser: .gitignore + .geminiignore
|
||||
this.combinedIgnoreFilter = new GitIgnoreParser(
|
||||
this.projectRoot,
|
||||
geminiPatterns,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,6 +50,14 @@ export class FileDiscoveryService {
|
||||
filterFiles(filePaths: string[], options: FilterFilesOptions = {}): string[] {
|
||||
const { respectGitIgnore = true, respectGeminiIgnore = true } = options;
|
||||
return filePaths.filter((filePath) => {
|
||||
if (
|
||||
respectGitIgnore &&
|
||||
respectGeminiIgnore &&
|
||||
this.combinedIgnoreFilter
|
||||
) {
|
||||
return !this.combinedIgnoreFilter.isIgnored(filePath);
|
||||
}
|
||||
|
||||
if (respectGitIgnore && this.gitIgnoreFilter?.isIgnored(filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user