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

Function collectGitStatus

src/extraction/index.ts:1105–1178  ·  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

1103}
1104
1105function collectGitStatus(repoDir: string, prefix: string, out: GitChanges, overrides?: Record<string, Language>, includeIgnored: Ignore | null = null, exclude: Ignore | null = null): void {
1106 const output = execFileSync(
1107 'git',
1108 ['status', '--porcelain', '--no-renames'],
1109 { cwd: repoDir, encoding: 'utf-8', timeout: 10000, maxBuffer: 50 * 1024 * 1024, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true }
1110 );
1111
1112 // This repo's own ignore rules — built-in defaults (#407) plus its .gitignore.
1113 // Change detection must exclude the SAME files the full index does, but git
1114 // status hides neither: it ignores nothing for *tracked* paths, and the
1115 // built-in defaults aren't gitignore at all. Without this filter a committed
1116 // vendor/ dir, or a tracked file under a .gitignored dir, surfaces here as a
1117 // change — so `codegraph status` (which reads getChangedFiles) reports a
1118 // pending edit the full index never tracks and `sync` never clears. Matching
1119 // repo-relative `rel` at each recursion level mirrors getGitVisibleFiles'
1120 // ScopeIgnore: every embedded repo is judged by ITS OWN rules, never the
1121 // parent's. (#766)
1122 const ig = buildDefaultIgnore(repoDir);
1123
1124 const untrackedDirs: string[] = [];
1125 for (const line of output.split('\n')) {
1126 if (line.length < 4) continue; // Minimum: "XY file"
1127
1128 const statusCode = line.substring(0, 2);
1129 const rel = normalizePath(line.substring(3));
1130
1131 // Untracked directory entries (trailing slash) may hide an embedded repo —
1132 // collect for the recursion below instead of treating as a file.
1133 if (statusCode === '??' && rel.endsWith('/')) {
1134 untrackedDirs.push(rel);
1135 continue;
1136 }
1137
1138 const filePath = normalizePath(prefix + rel);
1139 if (!isSourceFile(filePath, overrides)) continue;
1140
1141 if (statusCode.includes('D')) {
1142 // Deletions stay unfiltered: getChangedFiles acts on one only when the
1143 // path is already tracked in the DB, where removal is always correct — and
1144 // that lets a newly-excluded dir's stale rows clean themselves up. (#766)
1145 out.deleted.push(filePath);
1146 continue;
1147 }
1148
1149 // Added (`??`) / modified files inside an excluded dir must not enter the
1150 // index — match against the repo-relative path, same as the full scan. (#766)
1151 if (ig.ignores(rel)) continue;
1152 // User `codegraph.json` `exclude` (#999) is project-root-relative, so it's
1153 // matched against the full path — sync must not re-add a tracked file the
1154 // full index now keeps out. Deletions above stay unfiltered so a file that
1155 // WAS indexed before an exclude was added still cleans itself out.
1156 if (exclude && exclude.ignores(filePath)) continue;
1157
1158 if (statusCode === '??') {
1159 out.added.push(filePath);
1160 } else {
1161 // M, MM, AM, A (staged), etc. — treat as modified
1162 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.45

Tested by

no test coverage detected