# CBMignore Syntax and Precedence Over Gitignore: A Complete Guide

> Master .cbmignore syntax and its precedence over .gitignore. Learn how Codebase Memory overrides Git excludes to streamline your workflow.

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

---

**`.cbmignore` uses standard gitignore pattern syntax but sits lower in the precedence hierarchy than repository `.gitignore` files, only able to override global Git excludes.**

The `codebase-memory-mcp` project by DeusData uses `.cbmignore` files to control which paths are fed to the indexer during the discovery phase. While the pattern syntax mirrors familiar gitignore conventions, the precedence rules differ significantly, determining whether your exclusion rules actually affect indexing. Understanding these mechanics ensures you configure file filtering correctly without silent failures.

## CBMignore Syntax: Gitignore-Compatible Patterns

According to the specification in [`docs/cbmignore.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/cbmignore.md), the `.cbmignore` format follows the classic gitignore specification exactly. The file consists of one pattern per line, with blank lines and lines beginning with `#` ignored.

### Core Pattern Operators

The supported wildcards and operators include:

- **`*`** – Matches any run of characters except `/`
- **`?`** – Matches exactly one character except `/`
- **`**`** – Cross-directory wildcard (e.g., `**/name`, `dir/**`, `a/**/b`)
- **`[abc]` / `[a-z]`** – Character classes (negated with `!` or `^`)
- **Trailing `/`** – Directory-only match
- **Leading `/`** – Anchor to the repository root
- **No `/`** – Match the name at any depth
- **Leading `!`** – Negation to re-include a previously excluded path (last matching rule wins)

### Practical Syntax Examples

```gitignore

# Generated protobuf output, anywhere in the tree

*.pb.go

# A specific top-level directory (anchored)

/third_party/

# Any directory named "snapshots" at any depth

snapshots/

# Everything under any fixtures directory

**/fixtures/**

# Anchored glob for single-character API versions

/api/v?/generated/

# Yearly log folders

/logs/202[0-9]/

```

## Precedence Hierarchy: Five Layers of Ignore Resolution

The discovery logic implemented in [`src/discover/discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/discover/discover.c) evaluates ignore rules in a fixed five-layer order. The first layer that rejects a path wins, meaning `.cbmignore` cannot veto decisions made by higher layers.

1. **Built-in skip list** – Hard-coded directories such as `.git`, `node_modules`, `dist`, `target`, and `vendor` that cannot be overridden by any configuration.

2. **Repository-level `.gitignore`** – The root `.gitignore` file merged with work-tree-aware `info/exclude`.

3. **Nested `.gitignore` files** – Any `.gitignore` discovered while walking subdirectories, matched relative to their own location.

4. **`.cbmignore`** – A positive match skips the path; a negated match (`!`) can rescue a path only from the next layer (Git global excludes).

5. **Git global excludes** – The `core.excludesFile` configuration (e.g., `$HOME/.config/git/ignore`) consulted only when the project is a Git repository.

### Critical Limitations

A rule in `.cbmignore` **cannot override** any built-in skip lists or any `.gitignore` rule (repository-level or nested). A negation (`!`) in `.cbmignore` **can override** the global Git excludes (layer 5) but **cannot** rescue files excluded by layers 1-4. Within the same `.cbmignore` file, the last matching rule wins, mirroring standard gitignore behavior.

## Practical Implementation Examples

### Example 1: Simple Positive Ignore

Create a `.cbmignore` file in your repository root:

```gitignore
generated/
*.pb.go

```

**Effect**: Any file under a `generated/` directory or any file ending with [`.pb.go`](https://github.com/DeusData/codebase-memory-mcp/blob/main/.pb.go) is omitted from the index, regardless of what `.gitignore` says.

### Example 2: Negating a Global Git Exclude

Assume the user's global Git exclude contains `*.sql`:

*File: `~/.config/git/ignore`*

```gitignore
*.sql

```

*Project `.cbmignore`:*

```gitignore
!*.sql

```

**Effect**: The global rule is overridden, and SQL files are indexed. This works because the negation targets layer 5.

### Example 3: Attempting to Override Repository Gitignore (Fails)

*Repository `.gitignore`:*

```gitignore
*.log

```

*Project `.cbmignore`:*

```gitignore
!*.log

```

**Effect**: The `.log` files remain ignored because `.gitignore` (layer 2) outranks `.cbmignore` (layer 4). This limitation is verified in the test suite at [`tests/test_discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/tests/test_discover.c).

### Example 4: Combining Patterns

```gitignore

# Exclude all generated protobuf files

*.pb.go

# But keep CI YAML files

!ci.yaml

```

**Effect**: All `*.pb.go` files are skipped, while [`ci.yaml`](https://github.com/DeusData/codebase-memory-mcp/blob/main/ci.yaml) is explicitly re-included (last-match wins within the file).

## Summary

- **`.cbmignore`** uses identical syntax to `.gitignore`, supporting wildcards, anchoring, and negation.
- Precedence follows a strict five-layer hierarchy where `.cbmignore` sits below built-in skips and all `.gitignore` variants.
- Negation rules (`!`) in `.cbmignore` only function against global Git excludes, not repository-level ignores.
- The implementation resides in [`src/discover/discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/discover/discover.c), with comprehensive test coverage in [`tests/test_discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/tests/test_discover.c).
- For complete syntax details, reference [`docs/cbmignore.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/cbmignore.md) in the DeusData/codebase-memory-mcp repository.

## Frequently Asked Questions

### Can `.cbmignore` override rules in my repository's `.gitignore`?

No. The precedence hierarchy places `.cbmignore` at layer 4, below both repository-level and nested `.gitignore` files at layers 2 and 3. If a path is excluded by any `.gitignore` file, `.cbmignore` cannot rescue it, even with a negation pattern.

### What is the exact syntax specification for `.cbmignore`?

The syntax is identical to standard gitignore format as documented in [`docs/cbmignore.md`](https://github.com/DeusData/codebase-memory-mcp/blob/main/docs/cbmignore.md). It supports `*` for wildcards, `**` for cross-directory matching, `?` for single characters, character classes `[abc]`, trailing `/` for directories, leading `/` for root anchoring, and `!` for negation.

### Why aren't my negation rules in `.cbmignore` working?

Negation patterns (`!`) only have authority to override the global Git excludes file (layer 5). They cannot re-include files that were excluded by the built-in skip list, repository `.gitignore`, or nested `.gitignore` files. If you need to index files excluded by the repository's own `.gitignore`, you must modify that file rather than using `.cbmignore`.

### Where should the `.cbmignore` file be located?

Place `.cbmignore` in the repository root directory. The discovery module in [`src/discover/discover.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/discover/discover.c) evaluates this file during the directory walk, applying its rules relative to the repository root when patterns use leading `/` anchors.