Skip to content

A single negation (!) rule in .graphifyignore disables all directory pruning #1274

Description

@papinto

Summary

If .graphifyignore contains any negation pattern (!…), directory-level
pruning is switched off for the entire detect() walk. The walk then
descends into every ignored directory — node_modules/, bin/, obj/,
.git/, graphify-out/, etc. — even though those are still excluded from the
output. On a large repo this is a major, silent slowdown.

Output stays correct (the per-file _is_ignored filter still drops those
files), so this is a walk-performance bug, not a correctness bug — but the
detection pass can take dramatically longer than it should.

Where it happens

graphify/detect.py (verified on v0.8.37 / current v8 HEAD):

  • detect.py:1047has_negation = any(p.startswith("!") for _, p in ignore_patterns)
  • detect.py:1051 — directory kept if (has_negation or not _is_ignored(...))

Because has_negation is computed once for the whole walk, one unrelated !
rule keeps every ignored directory in the traversal.

Why it matters

The natural way to do a scoped build is a repo-wide exclude plus a re-include —
e.g. ignore everything but docs/:

node_modules/
*.md
!docs/**

That single !docs/** rule silently makes the scan walk the whole tree,
including all of node_modules/. We hit this building a docs-only graph: the
scoped extract appeared to hang because it was descending into every
node_modules/.

Repro

mkdir -p proj/bin/Debug proj/docs && echo x=1 > proj/bin/Debug/a.py
printf 'bin/\n*.md\n!docs/**\n' > proj/.graphifyignore
# detection descends into bin/ (a pattern-ignored, non-noise dir) despite the bin/ rule

Expected

A negation should re-include matching files without disabling pruning of
unrelated ignored directories. _is_ignored already honours negations
correctly — last-match-wins lets !dir/ un-ignore a directory (so it isn't
pruned), and gitignore's parent-exclusion rule means a ! cannot rescue a file
beneath an excluded directory — so descending an ignored directory to find a
re-included file is never necessary. Pruning can rely purely on _is_noise_dir

  • _is_ignored.

Workaround (until fixed)

Keep .graphifyignore negation-free for large/whole-repo scans; for a scoped
build, place an ancestor-scoped inner ignore (e.g. a docs/.graphifyignore
with !*.md) instead of a root-level re-include.

I have a fix + regression test ready and will open a PR shortly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions