From cb06a7decc6ad10d72c7a78a1ceb7e3f51c25976 Mon Sep 17 00:00:00 2001 From: himanshupatro-334 Date: Tue, 11 Aug 2026 16:54:35 +0100 Subject: [PATCH] fix(detect): preserve explicit scan roots vs parent unanchored .gitignore (#2468) --- graphify/detect.py | 9 ++++++- tests/test_detect.py | 58 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/graphify/detect.py b/graphify/detect.py index 60c30786..1c953c36 100644 --- a/graphify/detect.py +++ b/graphify/detect.py @@ -1189,7 +1189,14 @@ def _is_ignored( except ValueError: continue # target outside this pattern's anchor: cannot match if rel_anchor != ".": - matched = _matches(rel_anchor, p, path_relative=path_relative) + rel = rel_anchor + if not path_relative: + try: + if len(root.parts) > len(anchor.parts): + rel = str(target.relative_to(root)).replace(os.sep, "/") + except ValueError: + pass + matched = _matches(rel, p, path_relative=path_relative) if matched and directory_only and not target.is_dir(): matched = False diff --git a/tests/test_detect.py b/tests/test_detect.py index 2c6cc177..e9676a10 100644 --- a/tests/test_detect.py +++ b/tests/test_detect.py @@ -1209,6 +1209,64 @@ def test_anchored_multi_segment_pattern(tmp_path): ) +def test_detect_does_not_ignore_scan_root_itself_via_parent_gitignore(tmp_path): + """If a parent `.gitignore` (at the repo root) ignores the directory being scanned + (the scan root itself), files inside the scan root must not be ignored (#2468).""" + (tmp_path / ".git").mkdir() + + corpus_dir = tmp_path / "graphify-corpus" + corpus_dir.mkdir() + + (corpus_dir / "keep.py").write_text("x = 1\n", encoding="utf-8") + + docs_dir = corpus_dir / "docs" + docs_dir.mkdir() + (docs_dir / "intro.md").write_text("# Introduction\n", encoding="utf-8") + + (tmp_path / ".gitignore").write_text("graphify-corpus/\n", encoding="utf-8") + + result = detect(corpus_dir) + + all_files = [Path(f) for files in result["files"].values() for f in files] + file_names = {f.name for f in all_files} + + assert result["total_files"] == 2 + assert "keep.py" in file_names + assert "intro.md" in file_names + + +def test_detect_preserves_unrelated_parent_ignores_inside_scan_root(tmp_path): + """A parent `.gitignore` should still ignore unrelated directories (like `node_modules/`) + inside the scan root, even while the scan root itself is not ignored (#2468).""" + (tmp_path / ".git").mkdir() + + corpus_dir = tmp_path / "graphify-corpus" + corpus_dir.mkdir() + + (corpus_dir / "keep.py").write_text("x = 1\n", encoding="utf-8") + + docs_dir = corpus_dir / "docs" + docs_dir.mkdir() + (docs_dir / "intro.md").write_text("# Introduction\n", encoding="utf-8") + + node_modules_dir = corpus_dir / "node_modules" + node_modules_dir.mkdir() + (node_modules_dir / "lib.js").write_text("console.log(1);\n", encoding="utf-8") + + # Parent .gitignore ignoring the scan root itself AND node_modules/ + (tmp_path / ".gitignore").write_text("graphify-corpus/\nnode_modules/\n", encoding="utf-8") + + result = detect(corpus_dir) + + all_files = [Path(f) for files in result["files"].values() for f in files] + file_names = {f.name for f in all_files} + + assert result["total_files"] == 2 + assert "keep.py" in file_names + assert "intro.md" in file_names + assert "lib.js" not in file_names + + # Tests for #1235 - memoise _is_ignored/_eval results via a per-detect() cache def test_is_ignored_cache_matches_uncached_results(tmp_path):