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

Method watchTree

src/sync/watcher.ts:472–536  ·  view source on GitHub ↗

* Add an inotify watch for `dir` and recurse into its non-ignored * subdirectories. When `markExisting` is true (a directory that appeared * AFTER startup), the source files already inside it are recorded as pending * — this closes the `mkdir + write` race where files created before the new

(dir: string, markExisting: boolean)

Source from the content-addressed store, hash-verified

470 * sync owns the baseline).
471 */
472 private watchTree(dir: string, markExisting: boolean): void {
473 // A degrade() mid-walk (exhaustion on an earlier directory) calls stop(),
474 // which sets `stopped`; bail so the recursion unwinds without adding more
475 // watches to a watcher that is shutting down. `inotifyLimitWarned` does the
476 // same after ENOSPC — the kernel budget is gone, so stop trying the rest of
477 // the tree (every add would fail) while keeping the watches already set.
478 if (this.stopped || this.degradedReason || this.inotifyLimitWarned) return;
479 if (this.dirWatchers.has(dir)) return;
480 if (this.dirWatchers.size >= maxDirWatches()) {
481 if (!this.dirCapWarned) {
482 this.dirCapWarned = true;
483 logWarn('File watcher hit directory-watch cap; remaining subtrees rely on manual/periodic sync', {
484 cap: maxDirWatches(),
485 });
486 }
487 return;
488 }
489
490 let w: fs.FSWatcher;
491 try {
492 w = watchImpl(dir, { persistent: true }, (_event, filename) =>
493 this.handleDirEvent(dir, filename)
494 );
495 } catch (err) {
496 // EMFILE/ENFILE means the PROCESS is out of descriptors — every further
497 // directory would fail too, so degrade the whole watcher rather than
498 // limping along with a partial watch set.
499 if (isWatchResourceExhaustion(err)) {
500 this.degrade(EXHAUSTION_REASON, { error: String(err), dir });
501 } else if (isInotifyWatchExhaustion(err)) {
502 // ENOSPC = inotify watch budget exhausted. NON-fatal: keep the watches
503 // we have and tell the user the knob to raise (warn once).
504 this.warnInotifyLimit({ error: String(err), dir });
505 }
506 // ENOENT / EACCES on a single directory stays non-fatal: skip it quietly.
507 return;
508 }
509 w.on('error', (err: unknown) => {
510 if (isWatchResourceExhaustion(err)) {
511 this.degrade(EXHAUSTION_REASON, { error: String(err), dir });
512 return;
513 }
514 if (isInotifyWatchExhaustion(err)) {
515 this.warnInotifyLimit({ error: String(err), dir });
516 }
517 this.unwatchDir(dir);
518 });
519 this.dirWatchers.set(dir, w);
520
521 let entries: fs.Dirent[];
522 try {
523 entries = fs.readdirSync(dir, { withFileTypes: true });
524 } catch {
525 return;
526 }
527 for (const entry of entries) {
528 const child = path.join(dir, entry.name);
529 if (entry.isDirectory()) {

Callers 2

startPerDirectoryMethod · 0.95
handleDirEventMethod · 0.95

Calls 15

handleDirEventMethod · 0.95
degradeMethod · 0.95
warnInotifyLimitMethod · 0.95
unwatchDirMethod · 0.95
shouldIgnoreDirMethod · 0.95
handleChangeMethod · 0.95
logWarnFunction · 0.90
normalizePathFunction · 0.90
maxDirWatchesFunction · 0.85
isInotifyWatchExhaustionFunction · 0.85
hasMethod · 0.80

Tested by

no test coverage detected