perf(claude.sh): expand .claudeignore with a pruning find walk #25

Merged
claudiomaradonna merged 1 commit from perf/claudeignore-pruning into main 2026-07-29 08:40:29 +02:00

Each pattern was expanded with bash globstar, once per pattern. Bash
cannot prune, so every pattern re-walked the whole workspace including
the subtrees the ignore file itself hides -- on a 127k-path fixture,
99.2% of walked paths were inside node_modules/ and .git/, both listed
in .claudeignore. Matches under an already-tmpfs'd directory were
emitted as mounts too, though an empty tmpfs already hides them.

Patterns now become path-aware regexes fed to find(1), which prunes at
each matching directory. On that fixture a typical 6-pattern file goes
from 0.72s to 0.05s; node_modules/ + .git/ + '**/*.js' goes from 3.67s
and 240,027 arguments to 0.02s and 27. The old form was not merely slow:
7.36MB of argv against a 2MB ARG_MAX fails the exec outright.

Also fixes three defects in the code being replaced:

  • hits=($pat) was unquoted, so a pattern containing a space was
    word-split and matched nothing, silently leaving the file visible.
  • ${pattern%%:space:} strips one character, so two trailing
    spaces left one behind. Previously masked by the word-splitting.
  • The summary counted argv entries, reporting twice the path count.

Verified against the previous implementation over 800 fuzzed patterns:
the only divergence is that a pattern segment no longer reaches through
a symlinked directory, since find does not follow one. Documented in
README, pinned by a test, and the target stays hidden under its real
path. Adds tests/test_claudeignore.sh (27 tests); the feature had none.

Claude-Session: https://claude.ai/code/session_01NNo6M8YqCf7DrUDq6jBjF9

Each pattern was expanded with bash globstar, once per pattern. Bash cannot prune, so every pattern re-walked the whole workspace including the subtrees the ignore file itself hides -- on a 127k-path fixture, 99.2% of walked paths were inside node_modules/ and .git/, both listed in .claudeignore. Matches under an already-tmpfs'd directory were emitted as mounts too, though an empty tmpfs already hides them. Patterns now become path-aware regexes fed to find(1), which prunes at each matching directory. On that fixture a typical 6-pattern file goes from 0.72s to 0.05s; node_modules/ + .git/ + '**/*.js' goes from 3.67s and 240,027 arguments to 0.02s and 27. The old form was not merely slow: 7.36MB of argv against a 2MB ARG_MAX fails the exec outright. Also fixes three defects in the code being replaced: - hits=($pat) was unquoted, so a pattern containing a space was word-split and matched nothing, silently leaving the file visible. - ${pattern%%[[:space:]]} strips one character, so two trailing spaces left one behind. Previously masked by the word-splitting. - The summary counted argv entries, reporting twice the path count. Verified against the previous implementation over 800 fuzzed patterns: the only divergence is that a pattern segment no longer reaches through a symlinked directory, since find does not follow one. Documented in README, pinned by a test, and the target stays hidden under its real path. Adds tests/test_claudeignore.sh (27 tests); the feature had none. Claude-Session: https://claude.ai/code/session_01NNo6M8YqCf7DrUDq6jBjF9
Each pattern was expanded with bash globstar, once per pattern. Bash
cannot prune, so every pattern re-walked the whole workspace including
the subtrees the ignore file itself hides -- on a 127k-path fixture,
99.2% of walked paths were inside node_modules/ and .git/, both listed
in .claudeignore. Matches under an already-tmpfs'd directory were
emitted as mounts too, though an empty tmpfs already hides them.

Patterns now become path-aware regexes fed to find(1), which prunes at
each matching directory. On that fixture a typical 6-pattern file goes
from 0.72s to 0.05s; node_modules/ + .git/ + '**/*.js' goes from 3.67s
and 240,027 arguments to 0.02s and 27. The old form was not merely slow:
7.36MB of argv against a 2MB ARG_MAX fails the exec outright.

Also fixes three defects in the code being replaced:

  - hits=($pat) was unquoted, so a pattern containing a space was
    word-split and matched nothing, silently leaving the file visible.
  - ${pattern%%[[:space:]]} strips one character, so two trailing
    spaces left one behind. Previously masked by the word-splitting.
  - The summary counted argv entries, reporting twice the path count.

Verified against the previous implementation over 800 fuzzed patterns:
the only divergence is that a pattern segment no longer reaches through
a symlinked directory, since find does not follow one. Documented in
README, pinned by a test, and the target stays hidden under its real
path. Adds tests/test_claudeignore.sh (27 tests); the feature had none.

Claude-Session: https://claude.ai/code/session_01NNo6M8YqCf7DrUDq6jBjF9
claudiomaradonna deleted branch perf/claudeignore-pruning 2026-07-29 08:40:31 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
claudiomaradonna/claude-jail!25
No description provided.