A workspace holding several independent git repos failed two ways: - Enumeration: a super-repo whose .gitignore hides its child repos (/packages/) indexed 0 of their files — git never lists ignored dirs, and the #193 embedded-repo recursion only fired for UNTRACKED dirs. Gitignored embedded repos are now discovered (ignored-dirs listing + bounded .git search) and enumerated by their own git ls-files. - Change detection: git status in the parent says nothing about embedded repos (untracked OR ignored), so codegraph sync missed every child change. Status now recurses per embedded repo. ScopeIgnore is the new single source of truth for indexer + watcher scope: parent rules for ordinary paths, the child repo's own rules for paths inside it, built-in defaults uniformly on full paths (a git repo inside node_modules is an npm git-dependency, not project code), and ancestors of embedded roots are never pruned (the Linux per-directory watcher must descend to reach them). Non-git workspace roots already worked via the per-directory gitignore walk — locked in by test. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
13b2575dfe
commit
ed117ef90b
+9
-8
@@ -24,7 +24,7 @@
|
||||
* per-file watches are never needed.
|
||||
*
|
||||
* Excluded trees (node_modules/, dist/, .git/, …) are filtered via the
|
||||
* indexer's `buildDefaultIgnore` (built-in default-ignore dirs + the project's
|
||||
* indexer's `buildScopeIgnore` (built-in default-ignore dirs + the project's
|
||||
* .gitignore) — on Linux they're never descended into (so they cost no watch),
|
||||
* and on macOS/Windows the single recursive stream still covers them but their
|
||||
* events are dropped before any sync is scheduled. Either way the watcher's
|
||||
@@ -33,8 +33,7 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import type { Ignore } from 'ignore';
|
||||
import { isSourceFile, buildDefaultIgnore } from '../extraction';
|
||||
import { isSourceFile, buildScopeIgnore, type ScopeIgnore } from '../extraction';
|
||||
import { logDebug, logWarn } from '../errors';
|
||||
import { normalizePath } from '../utils';
|
||||
import { isCodeGraphDataDir } from '../directory';
|
||||
@@ -200,10 +199,12 @@ export class FileWatcher {
|
||||
* deterministically gate on watcher readiness.
|
||||
*/
|
||||
private readyWaiters: Array<() => void> = [];
|
||||
// The shared ignore matcher (built-in defaults + project .gitignore), built
|
||||
// once at start(). Same source of truth the indexer uses, so watcher scope
|
||||
// can never diverge from index scope.
|
||||
private ignoreMatcher: Ignore | null = null;
|
||||
// The shared scope matcher (built-in defaults + project .gitignore, with
|
||||
// embedded child repos matched by their OWN rules — #514), built once at
|
||||
// start(). Same source of truth the indexer uses, so watcher scope can
|
||||
// never diverge from index scope. An embedded repo created after start()
|
||||
// joins the scope on the next watcher restart / re-index.
|
||||
private ignoreMatcher: ScopeIgnore | null = null;
|
||||
|
||||
private readonly projectRoot: string;
|
||||
private readonly debounceMs: number;
|
||||
@@ -244,7 +245,7 @@ export class FileWatcher {
|
||||
}
|
||||
|
||||
// Reuse the indexer's ignore set so the watcher and indexer agree on scope.
|
||||
this.ignoreMatcher = buildDefaultIgnore(this.projectRoot);
|
||||
this.ignoreMatcher = buildScopeIgnore(this.projectRoot);
|
||||
|
||||
try {
|
||||
if (this.inertForTests) {
|
||||
|
||||
Reference in New Issue
Block a user