MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / collectGitFiles

Function collectGitFiles

src/extraction/index.ts:913–1010  ·  view source on GitHub ↗

* Collect git-visible files (tracked + untracked, .gitignore-respected) from the * git repository rooted at `repoDir`, adding each to `files` with `prefix` * prepended so paths stay relative to the original scan root. * * Recurses into embedded git repositories — nested repos that are NOT submod

(repoDir: string, prefix: string, files: Set<string>, embeddedRoots?: Set<string>, includeIgnored: Ignore | null = null)

Source from the content-addressed store, hash-verified

911 * parent's own gitignore rules.
912 */
913function collectGitFiles(repoDir: string, prefix: string, files: Set<string>, embeddedRoots?: Set<string>, includeIgnored: Ignore | null = null): void {
914 const gitOpts = { cwd: repoDir, encoding: 'utf-8' as const, timeout: 30000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'] as ['pipe', 'pipe', 'pipe'], windowsHide: true };
915
916 // Tracked files. --recurse-submodules pulls in files from active submodules,
917 // which the index would otherwise represent only as a commit pointer.
918 // Without this, monorepos using submodules index 0 files. (See issue #147.)
919 // Note: --recurse-submodules only supports -c/--cached and --stage modes — it
920 // can't be combined with -o, so untracked files are gathered separately below.
921 //
922 // We use --stage (-s) rather than -c so each entry carries its file mode. That
923 // lets us spot gitlink entries (mode 160000) that --recurse-submodules did NOT
924 // expand: a nested repo `git add`ed without a `.gitmodules` entry, or a
925 // submodule that isn't active/initialized in this checkout. Such a gitlink
926 // falls through every pass — it's tracked, so the untracked `-o` listing below
927 // never reports it, and --recurse-submodules only expands ACTIVE submodules —
928 // so its source would be silently skipped, leaving only the super-repo's own
929 // files indexed. We collect those gitlinks here and recurse into them below.
930 // (An active submodule is expanded inline by --recurse-submodules and so never
931 // surfaces as a 160000 entry — only the unhandled gitlinks do.) (#1031, #1033)
932 //
933 // -z gives NUL-separated, unquoted output so non-ASCII (e.g. CJK) paths
934 // survive verbatim. Without it git octal-escapes and double-quotes such paths
935 // (the core.quotepath default), and the quoted form never matches a real file
936 // on disk → those files are silently dropped from the index. (#541) With -s the
937 // path follows a TAB after the `<mode> <object> <stage>` prefix.
938 const gitlinkRels: string[] = [];
939 const tracked = execFileSync('git', ['ls-files', '-z', '-s', '--recurse-submodules'], gitOpts);
940 for (const entry of tracked.split('\0')) {
941 if (!entry) continue;
942 const tab = entry.indexOf('\t');
943 if (tab === -1) continue; // --stage always emits "<mode> <object> <stage>\t<path>"
944 const rel = entry.slice(tab + 1);
945 if (entry.slice(0, 6) === '160000') {
946 gitlinkRels.push(rel); // an unexpanded gitlink — recursed into below, not a source file itself
947 continue;
948 }
949 files.add(normalizePath(prefix + rel));
950 }
951
952 // Untracked files (submodules manage their own untracked state). Embedded git
953 // repos surface here as a single "subdir/" entry that git refuses to descend
954 // into — recurse into those as their own repos so their source gets indexed.
955 const untracked = execFileSync('git', ['ls-files', '-z', '-o', '--exclude-standard'], gitOpts);
956 for (const rel of untracked.split('\0')) {
957 if (!rel) continue;
958 if (rel.endsWith('/')) {
959 // git only emits a trailing-slash directory entry for an embedded repo.
960 // Guard with a .git check anyway, and skip anything else exactly as git
961 // itself skips it (we never descend into a non-repo opaque dir). Never
962 // descend into default-ignored locations — an embedded repo inside
963 // node_modules is an npm git-dependency, not project code.
964 const childDir = path.join(repoDir, rel);
965 // A git worktree surfaces here as an opaque untracked dir too — skip it,
966 // it's a duplicate working view of an already-indexed repo (#848).
967 if (classifyGitDir(childDir) === 'embedded' && !defaultsOnlyIgnore().ignores(rel)) {
968 embeddedRoots?.add(normalizePath(prefix + rel));
969 collectGitFiles(childDir, prefix + rel, files, embeddedRoots, includeIgnored);
970 }

Callers 1

getGitVisibleFilesFunction · 0.85

Calls 8

normalizePathFunction · 0.90
classifyGitDirFunction · 0.85
defaultsOnlyIgnoreFunction · 0.85
buildDefaultIgnoreFunction · 0.85
findIgnoredEmbeddedReposFunction · 0.85
ignoresMethod · 0.80
joinMethod · 0.45

Tested by

no test coverage detected