Skip to content

fix(extraction): index nested repos recorded as gitlinks (#1031, #1033)#1038

Merged
colbymchenry merged 1 commit into
mainfrom
fix/index-nested-gitlink-repos
Jun 28, 2026
Merged

fix(extraction): index nested repos recorded as gitlinks (#1031, #1033)#1038
colbymchenry merged 1 commit into
mainfrom
fix/index-nested-gitlink-repos

Conversation

@colbymchenry

Copy link
Copy Markdown
Owner

Problem

Running codegraph init at the root of a workspace built by stacking repos inside one another indexed only the outer repo's own files and stopped at the nested repo's boundary — a stacked-repo project came up nearly empty (#1031 saw ~10 files at the root; #1033 reported the same for submodule/gitlink modules).

Root cause

In collectGitFiles, a nested repo tracked as a gitlink (mode 160000) fell between the two file-collection passes:

  • it's tracked, so the untracked git ls-files -o pass never reports it, and
  • it's not an active submodule (a clone git added without a .gitmodules entry, or a submodule that isn't initialized in this checkout), so --recurse-submodules won't expand it.

So its source was silently skipped.

Fix

  • Switch the tracked scan from ls-files -cls-files -s --recurse-submodules to expose each entry's mode. Because active submodules are expanded inline by --recurse-submodules, any 160000 entry is by definition an unexpanded gitlink — a precise signal, no heuristics.
  • Recurse into each such gitlink that has a real .git checkout on disk (classifyGitDir(...) === 'embedded') and isn't under a default-ignored dir, indexing it as its own embedded repo.
  • Mirror the same pass in discoverEmbeddedRepoRoots so the file-watcher's scope stays equal to the indexer's (the "must not diverge" invariant).

Unchanged: active submodules (#147), plain untracked nested clones (#193). Excluded as before: gitlinks under vendor/, node_modules/, etc. (#407). A submodule with nothing checked out on disk is correctly left alone.

Tests — four-shape coverage (extraction.test.ts)

bare gitlink (recursively) · gitlink alongside an active submodule · gitlink under a default-ignored dir (excluded) · uninitialized submodule (left alone).

Validation

  • macOS — full suite (1813 passed) + live codegraph init on real A/B/C/D fixtures (scenario B: 1 → 3 files).
  • Linux (Docker, node:22-bookworm)npm ci + build + the gitlink/submodule/nested tests (10 passed) + live binary four-shape check.
  • Windows (real VM)npm ci + build + the same tests (10 passed).

Closes #1031.

Partially addresses #1033 — this fixes the gitlink/submodule indexing facet. The two Windows-specific facets remain open: junction/symlink-dir traversal in the non-git filesystem walk, and the shared-.codegraph-via-junction index degradation.

🤖 Generated with Claude Code

A nested git repo tracked as a gitlink (mode 160000) — a clone `git add`ed
into the super-repo without a `.gitmodules` entry, or a submodule that
isn't active/initialized in this checkout — fell through both file-collection
passes: it's tracked, so the untracked `-o` listing skips it, but it's not
an active submodule, so `--recurse-submodules` won't expand it. Indexing the
top level therefore pulled in only the outer repo's own files and stopped at
the nested repo's boundary (one report: ~10 files at the root).

Switch the tracked scan to `ls-files -s` to expose file modes, collect the
unexpanded 160000 entries, and recurse into each that has a real working tree
on disk as its own embedded repo. Mirror the same discovery in
discoverEmbeddedRepoRoots so the watcher's scope stays equal to the indexer's.

Active submodules (#147) and untracked nested clones (#193) are unchanged;
gitlinks under default-ignored dirs (vendor/, node_modules/) stay excluded
(#407); an uninitialized submodule with no checkout on disk is left alone.
Adds four-shape coverage in extraction.test.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.

on a tree combined from several repo, init from the root does not recursively scan the lower level repos

1 participant