fix: gitignore handling (#8177)

This commit is contained in:
Gaurav
2025-09-10 12:48:07 -07:00
committed by GitHub
parent 32abe905f1
commit db99fc70b6
5 changed files with 251 additions and 164 deletions

View File

@@ -33,14 +33,16 @@ describe('GitIgnoreParser', () => {
await fs.rm(projectRoot, { recursive: true, force: true });
});
describe('initialization', () => {
it('should initialize without errors when no .gitignore exists', async () => {
describe('Basic ignore behaviors', () => {
beforeEach(async () => {
await setupGitRepo();
expect(() => parser.loadGitRepoPatterns()).not.toThrow();
});
it('should load .gitignore patterns when file exists', async () => {
await setupGitRepo();
it('should not ignore files when no .gitignore exists', async () => {
expect(parser.isIgnored('file.txt')).toBe(false);
});
it('should ignore files based on a root .gitignore', async () => {
const gitignoreContent = `
# Comment
node_modules/
@@ -50,52 +52,28 @@ node_modules/
`;
await createTestFile('.gitignore', gitignoreContent);
parser.loadGitRepoPatterns();
expect(parser.getPatterns()).toEqual([
'.git',
'node_modules/',
'*.log',
'/dist',
'.env',
]);
expect(parser.isIgnored(path.join('node_modules', 'some-lib'))).toBe(
true,
);
expect(parser.isIgnored(path.join('src', 'app.log'))).toBe(true);
expect(parser.isIgnored(path.join('dist', 'index.js'))).toBe(true);
expect(parser.isIgnored('.env')).toBe(true);
expect(parser.isIgnored('src/index.js')).toBe(false);
});
it('should handle git exclude file', async () => {
await setupGitRepo();
await createTestFile(
path.join('.git', 'info', 'exclude'),
'temp/\n*.tmp',
);
parser.loadGitRepoPatterns();
expect(parser.getPatterns()).toEqual(['.git', 'temp/', '*.tmp']);
expect(parser.isIgnored(path.join('temp', 'file.txt'))).toBe(true);
expect(parser.isIgnored(path.join('src', 'file.tmp'))).toBe(true);
});
it('should handle custom patterns file name', async () => {
// No .git directory for this test
await createTestFile('.geminiignore', 'temp/\n*.tmp');
parser.loadPatterns('.geminiignore');
expect(parser.getPatterns()).toEqual(['temp/', '*.tmp']);
expect(parser.isIgnored(path.join('temp', 'file.txt'))).toBe(true);
expect(parser.isIgnored(path.join('src', 'file.tmp'))).toBe(true);
});
it('should initialize without errors when no .geminiignore exists', () => {
expect(() => parser.loadPatterns('.geminiignore')).not.toThrow();
expect(parser.isIgnored('src/file.js')).toBe(false);
});
});
describe('isIgnored', () => {
describe('isIgnored path handling', () => {
beforeEach(async () => {
await setupGitRepo();
const gitignoreContent = `
@@ -107,7 +85,6 @@ src/*.tmp
!src/important.tmp
`;
await createTestFile('.gitignore', gitignoreContent);
parser.loadGitRepoPatterns();
});
it('should always ignore .git directory', () => {
@@ -205,8 +182,6 @@ src/*.tmp
});
it('should handle nested .gitignore files correctly', async () => {
parser.loadGitRepoPatterns();
// From root .gitignore
expect(parser.isIgnored('root-ignored.txt')).toBe(true);
expect(parser.isIgnored('a/root-ignored.txt')).toBe(true);
@@ -230,34 +205,27 @@ src/*.tmp
expect(parser.isIgnored('a/d/f/g')).toBe(true);
expect(parser.isIgnored('a/f/g')).toBe(false);
});
it('should correctly transform patterns from nested gitignore files', () => {
parser.loadGitRepoPatterns();
const patterns = parser.getPatterns();
// From root .gitignore
expect(patterns).toContain('root-ignored.txt');
// From a/.gitignore
expect(patterns).toContain('/a/b'); // /b becomes /a/b
expect(patterns).toContain('/a/**/c'); // c becomes /a/**/c
// From a/d/.gitignore
expect(patterns).toContain('/a/d/**/e.txt'); // e.txt becomes /a/d/**/e.txt
expect(patterns).toContain('/a/d/f/g'); // f/g becomes /a/d/f/g
});
});
describe('precedence rules', () => {
it('should prioritize root .gitignore over .git/info/exclude', async () => {
beforeEach(async () => {
await setupGitRepo();
});
it('should prioritize nested .gitignore over root .gitignore', async () => {
await createTestFile('.gitignore', '*.log');
await createTestFile('a/b/.gitignore', '!special.log');
expect(parser.isIgnored('a/b/any.log')).toBe(true);
expect(parser.isIgnored('a/b/special.log')).toBe(false);
});
it('should prioritize .gitignore over .git/info/exclude', async () => {
// Exclude all .log files
await createTestFile(path.join('.git', 'info', 'exclude'), '*.log');
// But make an exception in the root .gitignore
await createTestFile('.gitignore', '!important.log');
parser.loadGitRepoPatterns();
expect(parser.isIgnored('some.log')).toBe(true);
expect(parser.isIgnored('important.log')).toBe(false);
expect(parser.isIgnored(path.join('subdir', 'some.log'))).toBe(true);
@@ -266,15 +234,4 @@ src/*.tmp
);
});
});
describe('getIgnoredPatterns', () => {
it('should return the raw patterns added', async () => {
await setupGitRepo();
const gitignoreContent = '*.log\n!important.log';
await createTestFile('.gitignore', gitignoreContent);
parser.loadGitRepoPatterns();
expect(parser.getPatterns()).toEqual(['.git', '*.log', '!important.log']);
});
});
});