MCPcopy
hub / github.com/colbymchenry/codegraph / collectGitStatus

Function collectGitStatus

src/extraction/index.ts:846–919  ·  view source on GitHub ↗
(repoDir: string, prefix: string, out: GitChanges, overrides?: Record<string, Language>, includeIgnored: Ignore | null = null, exclude: Ignore | null = null)

Source from the content-addressed store, hash-verified

844}
845
846function collectGitStatus(repoDir: string, prefix: string, out: GitChanges, overrides?: Record<string, Language>, includeIgnored: Ignore | null = null, exclude: Ignore | null = null): void {
847 const output = execFileSync(
848 'git',
849 ['status', '--porcelain', '--no-renames'],
850 { cwd: repoDir, encoding: 'utf-8', timeout: 10000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }
851 );
852
853 // This repo's own ignore rules — built-in defaults (#407) plus its .gitignore.
854 // Change detection must exclude the SAME files the full index does, but git
855 // status hides neither: it ignores nothing for *tracked* paths, and the
856 // built-in defaults aren't gitignore at all. Without this filter a committed
857 // vendor/ dir, or a tracked file under a .gitignored dir, surfaces here as a
858 // change — so `codegraph status` (which reads getChangedFiles) reports a
859 // pending edit the full index never tracks and `sync` never clears. Matching
860 // repo-relative `rel` at each recursion level mirrors getGitVisibleFiles'
861 // ScopeIgnore: every embedded repo is judged by ITS OWN rules, never the
862 // parent's. (#766)
863 const ig = buildDefaultIgnore(repoDir);
864
865 const untrackedDirs: string[] = [];
866 for (const line of output.split('\n')) {
867 if (line.length < 4) continue; // Minimum: "XY file"
868
869 const statusCode = line.substring(0, 2);
870 const rel = normalizePath(line.substring(3));
871
872 // Untracked directory entries (trailing slash) may hide an embedded repo —
873 // collect for the recursion below instead of treating as a file.
874 if (statusCode === '??' && rel.endsWith('/')) {
875 untrackedDirs.push(rel);
876 continue;
877 }
878
879 const filePath = normalizePath(prefix + rel);
880 if (!isSourceFile(filePath, overrides)) continue;
881
882 if (statusCode.includes('D')) {
883 // Deletions stay unfiltered: getChangedFiles acts on one only when the
884 // path is already tracked in the DB, where removal is always correct — and
885 // that lets a newly-excluded dir's stale rows clean themselves up. (#766)
886 out.deleted.push(filePath);
887 continue;
888 }
889
890 // Added (`??`) / modified files inside an excluded dir must not enter the
891 // index — match against the repo-relative path, same as the full scan. (#766)
892 if (ig.ignores(rel)) continue;
893 // User `codegraph.json` `exclude` (#999) is project-root-relative, so it's
894 // matched against the full path — sync must not re-add a tracked file the
895 // full index now keeps out. Deletions above stay unfiltered so a file that
896 // WAS indexed before an exclude was added still cleans itself out.
897 if (exclude && exclude.ignores(filePath)) continue;
898
899 if (statusCode === '??') {
900 out.added.push(filePath);
901 } else {
902 // M, MM, AM, A (staged), etc. — treat as modified
903 out.modified.push(filePath);

Callers 1

getGitChangedFilesFunction · 0.85

Calls 7

normalizePathFunction · 0.90
isSourceFileFunction · 0.90
buildDefaultIgnoreFunction · 0.85
findNestedGitReposFunction · 0.85
findIgnoredEmbeddedReposFunction · 0.85
ignoresMethod · 0.80
joinMethod · 0.80

Tested by

no test coverage detected