Skip to content

feat(config): add ignore_worktrees to skip linked git worktrees - #1430

Open
vitalNohj wants to merge 2 commits into
DeusData:mainfrom
vitalNohj:fm/cbm-worktree-ignore-1
Open

feat(config): add ignore_worktrees to skip linked git worktrees#1430
vitalNohj wants to merge 2 commits into
DeusData:mainfrom
vitalNohj:fm/cbm-worktree-ignore-1

Conversation

@vitalNohj

Copy link
Copy Markdown

Problem

Agent workflows increasingly use git worktree add to run several branches of the same repo side by side. Every worktree is discovered as its own project, so the cache accumulates a near-duplicate graph per worktree and project pickers get noisy. Today there is no way to say "index the main checkout, leave the worktrees alone".

Change

Adds an opt-in ignore_worktrees config key. When enabled, a linked worktree is left alone across every indexing entry point:

  • auto-index on connect skips it (logged as autoindex.skip with reason=linked_worktree)
  • the daemon's background auto-index skips it
  • explicit index_repository refuses with an actionable message naming both ways forward
  • hook-augment guidance stops telling the agent to run index_repository in a directory where it would only be refused

The explicit call keeps an escape hatch — index_worktree=true indexes a worktree regardless of the setting, so the key never becomes a dead end:

repo_path is a linked git worktree and ignore_worktrees is enabled. Pass
index_worktree=true to index it anyway, or run:
codebase-memory-mcp config set ignore_worktrees false

Default is false, so nothing changes for existing users unless they opt in.

Detection

cbm_git_is_linked_worktree() is git plumbing only, no subprocess — it runs on every session start, so shelling out the way cbm_git_context_resolve() does was not an option.

<path>/.git must be a regular file holding a gitdir: pointer and that gitdir must contain a commondir file.

That second condition is the important one: a submodule's .git is also a gitlink file, so a naive "is .git a regular file?" check misclassifies every submodule as a worktree. A submodule's gitdir (<super>/.git/modules/<name>) has no commondir entry, which separates the two cleanly. The main checkout of a repo that has worktrees is never affected.

Tests

tests/test_git_context.c — detection truth table: linked worktree true; the main checkout of that same repo false; a submodule false; a plain non-git dir false.

tests/test_mcp.c — the config gate on explicit index_repository: worktree refused, main checkout not refused, and index_worktree=true escaping the gate.

Both follow the existing fixture patterns in those files (th_mktempdir/th_rmtree, SKIP_PLATFORM on Windows, graceful skip when git is unavailable).

Verification

  • scripts/build.sh — clean
  • scripts/test.sh6661 passed, 0 failed (116 suites)
  • make -f Makefile.cbm security — all 8 layers pass
  • clang-format (repo-wide lint-format) and cppcheck — clean; clang-tidy reports 0 findings on the added lines
  • manually verified end-to-end against the built binary: refusal on a worktree, normal indexing on the main checkout, index_worktree=true override, and unchanged default-off behaviour

Docs

docs/CONFIGURATION.md gets a table row plus a section covering motivation, the enable command, all three behaviours, and the submodule/main-checkout caveat. README.md gets a short paragraph next to auto_watch.

Agents that use `git worktree add` to run parallel branches end up with one
indexed project per worktree. Each is a near-duplicate of the main checkout,
so the cache fills with redundant graphs and project pickers get noisy.

Add an opt-in `ignore_worktrees` config key. When enabled, a linked worktree
is left alone:

  - auto-index on connect skips it (logged as autoindex.skip)
  - the daemon's background auto-index skips it
  - explicit index_repository refuses with an actionable message naming both
    ways forward (index_worktree=true, or turn the key off)
  - the hook-augment "not indexed" guidance stops telling the agent to run
    index_repository in a directory where it would only be refused

Detection is git plumbing, no subprocess: <path>/.git must be a regular file
holding a "gitdir:" pointer AND that gitdir must contain a `commondir` file.
The commondir check is what separates a linked worktree from a submodule,
whose .git is also a gitlink file but whose gitdir has no commondir. The main
checkout of a repo that has worktrees is never affected.

Defaults to false, so behaviour is unchanged unless the key is set.

Signed-off-by: Nohj <vitalnohj@gmail.com>
@vitalNohj
vitalNohj requested a review from DeusData as a code owner August 4, 2026 11:23
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

The macos-14 TSan leg failed in test_mcp.c's DeusData#853 auto_watch guard, which
runs a real indexing worker in a forked child under a wall-clock alarm(60).
That budget is timing-sensitive under TSan instrumentation on the macOS
runners and is unrelated to this change: the ignore_worktrees gate
short-circuits on the config key, so with the key unset (the default, and
what that test uses) the worktree check is never evaluated.

Signed-off-by: Nohj <vitalnohj@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant