Repository navigation
fix(scanner,config): skip nested worktrees, union --exclude with config, keep .codemap/ out of git status - #198
Merged
Merged
Conversation
…ig, keep .codemap/ out of git status Nested git repositories (linked worktrees whose .git is a gitdir file, plain clones, submodules) are repository boundaries at any depth, under dotted directories too; the file walker, the ast-grep scan, the Go fallback and the manifest reader share one detector (issue #131). --exclude now adds to the project config's exclude list instead of replacing it; --only and --depth keep overriding (issue #150). Every codemap write that creates .codemap/ in a git checkout appends .codemap/ to <git-common-dir>/info/exclude when no rule ignores it yet, never touching a tracked .gitignore; doctor reports tracked .codemap/ files (exit test of issue #184). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159tcanCgA3jxi2E7DdRVWJ
…ot fail the worktree test Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159tcanCgA3jxi2E7DdRVWJ
JordanCoin
added a commit
that referenced
this pull request
Oct 7, 2026
Resolves the one conflict in mcp/main.go handleGetImporters: the #140 not-indexed check runs first inside the empty-importers branch (an unindexed file has no Go package to be a sibling of), and an indexed file with zero importers falls through to #199's Go package-granularity wording. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159tcanCgA3jxi2E7DdRVWJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three scan-hygiene fixes, one commit.
Bug A: nested git worktrees were scanned as project files (#131)
scanner.IgnoredDirsonly skipped a directory literally named.git; a linked worktree's.gitis a file (gitdir: ...), andfindNestedGitReposonly looked one level down, skipped dotted directories and required.gitto be a directory.Fix.
scanner.IsNestedGitRepo(dir)treats a.gitentry that is a directory or a regular file as a repository boundary.scanner.NestedGitRepos(root)walks the tree (any depth, dotted directories included, not enteringIgnoredDirs, gitignored directories, or a nested repo's own subtree) and returns the root-relative paths. One implementation now feedsScanFiles(socodemap .,context,handoff, config init, the daemon),findNestedGitRepos(ast-grep--globs "!<rel>/**", still with cwd=root and.from #193, and the Go fallback), andReadExternalDeps. The scan root's own.gitis never a boundary, so scanning a linked worktree as root still works.Before/after on a clone of this repo with
git worktree add .claude/worktrees/agent-x HEADand a plain clone atsub-clone/:codemap .Files: 259→518(worktree) →777(+ clone)Files: 259codemap --deps --json .file entries.claude/worktrees/orsub-clone/)No printed line changes.
Bug B:
--excludereplaced the config's exclude list (#150)main.goonly usedprojCfg.Excludewhen the CLI list was empty, so--exclude ZZZNOMATCHsilently re-admitted everything the config excluded.Fix.
config.MergeExcludes(configured, cli)unions config patterns (first) with CLI patterns, dropping blanks and duplicates;main.goalways applies it.--onlykeeps its override semantics on purpose:onlyis an allow-list and "just Swift for this run" is the obvious intent, and I found no equivalent bug there.--depthis unchanged. Hook subprocesses that already forward the config's excludes now just dedupe.Before/after (config
{"exclude":["vendorx"]}, 7 files + config):codemap .→Files: 5;--exclude ZZZNOMATCH→ wasFiles: 8, nowFiles: 5;--exclude src→ wasFiles: 6, nowFiles: 3.README's "Project config" note now reads:
--onlyand--depthoverride the config's values for one run;--excludeadds to the config'sexcludelist… (replacing "CLI flags always override config"). The existingTestBinaryDependencyModesHonorCLIFiltersOverConfiglocked in the old override behaviour; it is renamed...MergeCLIFiltersWithConfigand asserts the union.Bug C: an unignored
.codemap/left in every repo codemap touches (exit test of #184)cmd.EnsureProjectConfig(#189) deliberately auto-writes.codemap/config.json; that stays. The fallback from the issue is implemented instead.Fix. New
internal/gitexclude(the git-exclude helpers fromcmd/gitignore.go, moved sowatch,handoffandtopologycan use them;cmd/gitignore.gokeeps its names as thin wrappers, socodemap setup/config initbehave exactly as before, tests untouched):EnsureIgnoredIfNeeded(root):git check-ignore -v .codemap/; only when nothing ignores it, append.codemap/to<git rev-parse --git-common-dir>/info/exclude(shared by linked worktrees; verified a worktree writes to the primary's.git/info/exclude). Never creates or edits a tracked.gitignore; a repo whose.gitignorealready covers.codemap/gets no redundant local rule.MkdirAll(dir, perm):os.MkdirAll, and if that just created the.codemapdirectory (dir or an ancestor), runEnsureIgnoredIfNeededon its parent. Best-effort, nogitcall once.codemapexists. Used byEnsureProjectConfig, hook state (pre-compact, session leases, agent-edit log), the watch daemon/pid/transition writes, handoffWriteLatest, topology cache andskill init.TrackedStateFiles(root)(git ls-files -z -- .codemap) backs the new doctor check.Before/after on a fresh clone of a one-file repo with no codemap config,
git status --porcelainafter each command:codemap .?? .codemap/codemap --deps .?? .codemap/codemap find main .?? .codemap/context,handoff --no-save,blast-radiusNew printed lines
codemap: added .codemap/ to /path/.git/info/exclude so git status stays clean; verify with: git check-ignore -v .codemap— sourced (names the file written and the command that reproduces the fact), precise (one rule, one path), bounded (only printed when the rule was written this run; silent when a rule already existed or outside git), useful (the verify command).codemap doctor:MISS tracked codemap state: 1 file(s) under .codemap/ are committed; run: git rm -r --cached .codemap— sourced (git ls-files .codemap), precise (exact count), bounded (scoped to.codemap/, index state only), useful (the exact repair command). Counts as a doctor failure.codemap doctor:OK tracked codemap state: 0 files under .codemap/ are committed (git ls-files .codemap)— sourced (names the command), precise (0), bounded (says what it checked), useful (confirms the ignore rule is sufficient; appears next tolocal Codemap ignore: effective via ...).The state-location question in #184 (whether
.codemap/should live in the repo at all) is intentionally left open.Tests
scanner:TestScanFilesSkipsNestedWorktreesAndClones,TestNestedGitReposFindsWorktreesAtAnyDepth,TestReadExternalDepsSkipsNestedRepos(fixture built with realgit worktree addunder.claude/worktrees/plusgit clonetwo levels down),TestScanDirectoryExcludesNestedWorktreesByPath(captured ast-grep args contain--globs !.claude/worktrees/agent-x/**and!tools/sub-clone/**),TestScanDirectoryRealAstGrepSkipsNestedWorktree(real ast-grep indexes only the root file).config:TestMergeExcludesUnionsConfigAndCLI. root:TestMainExcludeFlagAddsToConfigExcludes(config exclude + non-matching CLI exclude leaves the count unchanged; matching removes both sets).internal/gitexclude: fresh clone stays clean after a state write, no local rule when.gitignorecovers it, non-git and no-.codemappaths are plainMkdirAll, linked worktree writes the primary's shared exclude, tracked-file count.cmd:TestEnsureProjectConfigKeepsFreshCloneClean,TestRunDoctorReportsTrackedCodemapState. root:TestMainFreshCloneLeavesGitStatusClean— the exit test from hygiene: codemap leaves .codemap/ state in every repo it touches, unignored and sometimes committed; agent worktrees pollute scans #184: fresh clone,codemap .,git status --porcelainempty, no.gitignorecreated.go test ./...: only the 3 pre-existing root-permission failures remain (TestRunSetupCreatesConfigAndHooks,TestSelectReportsInaccessiblePrimarySetup,TestListProjectsPreservesPerChildScanErrors).gofmt -l .,go vet ./...,staticcheck ./...clean.Closes #131
Closes #150
Addresses the exit test of #184
🤖 Generated with Claude Code
https://claude.ai/code/session_0159tcanCgA3jxi2E7DdRVWJ
Generated by Claude Code