Summary
On systems with older git (verified failing on git 2.25.1 / Ubuntu 20.04; working on git 2.34.1+ / Ubuntu 22.04/24.04/26.04), codegraph index silently drops files that should be pulled in via codegraph.json 's includeIgnored . The user gets no error — just a smaller index. Same project, same codegraph.json , same codegraph version (1.5.0) indexes correctly on newer git.
Environment
- codegraph: @colbymchenry/codegraph 1.5.0
- OS: Ubuntu 20.04 LTS
- git: 2.25.1 (fails) — works on 2.34.1+ (Ubuntu 22.04/24.04/26.04)
- Node: 20.x
- engines in package.json only constrains Node ( >=20.0.0 <25.0.0 ); no minimum git version is declared.
Reproduction
Minimal project ( t1/ ):
On Ubuntu 20.04 / git 2.25.1:
On Ubuntu 22.04 / git 2.34.1 (same repo, same codegraph 1.5.0):
Root cause
Both call sites run git ls-files -z -s --recurse-submodules . On older git, --recurse-submodules only supports -c/--cached , not -s/--stage :
Call site 1 — collectGitFiles() (the indexing path, fatal )
No try/catch here → execFileSync throws (exit 128) → exception escapes → getGitVisibleFiles() catches it and return null → scanDirectory() falls back to scanDirectoryWalk() (the non-git filesystem walk). scanDirectoryWalk() only parses .gitignore and does not implement includeIgnored — that opt-in lives exclusively in the git path ( findIgnoredEmbeddedRepos() , called from collectGitFiles() ), which never runs. dir_b is gitignored → skipped → only a.c is indexed.
Net effect: on older git the entire includeIgnored mechanism is silently disabled, and because the failure is swallowed into a "non-git project" fallback, the user gets no diagnostic — just missing files.
Call site 2 — discoverEmbeddedRepoRoots() (the watcher scope, non-fatal but inconsistent )
This one is already wrapped in try/catch, so on older git the gitlink discovery pass is silently skipped (unexpanded gitlinks → not found). Non-fatal, but it means the watcher scope diverges from the indexer scope on older git: the indexer (once site 1 is fixed) recurses into gitlinks via collectGitFiles() , while the watcher's discoverEmbeddedRepoRoots() misses them.
Impact
Any project relying on includeIgnored to index embedded/gitignored repos is silently mis-indexed on older git. The super-repo-of-clones layout (the very use case includeIgnored exists for, per issues #622/#699/#514) is affected — and in particular embedded repos tracked as gitlinks (mode 160000, without .gitmodules ) are missed by the watcher on old git even after site 1 is fixed. No warning or error is surfaced in either case.
Suggested fix
Wrap both call sites in a try/catch and fall back to git ls-files -z -s (drop --recurse-submodules ) when the combined form is unsupported.
Site 1 — collectGitFiles() :
Site 2 — discoverEmbeddedRepoRoots() :
Why the -s fallback is safe
- -s keeps the \t format, so the existing 160000 gitlink detection still works.
- Without --recurse-submodules , active submodules surface as 160000 gitlinks and are recursed into by the existing gitlink pass at the end of collectGitFiles() ( classifyGitDir(...) === 'embedded' covers submodule checkouts). So active submodules are still indexed.
- Embedded independent repos (the dir_b case — untracked, not a gitlink) are still discovered by the untracked trailing-slash pass and findIgnoredEmbeddedRepos() , so includeIgnored works again. Verification
Applied to the packaged lib/dist/extraction/index.js on Ubuntu 20.04 / git 2.25.1:
$ codegraph index .
◆ Indexed 2 files # a.c + dir_b/b.c ✓
$ codegraph files
├── dir_b
│ └── b.c (c, 2 symbols)
└── a.c (c, 2 symbols)
Behavior on newer git is unchanged (the try branch still runs the original command).
Alternatives considered
- Declare a minimum git version (e.g. ≥2.34) and error out clearly on older git. This avoids the silent-misindex UX but excludes Ubuntu 20.04 LTS users entirely; the graceful fallback above is strictly better for compatibility.
- Fall back to git ls-files -z -c --recurse-submodules (which old git supports). Rejected: -c omits mode bits, breaking 160000 gitlink detection, so the gitlink pass would stop finding unexpanded submodules. The -s fallback keeps mode bits.
Summary
On systems with older git (verified failing on git 2.25.1 / Ubuntu 20.04; working on git 2.34.1+ / Ubuntu 22.04/24.04/26.04), codegraph index silently drops files that should be pulled in via codegraph.json 's includeIgnored . The user gets no error — just a smaller index. Same project, same codegraph.json , same codegraph version (1.5.0) indexes correctly on newer git.
Environment
Reproduction
Minimal project ( t1/ ):
On Ubuntu 20.04 / git 2.25.1:
On Ubuntu 22.04 / git 2.34.1 (same repo, same codegraph 1.5.0):
Root cause
Both call sites run git ls-files -z -s --recurse-submodules . On older git, --recurse-submodules only supports -c/--cached , not -s/--stage :
Call site 1 — collectGitFiles() (the indexing path, fatal )
No try/catch here → execFileSync throws (exit 128) → exception escapes → getGitVisibleFiles() catches it and return null → scanDirectory() falls back to scanDirectoryWalk() (the non-git filesystem walk). scanDirectoryWalk() only parses .gitignore and does not implement includeIgnored — that opt-in lives exclusively in the git path ( findIgnoredEmbeddedRepos() , called from collectGitFiles() ), which never runs. dir_b is gitignored → skipped → only a.c is indexed.
Net effect: on older git the entire includeIgnored mechanism is silently disabled, and because the failure is swallowed into a "non-git project" fallback, the user gets no diagnostic — just missing files.
Call site 2 — discoverEmbeddedRepoRoots() (the watcher scope, non-fatal but inconsistent )
This one is already wrapped in try/catch, so on older git the gitlink discovery pass is silently skipped (unexpanded gitlinks → not found). Non-fatal, but it means the watcher scope diverges from the indexer scope on older git: the indexer (once site 1 is fixed) recurses into gitlinks via collectGitFiles() , while the watcher's discoverEmbeddedRepoRoots() misses them.
Impact
Any project relying on includeIgnored to index embedded/gitignored repos is silently mis-indexed on older git. The super-repo-of-clones layout (the very use case includeIgnored exists for, per issues #622/#699/#514) is affected — and in particular embedded repos tracked as gitlinks (mode 160000, without .gitmodules ) are missed by the watcher on old git even after site 1 is fixed. No warning or error is surfaced in either case.
Suggested fix
Wrap both call sites in a try/catch and fall back to git ls-files -z -s (drop --recurse-submodules ) when the combined form is unsupported.
Site 1 — collectGitFiles() :
Site 2 — discoverEmbeddedRepoRoots() :
Why the -s fallback is safe
Applied to the packaged lib/dist/extraction/index.js on Ubuntu 20.04 / git 2.25.1:
Behavior on newer git is unchanged (the try branch still runs the original command).
Alternatives considered