mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-10 03:27:59 +00:00
fix(ignore): respect negation patterns in .gitnexusignore childrenIgnored
childrenIgnored checked `ig.ignores(rel) || ig.ignores(rel + '/')` which
short-circuited on the bare path — directory-only negation patterns like
`!iOS/` were missed because `ig.ignores('iOS')` treats the path as a file.
Now only checks with trailing slash since childrenIgnored is only called
for directories. Bare-name patterns (e.g. `local`) still match per gitignore spec.
Fixes #596
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5c4fca21c3
commit
aca78c042e
2 changed files with 50 additions and 3 deletions
|
|
@ -398,11 +398,14 @@ export const createIgnoreFilter = async (repoPath: string, options?: IgnoreOptio
|
|||
// defense-in-depth — do not remove `dot: false` assuming this covers it.
|
||||
if (DEFAULT_IGNORE_LIST.has(p.name)) return true;
|
||||
// Check against .gitignore / .gitnexusignore patterns.
|
||||
// Test both bare path and path with trailing slash to handle
|
||||
// bare-name patterns (e.g. `local`) and dir-only patterns (e.g. `local/`).
|
||||
// Since childrenIgnored is only called for directories, always test with
|
||||
// a trailing slash. This ensures directory-only negation patterns (e.g.
|
||||
// `!iOS/`) are applied correctly — without the slash, `ig.ignores('iOS')`
|
||||
// treats the path as a file and misses the negation.
|
||||
// Bare-name patterns (e.g. `local`) still match `local/` per gitignore spec.
|
||||
if (ig) {
|
||||
const rel = p.relative();
|
||||
if (rel && (ig.ignores(rel) || ig.ignores(rel + '/'))) return true;
|
||||
if (rel && ig.ignores(rel + '/')) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -333,6 +333,50 @@ describe('createIgnoreFilter', () => {
|
|||
await fs.unlink(path.join(tmpDir, '.gitignore'));
|
||||
});
|
||||
|
||||
it('childrenIgnored respects negation patterns (exclude-all + whitelist)', async () => {
|
||||
// Reproduces https://github.com/abhigyanpatwari/GitNexus/issues/596
|
||||
// Pattern: `*` (exclude all) + `!iOS/` + `!iOS/**` (whitelist iOS)
|
||||
await fs.writeFile(
|
||||
path.join(tmpDir, '.gitnexusignore'),
|
||||
'*\n!iOS/\n!iOS/**\n!backend/\n!backend/living_plan/\n!backend/living_plan/**\n',
|
||||
);
|
||||
const filter = await createIgnoreFilter(tmpDir);
|
||||
|
||||
// Whitelisted directories must NOT be pruned
|
||||
const iosPath = { name: 'iOS', relative: () => 'iOS' } as any;
|
||||
expect(filter.childrenIgnored(iosPath)).toBe(false);
|
||||
|
||||
const backendPath = { name: 'backend', relative: () => 'backend' } as any;
|
||||
expect(filter.childrenIgnored(backendPath)).toBe(false);
|
||||
|
||||
const livingPlanPath = { name: 'living_plan', relative: () => 'backend/living_plan' } as any;
|
||||
expect(filter.childrenIgnored(livingPlanPath)).toBe(false);
|
||||
|
||||
// Non-whitelisted directories must still be pruned
|
||||
const srcPath = { name: 'src', relative: () => 'src' } as any;
|
||||
expect(filter.childrenIgnored(srcPath)).toBe(true);
|
||||
|
||||
const libPath = { name: 'lib', relative: () => 'lib' } as any;
|
||||
expect(filter.childrenIgnored(libPath)).toBe(true);
|
||||
|
||||
await fs.unlink(path.join(tmpDir, '.gitnexusignore'));
|
||||
});
|
||||
|
||||
it('ignored respects negation patterns for files under whitelisted directories', async () => {
|
||||
await fs.writeFile(path.join(tmpDir, '.gitnexusignore'), '*\n!iOS/\n!iOS/**\n');
|
||||
const filter = await createIgnoreFilter(tmpDir);
|
||||
|
||||
// Files under whitelisted directory should NOT be ignored
|
||||
const swiftFile = { name: 'App.swift', relative: () => 'iOS/App.swift' } as any;
|
||||
expect(filter.ignored(swiftFile)).toBe(false);
|
||||
|
||||
// Files outside whitelisted directory should be ignored
|
||||
const pyFile = { name: 'main.py', relative: () => 'scripts/main.py' } as any;
|
||||
expect(filter.ignored(pyFile)).toBe(true);
|
||||
|
||||
await fs.unlink(path.join(tmpDir, '.gitnexusignore'));
|
||||
});
|
||||
|
||||
it('ignored returns true for file-glob patterns like *.log', async () => {
|
||||
await fs.writeFile(path.join(tmpDir, '.gitignore'), '*.log\n');
|
||||
const filter = await createIgnoreFilter(tmpDir);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue