# How to Ignore Files with .cbmignore and Understand Layer Precedence in codebase-memory-mcp

> Learn how to ignore files with .cbmignore and understand layer precedence in codebase-memory-mcp. Exclude files using .gitignore style patterns with a strict layer precedence system.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: how-to-guide
- Published: 2026-07-30

---

**The `.cbmignore` file lets you exclude specific files and directories from indexing using `.gitignore`-style patterns, evaluated through five strict layers where the first rejection wins and only negations targeting the global Git exclude layer can be overridden.**

The **codebase-memory-mcp** project (available at `DeusData/codebase-memory-mcp`) uses a layered discovery system to decide which paths enter the index graph. While the tool respects your existing Git ignore rules, `.cbmignore` provides an additional project-specific filter with unique precedence semantics. Understanding how these layers interact ensures you configure exclusions correctly without unexpected indexing behavior.

## What Is .cbmignore?

`.cbmignore` is a repository-specific ignore file read **once** from the repository root (`<repo>/.cbmignore`). It follows standard `.gitignore` glob syntax—supporting wildcards, glob-stars (`**`), character classes, and leading `!` for negation. According to the specification in [`docs/cbmignore.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/cbmignore.md), this file allows teams to define exclusions that apply only to the memory indexing context, separate from version control rules.

When the discovery walk runs (implemented in [`src/discover/discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/discover/discover.c)), the indexer evaluates every path through a fixed sequence of five filtering layers. **The first layer that rejects a path terminates evaluation immediately**, making layer precedence critical to effective configuration.

## The Five Layers of Discovery Filters

The discovery process applies filters in the following strict order, as defined in the core discovery implementation:

### 1. Built-in Skip List

The indexer maintains a hard-coded list of approximately 60 entries including `.git`, `node_modules`, `dist`, `target`, `vendor`, and common tool caches. These skips are **non-negotiable**—no ignore file can override them, and they are applied before any file system inspection occurs.

### 2. Repository Root .gitignore

The repository's root `.gitignore` merges with the git common-directory `info/exclude` file. Within this layer, later patterns take precedence over earlier ones, following standard Git conflict resolution semantics.

### 3. Nested .gitignore Files

During directory traversal, any `.gitignore` files found in subdirectories are evaluated relative to their containing directory. These nested rules apply dynamically as the walk descends, with patterns later in the traversal capable of overriding earlier definitions.

### 4. .cbmignore Rules

The `.cbmignore` file at the repository root applies at this stage. A positive match here skips the path permanently. However, negated patterns (`!pattern`) in `.cbmignore` possess limited scope: they can **rescue** paths only from **Layer 5 (Git global excludes)**. They cannot un-exclude files rejected by the built-in list, root `.gitignore`, or nested `.gitignore` files.

### 5. Git Global Excludes

The final layer references the file specified by Git's `core.excludesFile` configuration (typically `~/.config/git/ignore` or `~/.gitignore_global`). This layer applies only when the project is a valid Git repository, and it represents the **only** layer that `.cbmignore` negations can override.

> **Note:** Additional fast-mode filters for file suffixes, size caps, and symlinks run before these five layers and are equally non-overridable by `.cbmignore`.

## How to Create and Configure .cbmignore

Create a file named `.cbmignore` in your repository root. Add one pattern per line—blank lines and lines starting with `#` are ignored. The syntax mirrors `.gitignore`:

```gitignore

# Exclude generated protobuf files anywhere

*.pb.go

# Exclude a top-level directory specifically

/third_party/

# Exclude "snapshots" directories at any depth

snapshots/

# Exclude everything under any fixtures directory

**/fixtures/**

```

Commit this file to share indexing exclusions with your team, or add it to `.gitignore` if the rules should remain local. Changes take effect on the next indexing run, which can be triggered manually or will occur automatically depending on your configuration.

## Negation Patterns and Their Limitations

Within `.cbmignore`, patterns evaluate top-to-bottom, and **the last matching pattern wins**. This allows you to exclude broad categories while re-including specific files:

```gitignore

# Exclude all YAML files

*.yaml

# But keep the CI configuration

!ci.yaml

```

**Critical limitation:** Negations can only rescue paths from Layer 5 (Git global excludes). If a file is excluded by the built-in skip list, root `.gitignore`, or nested `.gitignore`, a `.cbmignore` negation cannot save it. Additionally, if a parent directory is excluded, the walk never descends into it, making subdirectory negations ineffective unless you also negate the parent.

## Practical Examples

### Excluding Build Artifacts

To prevent build outputs from polluting the index while respecting that these might be tracked or ignored differently in Git:

```gitignore

# .cbmignore at repo root

obj/
dist/
*.o
*.a

```

This skips all `obj/` and `dist/` directories regardless of `.gitignore` status, applying at Layer 4.

### Overriding Global Git Excludes

Assume your global Git exclude (`~/.config/git/ignore`) contains `*.sql` to ignore database dumps across all projects. To ensure SQL files are indexed in this specific repository:

```gitignore

# .cbmignore

!*.sql

```

This negation operates at Layer 4 to rescue the files from Layer 5, while still respecting built-in skips and repository `.gitignore` rules.

### Selective Directory Inclusion

To ignore a generated directory except for a specific subdirectory you need indexed:

```gitignore

# .cbmignore

# Exclude everything under "generated/"

generated/**

# Rescue only the "keep" subdirectory

!generated/keep/

```

Because excluding `generated/` prevents the walker from descending into it, you must explicitly negate `generated/keep/` to re-enable traversal into that path.

## Verifying Your Ignore Rules

When running `codebase-memory-mcp index_repository`, the response includes an `excluded` field showing which directories were omitted. As implemented in [`src/mcp/mcp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c), the output format is:

```json
{
  "dirs": ["third_party/", "snapshots/"],
  "count": 2,
  "truncated": false
}

```

This diagnostic confirms which layers filtered specific paths, allowing you to verify that your `.cbmignore` patterns apply as expected.

## Summary

- `.cbmignore` resides at the repository root and uses standard glob syntax defined in [`docs/cbmignore.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/cbmignore.md).
- Five layers evaluate in fixed order: built-in skips, root `.gitignore`, nested `.gitignore`, `.cbmignore`, and global Git excludes.
- **First match wins**—once a path is rejected by any layer, evaluation stops.
- `.cbmignore` negations (`!pattern`) can only rescue files from the **global Git exclude layer**, not from `.gitignore` or built-in rules.
- Verify exclusions through the JSON output returned by the indexing command.

## Frequently Asked Questions

### Can .cbmignore override .gitignore rules?

No. `.cbmignore` operates at Layer 4, while repository and nested `.gitignore` files occupy Layers 2 and 3. Because the discovery walk evaluates layers sequentially and stops at the first rejection, a file excluded by `.gitignore` never reaches the `.cbmignore` evaluation stage. Only Layer 5 (Git global excludes) can be overridden by `.cbmignore` negations.

### Why isn't my negated pattern re-including files?

Negated patterns in `.cbmignore` (`!pattern`) only affect paths caught by the Git global exclude layer. If the file is excluded by the built-in skip list, root `.gitignore`, or nested `.gitignore`, the negation cannot rescue it. Additionally, if you exclude a parent directory, the indexer never traverses into it, so negating a child path has no effect unless you also negate the parent directory itself.

### Where should I place the .cbmignore file?

Always place `.cbmignore` in the **repository root** (`<repo>/.cbmignore`). The discovery implementation in [`src/discover/discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/discover/discover.c) reads this file exactly once at the beginning of the indexing process and does not search for it in subdirectories. Patterns inside the file can target paths at any depth using relative patterns or glob-stars.

### How do I check which files were excluded by which layer?

Run the `codebase-memory-mcp index_repository` command and inspect the `excluded` field in the JSON response. This output lists the directories omitted during discovery, though it aggregates results from all layers. For detailed debugging, consult the implementation in [`src/mcp/mcp.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/mcp/mcp.c) which handles the diagnostic messaging during the discovery walk.