fix(mcp): don't warn "different git working tree" for submodules covered by the parent index (#1031, #1033) (#1039)

Indexing a super-repo now descends into its submodules and gitlinked clones, so
a query run from inside one resolves up to the parent's unified index — whose
graph DOES contain that nested repo's files. But the git-worktree-mismatch
warning still fired, telling the agent the results were from "a different working
tree" and to run `codegraph init -i` — which would split the submodule back into
its own index and undo the unified view. A false positive carrying harmful advice.

Distinguish a genuine borrowed worktree (the SAME repository on a different
branch — shares a git common dir with the index root) from a submodule/embedded
clone (a DIFFERENT repository — its own common dir), and suppress the warning
only for the latter. Add gitCommonDir() for the check. The issue-#155 linked-worktree
case is unchanged.

Verified end-to-end: the warning no longer fires for a submodule-rooted MCP
session and still fires for a real linked worktree. Edit-sync (manual sync +
the live watcher) keeps the nested repo's files current on both macOS and Linux
(active-submodule and bare-gitlink shapes), so suppressing the warning is safe.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-28 19:35:17 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent a4dfc3438f
commit f73227d2f5
3 changed files with 137 additions and 0 deletions
+38
View File
@@ -43,6 +43,30 @@ export function gitWorktreeRoot(dir: string): string | null {
}
}
/**
* Absolute, symlink-resolved git **common** directory for `dir` — the shared
* `.git` that all worktrees of one repository point at. Linked worktrees of the
* same repo report the SAME common dir; a submodule or an embedded clone is a
* DIFFERENT repository and reports its own (`…/.git/modules/<name>` or its own
* `.git`). That distinction is what separates a genuine "borrowed worktree"
* from a nested repo the parent index already covers. Null when not a repo.
*/
export function gitCommonDir(dir: string): string | null {
try {
const out = execFileSync('git', ['rev-parse', '--git-common-dir'], {
cwd: dir,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
windowsHide: true,
}).trim();
if (!out) return null;
// `--git-common-dir` is relative to cwd unless already absolute.
return realpath(path.isAbsolute(out) ? out : path.resolve(dir, out));
} catch {
return null;
}
}
export interface WorktreeIndexMismatch {
/** The git working tree the command was run from. */
worktreeRoot: string;
@@ -76,6 +100,20 @@ export function detectWorktreeIndexMismatch(
// plain ancestor directory", and avoids warning outside git entirely.
if (gitWorktreeRoot(resolvedIndexRoot) !== resolvedIndexRoot) return null;
// Don't flag a nested repo (submodule / embedded clone) that `indexRoot`'s
// index ALREADY covers: indexing a super-repo descends into its submodules
// and gitlinked clones, so a query run from inside one resolves up to the
// parent index — whose graph *does* contain that nested repo's files. The
// warning's premise ("results are a different branch; symbols changed only
// here are missing") is false there, and its "run codegraph init -i" advice
// would needlessly fragment the unified workspace index. A genuine borrowed
// worktree and the index root are the SAME repository (they share a git
// common dir); a submodule/embedded clone is a DIFFERENT repository and does
// not — so suppress only when the two clearly differ. (#1031, #1033)
const worktreeCommon = gitCommonDir(worktreeRoot);
const indexCommon = gitCommonDir(resolvedIndexRoot);
if (worktreeCommon && indexCommon && worktreeCommon !== indexCommon) return null;
return { worktreeRoot, indexRoot: resolvedIndexRoot };
}