Merge pull request #448 from blaugold/fix-gitignore-file-discovery

fix: discover all `.gitignore` files in project
This commit is contained in:
Dominik Jain authored and GitHub committed 2025-08-08 20:49:10 +02:00
commit 3a1d14dbf4
2 files changed
+32 -6

No files matched your search

+2 -1
View File
@@ -141,7 +141,8 @@ class GitignoreParser:
:return: List of absolute paths to .gitignore files
"""
return glob.glob(self.repo_root + "/.gitignore") + glob.glob(self.repo_root + "/**/.gitignore")
relative_paths = glob.glob("**/.gitignore", root_dir=self.repo_root, recursive=True)
return [os.path.join(self.repo_root, rel_path) for rel_path in relative_paths]
def _create_ignore_spec(self, gitignore_file_path: str) -> GitignoreSpec:
"""
+30 -5
View File
@@ -37,8 +37,11 @@ class TestGitignoreParser:
│ ├── .gitignore
│ ├── main.py
│ ├── test.log
│ └── build/
│ └── output.o
│ ├── build/
│ │ └── output.o
│ └── lib/
│ ├── .gitignore
│ └── cache.tmp
└── docs/
├── .gitignore
├── api.md
@@ -48,6 +51,7 @@ class TestGitignoreParser:
# Create directories
(self.repo_path / "src").mkdir()
(self.repo_path / "src" / "build").mkdir()
(self.repo_path / "src" / "lib").mkdir()
(self.repo_path / "docs").mkdir()
(self.repo_path / "docs" / "temp").mkdir()
@@ -57,6 +61,7 @@ class TestGitignoreParser:
(self.repo_path / "src" / "main.py").touch()
(self.repo_path / "src" / "test.log").touch()
(self.repo_path / "src" / "build" / "output.o").touch()
(self.repo_path / "src" / "lib" / "cache.tmp").touch()
(self.repo_path / "docs" / "api.md").touch()
(self.repo_path / "docs" / "temp" / "draft.md").touch()
@@ -79,6 +84,15 @@ build/
"""
)
# Create src/lib/.gitignore (deeply nested)
src_lib_gitignore = self.repo_path / "src" / "lib" / ".gitignore"
src_lib_gitignore.write_text(
"""# Library gitignore
*.tmp
*.cache
"""
)
# Create docs/.gitignore
docs_gitignore = self.repo_path / "docs" / ".gitignore"
docs_gitignore.write_text(
@@ -93,10 +107,10 @@ temp/
parser = GitignoreParser(str(self.repo_path))
assert parser.repo_root == str(self.repo_path.absolute())
assert len(parser.get_ignore_specs()) == 3
assert len(parser.get_ignore_specs()) == 4
def test_find_gitignore_files(self):
"""Test finding all gitignore files in repository."""
"""Test finding all gitignore files in repository, including deeply nested ones."""
parser = GitignoreParser(str(self.repo_path))
# Get file paths from specs
@@ -106,9 +120,10 @@ temp/
rel_paths = [os.path.relpath(f, self.repo_path) for f in gitignore_files]
rel_paths.sort()
assert len(rel_paths) == 3
assert len(rel_paths) == 4
assert ".gitignore" in rel_paths
assert os.path.join("src", ".gitignore") in rel_paths
assert os.path.join("src", "lib", ".gitignore") in rel_paths # Deeply nested
assert os.path.join("docs", ".gitignore") in rel_paths
def test_parse_patterns_root_directory(self):
@@ -192,6 +207,16 @@ test.log
# But temp/ outside docs should not be ignored by docs/.gitignore
assert not parser.should_ignore("temp/file.txt")
# Test deeply nested .gitignore in src/lib/
# .tmp files in src/lib should be ignored
assert parser.should_ignore("src/lib/cache.tmp")
# .cache files in src/lib should also be ignored
assert parser.should_ignore("src/lib/data.cache")
# But .tmp files outside src/lib should not be ignored by src/lib/.gitignore
assert not parser.should_ignore("src/other.tmp")
def test_anchored_vs_non_anchored_patterns(self):
"""Test the difference between anchored and non-anchored patterns."""
# Create new test structure