|
| 1 | +# `.cbmignore` — Excluding Files from Indexing |
| 2 | + |
| 3 | +`.cbmignore` is a project-specific ignore file that controls which files the |
| 4 | +indexer sees. It uses gitignore-style syntax and is read from the **root of |
| 5 | +the indexed directory** (`<repo>/.cbmignore`). Nested `.cbmignore` files in |
| 6 | +subdirectories are not read. |
| 7 | + |
| 8 | +It applies at **file discovery time** — the directory walk that selects files |
| 9 | +for parsing. Every indexing path uses the same discovery: the initial |
| 10 | +`index_repository`, manual re-indexing, and background auto-sync. A path |
| 11 | +matched by `.cbmignore` never enters the graph. Changes to `.cbmignore` take |
| 12 | +effect on the next (re-)index. |
| 13 | + |
| 14 | +Unlike `.gitignore`, it has no effect on git itself — it only shapes what the |
| 15 | +indexer sees. Commit it to share indexing excludes with your team, or list it |
| 16 | +in `.gitignore` to keep personal excludes untracked. |
| 17 | + |
| 18 | +To verify it works: directory subtrees skipped during discovery are reported |
| 19 | +in the `index_repository` response under `excluded` |
| 20 | +(`{"dirs": [up to 25 paths], "count": <total>, "truncated": <bool>}`). |
| 21 | + |
| 22 | +## Syntax |
| 23 | + |
| 24 | +One pattern per line. Blank lines are ignored, lines starting with `#` are |
| 25 | +comments, and trailing whitespace is trimmed. |
| 26 | + |
| 27 | +| Feature | Meaning | |
| 28 | +|---|---| |
| 29 | +| `*` | matches any run of characters, except `/` | |
| 30 | +| `?` | matches exactly one character, except `/` | |
| 31 | +| `**` | matches across directory boundaries (`**/name`, `dir/**`, `a/**/b`) | |
| 32 | +| `[abc]`, `[a-z]` | character classes; `[!a-z]` / `[^a-z]` negate the class | |
| 33 | +| trailing `/` | pattern matches **directories only** | |
| 34 | +| `/` anywhere else | anchors the pattern to the repo root | |
| 35 | +| no `/` in pattern | matches the file/directory name at **any depth** | |
| 36 | +| leading `!` | negation — re-includes a previously matched path; the **last matching pattern wins** | |
| 37 | + |
| 38 | +Examples: |
| 39 | + |
| 40 | +```gitignore |
| 41 | +# Generated protobuf output, anywhere in the tree |
| 42 | +*.pb.go |
| 43 | +
|
| 44 | +# A specific top-level directory (leading / anchors to the repo root) |
| 45 | +/third_party/ |
| 46 | +
|
| 47 | +# Any directory named "snapshots", at any depth (trailing / = directories only) |
| 48 | +snapshots/ |
| 49 | +
|
| 50 | +# Everything under any fixtures directory |
| 51 | +**/fixtures/** |
| 52 | +
|
| 53 | +# Anchored glob: generated clients for any single-character API version |
| 54 | +/api/v?/generated/ |
| 55 | +
|
| 56 | +# Character class: yearly log folders 2020-2029 |
| 57 | +/logs/202[0-9]/ |
| 58 | +
|
| 59 | +# Ignore all YAML, but keep CI configs (negation — last match wins) |
| 60 | +*.yaml |
| 61 | +!ci.yaml |
| 62 | +``` |
| 63 | + |
| 64 | +## Precedence |
| 65 | + |
| 66 | +Discovery applies its filters in a fixed order — the first layer that rejects |
| 67 | +a path wins. For directories: |
| 68 | + |
| 69 | +1. **Built-in skip list** — `.git`, `node_modules`, `dist`, `target`, |
| 70 | + `vendor`, tool caches, etc. (60+ names; the fast/moderate index modes add |
| 71 | + more, e.g. `docs`, `examples`, `testdata`). Not overridable from any |
| 72 | + ignore file today. |
| 73 | +2. **Repo `.gitignore`** — `<repo>/.gitignore` merged with |
| 74 | + `<git-common-dir>/info/exclude` (worktree-aware); later patterns win on |
| 75 | + conflict. Honored even when the indexed directory is not a git repo root. |
| 76 | +3. **Nested `.gitignore` files** — picked up during the walk and matched |
| 77 | + relative to their own directory. |
| 78 | +4. **`.cbmignore`** — a positive match skips the path; a negated match can |
| 79 | + only rescue paths from layer 5. |
| 80 | +5. **Git global excludes** — `core.excludesFile` from `~/.gitconfig` or the |
| 81 | + XDG git config (default `$XDG_CONFIG_HOME/git/ignore`); consulted only |
| 82 | + when the project is a git repo with a config. |
| 83 | + |
| 84 | +For files, built-in suffix filters (`.png`, `.o`, `.db`, …; fast modes add |
| 85 | +archives, media, lockfiles, `.min.js`, …) and fast-mode filename/substring |
| 86 | +filters run **before** the ignore files, and a maximum-file-size cap runs |
| 87 | +after them; none of these are overridable from `.cbmignore`. Symlinks are |
| 88 | +always skipped. |
| 89 | + |
| 90 | +## Negation (`!`) — current behavior |
| 91 | + |
| 92 | +- **Within `.cbmignore`**: standard gitignore semantics. Patterns are |
| 93 | + evaluated top to bottom and the last matching pattern wins, so |
| 94 | + `!pattern` re-includes something an earlier line excluded. |
| 95 | +- **Parent pruning** (same caveat as git): when a directory is excluded, the |
| 96 | + walk never descends into it — you cannot re-include a file whose parent |
| 97 | + directory is excluded. Negate the directory itself if you need its |
| 98 | + contents. |
| 99 | +- **Across layers**: a `.cbmignore` negation overrides the **git global |
| 100 | + excludes** layer only. Example: your `~/.config/git/ignore` ignores |
| 101 | + `*.sql`, but this project's SQL should be indexed — add `!*.sql` to |
| 102 | + `.cbmignore`. Negation cannot override the built-in skip lists, the repo |
| 103 | + `.gitignore`/`info/exclude`, nested `.gitignore` files, the built-in |
| 104 | + suffix/filename filters, or the size cap. |
| 105 | + |
| 106 | +### Planned (not yet implemented) |
| 107 | + |
| 108 | +The negation story is being unified; none of the following works yet: |
| 109 | + |
| 110 | +- `!` in `.cbmignore` will be able to un-skip ordinary built-in skip |
| 111 | + directories (`obj/`, `dist/`, `target/`, …) so build-output-like |
| 112 | + directories that actually contain source can be indexed. |
| 113 | +- A small safety core stays non-negatable by design — `.git`, |
| 114 | + `node_modules`, and worktree-internal directories — because indexing them |
| 115 | + risks OOM and correctness issues (see issue #489). |
| 116 | +- Auxiliary filesystem walkers will honor the same ignore predicate as |
| 117 | + discovery, so every code path sees an identical ignore decision |
| 118 | + (unification tracked in a follow-up issue). |
| 119 | + |
| 120 | +Until these land, the "Precedence" and "Negation — current behavior" sections |
| 121 | +above describe the actual behavior. |
0 commit comments