@gitkraken/core-gitlens 0.16.4 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -1
- package/dist/git/cache.d.ts +8 -2
- package/dist/git/cache.js +15 -6
- package/dist/git/cache.js.map +1 -1
- package/dist/git/models/issue.d.ts +20 -1
- package/dist/git/models/issue.js +3 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/models/repository.d.ts +4 -2
- package/dist/git/models/repository.js +1 -0
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/models/worktree.d.ts +5 -1
- package/dist/git/models/worktree.js +5 -3
- package/dist/git/models/worktree.js.map +1 -1
- package/dist/git/providers/provider.d.ts +17 -0
- package/dist/git/providers/worktrees.d.ts +17 -2
- package/dist/git/run.types.d.ts +7 -0
- package/dist/git/service.d.ts +5 -2
- package/dist/git/service.js +11 -5
- package/dist/git/service.js.map +1 -1
- package/dist/git/utils/issue.utils.js +5 -1
- package/dist/git/utils/issue.utils.js.map +1 -1
- package/dist/git/watching/classifyChange.d.ts +4 -3
- package/dist/git/watching/classifyChange.js +7 -4
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/provider.d.ts +6 -0
- package/dist/git/watching/watchGroup.d.ts +142 -3
- package/dist/git/watching/watchGroup.js +424 -19
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +25 -0
- package/dist/git/watching/watchService.js +27 -7
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +13 -8
- package/dist/git/watching/watchSession.js +50 -10
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +14 -0
- package/dist/git/watching/watcherPatterns.js +38 -1
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +64 -2
- package/dist/git/watching/worktreeSummary.js +326 -13
- package/dist/git/watching/worktreeSummary.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +26 -0
- package/dist/git-cli/cliGitProvider.js +66 -5
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/commandClassifier.d.ts +9 -3
- package/dist/git-cli/exec/commandClassifier.js +98 -17
- package/dist/git-cli/exec/commandClassifier.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +20 -0
- package/dist/git-cli/exec/git.js +69 -7
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/headState.d.ts +5 -0
- package/dist/git-cli/exec/headState.js +14 -1
- package/dist/git-cli/exec/headState.js.map +1 -1
- package/dist/git-cli/parsers/worktreeParser.js +2 -2
- package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
- package/dist/git-cli/providers/branches.js +8 -4
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/graph.js +1 -1
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +3 -0
- package/dist/git-cli/providers/operations.js +14 -5
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts +25 -3
- package/dist/git-cli/providers/worktrees.js +125 -32
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts +2 -0
- package/dist/git-cli/service.js +4 -2
- package/dist/git-cli/service.js.map +1 -1
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +13 -0
- package/dist/plus/integrations/models/gitHostIntegration.js +20 -3
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.d.ts +12 -0
- package/dist/plus/integrations/models/integration.js +7 -1
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.d.ts +13 -0
- package/dist/plus/integrations/providers/azure/azure.js +11 -3
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +3 -1
- package/dist/plus/integrations/providers/azure/models.js +11 -5
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +33 -5
- package/dist/plus/integrations/providers/azureDevOps.js +75 -11
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -0
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +19 -0
- package/dist/plus/integrations/providers/models.js +12 -3
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/reads/etag.js +6 -1
- package/dist/plus/integrations/reads/etag.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchGroup.js","names":[],"sources":["../../../../git/src/watching/watchGroup.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { isDescendant, joinPaths, relative } from '@gitlens/utils/path.js';\nimport type { GitDir, RepositoryChange } from '../models/repository.js';\nimport { allWatchedRepositoryChanges, classifyGitDirChange } from './classifyChange.js';\nimport type { GitConfigSections, GitConfigSectionsChange } from './configSections.js';\nimport { diffGitConfigSections, parseGitConfigBytes } from './configSections.js';\nimport type { GitDirPoller, GitDirProbe } from './gitDirPoller.js';\nimport { GitDirProbeTarget } from './gitDirPoller.js';\nimport type {\n\tFileWatcher,\n\tFileWatcherOptions,\n\tFileWatcherReliability,\n\tFileWatchEvent,\n\tFileWatchingProvider,\n} from './provider.js';\nimport {\n\tdotGitGlobCombined,\n\tdotGitGlobCommon,\n\tdotGitGlobRoot,\n\tgetGitDirRefProbes,\n\tgitDirCommonProbes,\n\tgitDirExcludes,\n\tgitDirProbesByEmittedPath,\n\tgitDirRootProbes,\n\tshouldIgnoreRepoPath,\n} from './watcherPatterns.js';\n\n/**\n * How long a read of the shared `.git/config` may take before it counts as failed (unconfined). Reads run one at a\n * time, so one that never settles, as on a hung network or WSL mount, would otherwise hold back every later report.\n */\nconst configReadTimeoutMs = 5000;\n\n/**\n * Hooks that the WatchGroup fires when events are interpreted.\n * These are raw, un-debounced events — the session handles debouncing.\n */\nexport interface WatchGroupHooks {\n\t/**\n\t * `ref` is the full ref name (or ref directory) or pseudo-ref (`HEAD`, `ORIG_HEAD`) whose file changed, with a\n\t * trailing `.lock` stripped; `false` when config changed without moving refs, and `undefined` when ref movement\n\t * is unknown. `overflow` marks the every-kind rescan of a watcher that dropped events, which no single write can\n\t * account for. `configBranches`, with a `config` change, are the local branches (full ref names) whose\n\t * `branch.<name>` sections are all that changed in the shared `.git/config`; without it, the config change may be\n\t * anywhere.\n\t */\n\treadonly onRepoChanged: (\n\t\trepoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\tref?: string | false,\n\t\toverflow?: boolean,\n\t\tconfigBranches?: ReadonlySet<string>,\n\t) => void;\n\treadonly onFetchHeadChanged?: (repoPath: string) => void;\n\treadonly onIgnoresChanged?: (repoPath: string) => void;\n\t/** The refs whose loose files a poll stats for this session, read at each poll */\n\treadonly getProbeRefs?: () => Iterable<string>;\n\t/** A watcher this session hears through went `unwatchable`, or stopped being it (see {@link WatchGroup.isUnwatchable}) */\n\treadonly onUnwatchableChanged?: (repoPath: string) => void;\n}\n\n/**\n * Tracks one session's connection to a WatchGroup.\n * Each session has its own root watcher but shares the common watcher.\n */\ninterface SessionEntry {\n\treadonly repoPath: string;\n\treadonly gitDirPath: string;\n\treadonly rootWatcher: FileWatcher;\n\t/** True when the root watcher is on the common dir itself, so it already sees every common file */\n\treadonly coversCommon: boolean;\n\t/** For a linked session, its `<n>` in `<common>/worktrees/<n>`; otherwise `undefined` */\n\treadonly worktreeId: string | undefined;\n\treadonly hooks: WatchGroupHooks;\n\treliability: FileWatcherReliability;\n\t/** Polls in place of (or beside) the root watcher while it isn't `reliable` */\n\ttarget?: GitDirProbeTarget;\n}\n\n/**\n * Manages filesystem watchers for a single physical `.git` directory,\n * shared across all worktrees that reference it.\n *\n * Structure:\n * - 1 common watcher: config, refs/**, info/exclude, etc. Delivers to linked sessions only; a\n * session rooted at the common dir hears the same files from its own combined root watcher.\n * - N root watchers: one per worktree (index, HEAD, *_HEAD, etc.)\n *\n * For a standard (non-worktree) repo, the common and root directories\n * are the same, but we still create separate watchers for clean separation.\n *\n * Given a `readFile`, the group keeps the shared `.git/config` as it last read it, section by section. A report of\n * that file (or its lock), from whichever watcher, is read and compared with it once, and every session hears what\n * changed: only some branches' `branch.<name>` sections (`config`, confined to them), anything else (`config` and\n * `remotes`, unconfined), or nothing at all, when an earlier report's read already delivered the change. Reads run\n * one at a time, so each compares with the one before it and none is skipped. The first read, taken as the group\n * starts, is the baseline and delivers nothing; a report arriving before it completes may describe a change it\n * already holds, so it is delivered unconfined. A read that fails or doesn't settle within a few seconds, or a file\n * git couldn't parse, is unconfined too.\n */\nexport class WatchGroup implements UnifiedDisposable {\n\tprivate readonly _sessions = new Map<string, SessionEntry>();\n\tprivate _commonWatcher: FileWatcher | undefined;\n\tprivate _commonReliability: FileWatcherReliability = 'reliable';\n\tprivate _commonTarget: GitDirProbeTarget | undefined;\n\tprivate _disposed = false;\n\n\t/** The shared `.git/config`, by section, as the last read that could parse it found it */\n\tprivate _config: GitConfigSections | undefined;\n\t/** The baseline read is in flight */\n\tprivate _configBaselining = false;\n\tprivate _configReading = false;\n\tprivate _configRereadQueued = false;\n\n\tconstructor(\n\t\treadonly commonGitDir: string,\n\t\tprivate readonly provider: FileWatchingProvider,\n\t\t/** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */\n\t\tprivate readonly poller?: GitDirPoller,\n\t\t/** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */\n\t\tprivate readonly readFile?: (path: string) => Promise<Uint8Array>,\n\t) {\n\t\tif (readFile != null) {\n\t\t\tvoid this.readConfigBaseline();\n\t\t}\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\t/** Dispose the entire group (common watcher + all root watchers). */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tentry.rootWatcher.dispose();\n\t\t\tthis.stopPolling(entry);\n\t\t}\n\t\tthis._sessions.clear();\n\n\t\tthis.disposeCommonWatcher();\n\t}\n\n\tget sessions(): ReadonlyMap<string, SessionEntry> {\n\t\treturn this._sessions;\n\t}\n\n\t/** True when a watcher the session hears through — its root watcher, or a linked session's common one — can't watch */\n\tisUnwatchable(repoPath: string): boolean {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return false;\n\n\t\treturn (\n\t\t\tentry.reliability === 'unwatchable' || (!entry.coversCommon && this._commonReliability === 'unwatchable')\n\t\t);\n\t}\n\n\t/** Add a session to this group. Creates the root watcher for the session's gitDir. */\n\taddSession(repoPath: string, gitDir: GitDir, hooks: WatchGroupHooks): void {\n\t\tif (this._disposed) return;\n\t\tif (this._sessions.has(repoPath)) return; // Already tracked\n\n\t\t// Determine the glob pattern for the root watcher\n\t\tconst isStandard = gitDir.commonUri == null;\n\t\tconst rootPattern = isStandard ? dotGitGlobCombined : dotGitGlobRoot;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns, so before the entry exists\n\t\tlet entry: SessionEntry | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\n\t\tconst rootWatcher = this.provider.createWatcher(\n\t\t\tgitDir.uri.fsPath,\n\t\t\trootPattern,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tconst e = this._sessions.get(repoPath);\n\t\t\t\tif (e != null) {\n\t\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\t\te.target?.forget(event.path);\n\t\t\t\t\t}\n\t\t\t\t\tthis.onRootEvent(event, e);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.getWatcherOptions(state => {\n\t\t\t\tif (entry == null) {\n\t\t\t\t\tinitialReliability = state;\n\t\t\t\t} else if (this._sessions.get(repoPath) === entry) {\n\t\t\t\t\tthis.setRootReliability(entry, state);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tconst entryGitDirPath = gitDir.uri.fsPath;\n\t\tentry = {\n\t\t\trepoPath: repoPath,\n\t\t\tgitDirPath: entryGitDirPath,\n\t\t\trootWatcher: rootWatcher,\n\t\t\tcoversCommon: isStandard,\n\t\t\tworktreeId: isStandard ? undefined : this.getWorktreeId(entryGitDirPath),\n\t\t\thooks: hooks,\n\t\t\treliability: 'reliable',\n\t\t};\n\n\t\tthis._sessions.set(repoPath, entry);\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setRootReliability(entry, initialReliability, true);\n\t\t}\n\n\t\t// Start the common watcher if this is a worktree repo and we haven't yet.\n\t\t// Standard repos don't need a separate common watcher because their root\n\t\t// watcher uses dotGitGlobCombined which already covers all patterns.\n\t\tif (!isStandard) {\n\t\t\tthis.ensureCommonWatcher();\n\t\t}\n\t}\n\n\t/** Remove a session from this group. Disposes its root watcher. */\n\tremoveSession(repoPath: string): void {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return;\n\n\t\tentry.rootWatcher.dispose();\n\t\tthis.stopPolling(entry);\n\t\tthis._sessions.delete(repoPath);\n\n\t\t// Once no linked session is left, dispose the common watcher — it delivers to linked sessions only\n\t\tif (this._commonWatcher != null && !some(this._sessions.values(), e => !e.coversCommon)) {\n\t\t\tthis.disposeCommonWatcher();\n\t\t}\n\t}\n\n\tprivate getWorktreeId(gitDirPath: string): string | undefined {\n\t\tif (!isDescendant(gitDirPath, this.commonGitDir)) return undefined;\n\n\t\tconst rel = relative(this.commonGitDir, gitDirPath);\n\t\tif (!rel.startsWith('worktrees/')) return undefined;\n\n\t\tconst id = rel.substring(10);\n\t\treturn id.length > 0 && !id.includes('/') ? id : undefined;\n\t}\n\n\tprivate ensureCommonWatcher(): void {\n\t\tif (this._commonWatcher != null || this._disposed) return;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns\n\t\tlet watcher: FileWatcher | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\t\tconst options = this.getWatcherOptions(state => {\n\t\t\tif (watcher == null) {\n\t\t\t\tinitialReliability = state;\n\t\t\t} else if (this._commonWatcher === watcher) {\n\t\t\t\tthis.setCommonReliability(state);\n\t\t\t}\n\t\t});\n\n\t\twatcher = this.provider.createWatcher(\n\t\t\tthis.commonGitDir,\n\t\t\tdotGitGlobCommon,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\tthis._commonTarget?.forget(event.path);\n\t\t\t\t}\n\t\t\t\tthis.onCommonEvent(event);\n\t\t\t},\n\t\t\toptions,\n\t\t);\n\t\tthis._commonWatcher = watcher;\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setCommonReliability(initialReliability, true);\n\t\t}\n\t}\n\n\tprivate disposeCommonWatcher(): void {\n\t\tthis._commonWatcher?.dispose();\n\t\tthis._commonWatcher = undefined;\n\t\tthis._commonReliability = 'reliable';\n\t\tif (this._commonTarget != null) {\n\t\t\tthis.poller?.delete(this._commonTarget);\n\t\t\tthis._commonTarget = undefined;\n\t\t}\n\t}\n\n\t/** Hosts that didn't opt into polling get exactly the options they always had */\n\tprivate getWatcherOptions(onDidChangeReliability: (state: FileWatcherReliability) => void): FileWatcherOptions {\n\t\treturn this.poller != null\n\t\t\t? { excludes: gitDirExcludes, onDidChangeReliability: onDidChangeReliability }\n\t\t\t: { excludes: gitDirExcludes };\n\t}\n\n\tprivate setRootReliability(entry: SessionEntry, state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = entry.reliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tentry.reliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tthis.stopPolling(entry);\n\t\t} else if (entry.target == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tentry.gitDirPath,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getRootProbes(entry),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\t(event, probe) => {\n\t\t\t\t\tif (entry.target !== target) return;\n\n\t\t\t\t\tthis.onRootEvent(event, entry);\n\t\t\t\t\tif (probe.path === 'logs/HEAD' && !entry.coversCommon) {\n\t\t\t\t\t\tthis.onLinkedHeadMoved();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tentry.target = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\t// The watch went down or came back: whatever changed meanwhile went unseen\n\t\tif (!initial) {\n\t\t\tthis.deliverOverflow(entry);\n\t\t}\n\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t}\n\n\tprivate setCommonReliability(state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = this._commonReliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tthis._commonReliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tif (this._commonTarget != null) {\n\t\t\t\tthis.poller.delete(this._commonTarget);\n\t\t\t\tthis._commonTarget = undefined;\n\t\t\t}\n\t\t} else if (this._commonTarget == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tthis.commonGitDir,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getCommonProbes(),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\tevent => {\n\t\t\t\t\tif (this._commonTarget === target) {\n\t\t\t\t\t\tthis.onCommonEvent(event);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._commonTarget = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (!initial) {\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\tprivate stopPolling(entry: SessionEntry): void {\n\t\tif (entry.target == null) return;\n\n\t\tthis.poller?.delete(entry.target);\n\t\tentry.target = undefined;\n\t}\n\n\t/**\n\t * A session rooted at the common dir polls the common files too, with the refs of every session in the group: its\n\t * combined watcher would hear them all. While the common dir is polled too, the poller stats each shared file once\n\t * for both, and each keeps its own readings, so what one watcher reported is held back from its own sessions only\n\t */\n\tprivate getRootProbes(entry: SessionEntry): readonly GitDirProbe[] {\n\t\tif (!entry.coversCommon) return gitDirRootProbes;\n\n\t\tconst refs = new Set<string>();\n\t\tfor (const e of this._sessions.values()) {\n\t\t\tfor (const ref of e.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirRootProbes, ...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\t/**\n\t * A linked worktree's `logs/HEAD` moved — a commit or reset there, on a branch a polled session rooted at the common\n\t * dir may not probe — so each such session hears a `heads` change its combined watcher would have heard\n\t */\n\tprivate onLinkedHeadMoved(): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (!entry.coversCommon || entry.target == null) continue;\n\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['heads']);\n\t\t}\n\t}\n\n\t/** The common files, with the refs of every linked session (the only ones the common watcher delivers to) */\n\tprivate getCommonProbes(): GitDirProbe[] {\n\t\tconst refs = new Set<string>();\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tfor (const ref of entry.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\tprivate onCommonEvent(event: FileWatchEvent): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tfor (const entry of this._sessions.values()) {\n\t\t\t\tif (entry.coversCommon) continue;\n\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Compute relative path from common git dir\n\t\tif (!isDescendant(event.path, this.commonGitDir)) return;\n\n\t\tconst relativePath = relative(this.commonGitDir, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\tif (this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst isFetchHead = relativePath === 'FETCH_HEAD';\n\t\tconst isExclude = relativePath === 'info/exclude';\n\t\tconst changes = isFetchHead ? undefined : classifyGitDirChange(relativePath);\n\t\tif (changes == null && !isFetchHead && !isExclude) return;\n\n\t\tconst ref = changes != null ? getChangedRef(relativePath) : undefined;\n\n\t\t// `worktrees/<n>/HEAD`: the owner hears its own HEAD from its root watcher. Its `locked` and `gitdir` aren't in\n\t\t// the root glob, so the owner hears those only from here\n\t\tlet ownerId: string | undefined;\n\t\tif (changes != null && relativePath.length > 10 && relativePath.startsWith('worktrees/')) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd !== -1 && relativePath.substring(idEnd + 1) === 'HEAD') {\n\t\t\t\townerId = relativePath.substring(10, idEnd);\n\t\t\t}\n\t\t}\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\t// A session rooted at the common dir sees every common file (hooks included) through its own\n\t\t\t// combined root watcher, so delivering here too would duplicate it\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (isFetchHead) {\n\t\t\t\t// FETCH_HEAD lives in the main .git dir; each worktree updates its last-fetched state\n\t\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (changes != null && (ownerId == null || entry.worktreeId !== ownerId)) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, ref);\n\t\t\t}\n\n\t\t\tif (isExclude) {\n\t\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate deliverOverflow(entry: SessionEntry): void {\n\t\t// Every kind, no ref (ref kinds stay unnamed). Never `'unknown'` — the cache would unregister the path\n\t\tentry.hooks.onRepoChanged(entry.repoPath, [...allWatchedRepositoryChanges], undefined, true);\n\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t}\n\n\tprivate onRootEvent(event: FileWatchEvent, entry: SessionEntry): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tthis.deliverOverflow(entry);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isDescendant(event.path, entry.gitDirPath)) return;\n\n\t\tconst relativePath = relative(entry.gitDirPath, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\t// Check for FETCH_HEAD before interpretation\n\t\tif (relativePath === 'FETCH_HEAD') {\n\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\t// A linked worktree's git dir isn't the common one, so no file there is the shared config\n\t\tif (entry.coversCommon && this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst changes = classifyGitDirChange(relativePath);\n\t\tif (changes != null) {\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, getChangedRef(relativePath));\n\t\t}\n\n\t\tif (relativePath === 'info/exclude') {\n\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\tprivate onConfigReported(): void {\n\t\t// The baseline may already hold the change reported, which comparing with it would lose\n\t\tif (this._configBaselining) {\n\t\t\tthis.deliverConfigChange(undefined);\n\t\t\treturn;\n\t\t}\n\t\tif (this._configReading) {\n\t\t\tthis._configRereadQueued = true;\n\t\t\treturn;\n\t\t}\n\n\t\tvoid this.readConfigChanges();\n\t}\n\n\tprivate async readConfigBaseline(): Promise<void> {\n\t\tthis._configBaselining = true;\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tconst config = await this.readConfig();\n\t\t\tif (this._disposed) return;\n\n\t\t\tthis._config = config;\n\t\t} finally {\n\t\t\tthis._configBaselining = false;\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** Reads and compares until no report came in during the last read: it may describe a change the read missed */\n\tprivate async readConfigChanges(): Promise<void> {\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tthis._configRereadQueued = false;\n\t\t\t\tconst config = await this.readConfig();\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tconst previous = this._config;\n\t\t\t\tif (config == null || previous == null) {\n\t\t\t\t\t// Unreadable now, or never readable before: the change may be anywhere. A failed read keeps the last\n\t\t\t\t\t// snapshot, so the next read still compares with what every session was told\n\t\t\t\t\tthis._config ??= config;\n\t\t\t\t\tthis.deliverConfigChange(undefined);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis._config = config;\n\t\t\t\tconst change = diffGitConfigSections(previous, config);\n\t\t\t\tif (change != null) {\n\t\t\t\t\tthis.deliverConfigChange(change);\n\t\t\t\t}\n\t\t\t} while (this._configRereadQueued);\n\t\t} finally {\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** `undefined` when the file couldn't be read or parsed, or the read didn't settle in time */\n\tprivate async readConfig(): Promise<GitConfigSections | undefined> {\n\t\tconst readFile = this.readFile;\n\t\tif (readFile == null) return undefined;\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tconst data = await Promise.race([\n\t\t\t\treadFile(joinPaths(this.commonGitDir, 'config')),\n\t\t\t\tnew Promise<undefined>(resolve => {\n\t\t\t\t\ttimer = setTimeout(resolve, configReadTimeoutMs, undefined);\n\t\t\t\t}),\n\t\t\t]);\n\t\t\treturn data != null ? parseGitConfigBytes(data) : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t}\n\n\t/**\n\t * Tells every session of a change to the shared `.git/config`: confined to some branches' sections, or (`undefined`,\n\t * or a change outside them) anywhere, which may have changed a remote's settings too. Either way no ref moved\n\t */\n\tprivate deliverConfigChange(change: GitConfigSectionsChange | undefined): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (change?.confined) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config'], false, false, change.branches);\n\t\t\t} else {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config', 'remotes'], false);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The shared `.git/config` (or the lock git writes it through), relative to the common git dir */\nfunction isConfigFile(relativePath: string): boolean {\n\treturn relativePath === 'config' || relativePath === 'config.lock';\n}\n\n/**\n * The ref a `.git`-relative path names: a loose ref (or ref directory) under `refs/`, or the `HEAD`/`ORIG_HEAD`\n * pseudo-refs. A lock file names the ref it locks; the only ones classification lets through are the pseudo-refs' and\n * `refs/stash.lock`.\n */\nfunction getChangedRef(relativePath: string): string | false | undefined {\n\tconst ref = relativePath.endsWith('.lock') ? relativePath.substring(0, relativePath.length - 5) : relativePath;\n\t// Remote config still invalidates remote and branch caches, but doesn't itself move any ref.\n\tif (ref === 'config' || ref === 'config.worktree') return false;\n\n\treturn ref.startsWith('refs/') || ref === 'HEAD' || ref === 'ORIG_HEAD' ? ref : undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"watchGroup.js","names":[],"sources":["../../../../git/src/watching/watchGroup.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { dirname, isAbsolute, isDescendant, joinPaths, normalizePath, relative } from '@gitlens/utils/path.js';\nimport { FileType } from '../context.js';\nimport type { GitDir, RepositoryChange } from '../models/repository.js';\nimport { allWatchedRepositoryChanges, classifyGitDirChange } from './classifyChange.js';\nimport type { GitConfigSections, GitConfigSectionsChange } from './configSections.js';\nimport { diffGitConfigSections, parseGitConfigBytes } from './configSections.js';\nimport type { GitDirPoller, GitDirProbe } from './gitDirPoller.js';\nimport { GitDirProbeTarget } from './gitDirPoller.js';\nimport type {\n\tFileWatcher,\n\tFileWatcherOptions,\n\tFileWatcherReliability,\n\tFileWatchEvent,\n\tFileWatchingProvider,\n} from './provider.js';\nimport {\n\tdotGitGlobCombined,\n\tdotGitGlobCommon,\n\tdotGitGlobRoot,\n\tgetGitDirRefProbes,\n\tgetIgnoredWorktreeFolderExcludes,\n\tgitDirCommonProbes,\n\tgitDirExcludes,\n\tgitDirProbesByEmittedPath,\n\tgitDirRootProbes,\n\tshouldIgnoreRepoPath,\n} from './watcherPatterns.js';\n\n/**\n * How long a read of the shared `.git/config`, or of the worktrees' admin entries, may take before it counts as failed.\n * Reads of each run one at a time, so one that never settles, as on a hung network or WSL mount, would otherwise hold\n * back every later report.\n */\nconst readTimeoutMs = 5000;\n\n/**\n * How many removed admin entries are remembered. Each watcher reports a removal once for each file it watched in the\n * entry, and two watchers see the common dir beside a main checkout, so its reports outlive any single one of them\n */\nconst removedWorktreeIdsLimit = 64;\n\n/**\n * How long after an admin entry's `gitdir` couldn't be read it is read once more. A watcher can report the entry before\n * git wrote its `gitdir`, and then miss the write itself (a new directory's files go unseen until it is watched)\n */\nconst worktreeRetryDelayMs = 250;\n\nfunction noop(): void {}\n\n/**\n * Hooks that the WatchGroup fires when events are interpreted.\n * These are raw, un-debounced events — the session handles debouncing.\n */\nexport interface WatchGroupHooks {\n\t/**\n\t * `ref` is the full ref name (or ref directory) or pseudo-ref (`HEAD`, `ORIG_HEAD`) whose file changed, with a\n\t * trailing `.lock` stripped; `false` when config changed without moving refs, and `undefined` when ref movement\n\t * is unknown. `overflow` marks the every-kind rescan of a watcher that dropped events, which no single write can\n\t * account for. `configBranches`, with a `config` change, are the local branches (full ref names) whose\n\t * `branch.<name>` sections are all that changed in the shared `.git/config`; without it, the config change may be\n\t * anywhere.\n\t */\n\treadonly onRepoChanged: (\n\t\trepoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\tref?: string | false,\n\t\toverflow?: boolean,\n\t\tconfigBranches?: ReadonlySet<string>,\n\t) => void;\n\treadonly onFetchHeadChanged?: (repoPath: string) => void;\n\treadonly onIgnoresChanged?: (repoPath: string) => void;\n\t/** The refs whose loose files a poll stats for this session, read at each poll */\n\treadonly getProbeRefs?: () => Iterable<string>;\n\t/** A watcher this session hears through went `unwatchable`, or stopped being it (see {@link WatchGroup.isUnwatchable}) */\n\treadonly onUnwatchableChanged?: (repoPath: string) => void;\n}\n\n/**\n * Tracks one session's connection to a WatchGroup.\n * Each session has its own root watcher but shares the common watcher.\n */\ninterface SessionEntry {\n\treadonly repoPath: string;\n\treadonly gitDirPath: string;\n\treadonly rootWatcher: FileWatcher;\n\t/** True when the root watcher is on the common dir itself, so it already sees every common file */\n\treadonly coversCommon: boolean;\n\t/** For a linked session, its `<n>` in `<common>/worktrees/<n>`; otherwise `undefined` */\n\treadonly worktreeId: string | undefined;\n\treadonly hooks: WatchGroupHooks;\n\treliability: FileWatcherReliability;\n\t/** Polls in place of (or beside) the root watcher while it isn't `reliable` */\n\ttarget?: GitDirProbeTarget;\n}\n\n/**\n * Manages filesystem watchers for a single physical `.git` directory,\n * shared across all worktrees that reference it.\n *\n * Structure:\n * - 1 common watcher: config, refs/**, info/exclude, etc. Delivers to linked sessions only; a\n * session rooted at the common dir hears the same files from its own combined root watcher.\n * - N root watchers: one per worktree (index, HEAD, *_HEAD, etc.)\n *\n * For a standard (non-worktree) repo, the common and root directories\n * are the same, but we still create separate watchers for clean separation.\n *\n * Given a `readFile`, the group keeps the shared `.git/config` as it last read it, section by section. A report of\n * that file (or its lock), from whichever watcher, is read and compared with it once, and every session hears what\n * changed: only some branches' `branch.<name>` sections (`config`, confined to them), anything else (`config` and\n * `remotes`, unconfined), or nothing at all, when an earlier report's read already delivered the change. Reads run\n * one at a time, so each compares with the one before it and none is skipped. The first read, taken as the group\n * starts, is the baseline and delivers nothing; a report arriving before it completes may describe a change it\n * already holds, so it is delivered unconfined. A read that fails or doesn't settle within a few seconds, or a file\n * git couldn't parse, is unconfined too.\n *\n * Given a `readFile`, a `readDirectory` (a directory's entries with their types, only the directories counting as\n * admin entries) and the folders whose worktrees the host has core ignore (a tool's throwaway worktrees, which no\n * host shows), the group reports no change of an ignored worktree's admin entry\n * (`<common>/worktrees/<id>`) to any session. Only registered folders count, since a user may keep real worktrees\n * anywhere, `.git` included. It lists the entries and reads each one's `gitdir` once as it starts, and again after a\n * watcher dropped events; a report arriving meanwhile is told at once, and decided once the listing settles, so what\n * is known of its entry catches up. From then on it reads an entry only when its `gitdir` may be new (the entry was\n * created, or its `gitdir` written, or it was removed before), so a real worktree's HEAD moves are told at once,\n * unread. Of two reads of one entry, the later decides both reports. An entry it doesn't know whose `gitdir` can't be\n * read (a watcher can report one before git writes it, and miss the write) is read once more a moment later, its\n * reports held until then, and told if it still can't be. Only the entry's directory or its `gitdir` going is a\n * removal; a deleted `HEAD` (a watcher may report git's lock file renamed over it as a delete) or `locked` is told as\n * the entry is known. Removing an entry deletes the `gitdir` a read needs, so a removal is told from what the entry was\n * known to be (before a read that couldn't read it, if one is waiting to read it again), and an entry it never knew is\n * told as real. Any other report of a removed entry but a delete is read, and one that can't be read is the removal's\n * own (a watcher may report the entry's directory changed beside each file it deletes), unless it creates the entry\n * again. A report of the `worktrees` directory itself (a coalesced\n * removal, a parent directory change, a poll, which sees nothing finer) lists it again: it is told to every session\n * when a real entry was added or removed since the last listing, and to none when only ignored ones were. Git removes\n * the directory with its last entry, so a directory that isn't there is an empty listing, as is a removal of it that\n * can't be listed. Since a listing (or a worktree moved into or out of a folder) changes what every session shows, it\n * is told to all of them; the other watcher's report of the same change then finds nothing left to tell. Where only a\n * poll watches the common dir, it sees just the `worktrees` directory, so an entry removed and added again under the\n * same id between two polls, or an entry's HEAD or lock changed in place, goes untold.\n *\n * Given the folders, the watchers over the common dir leave each one unwatched, since a tool's checkout there would\n * otherwise have every directory of it watched; one that shares a top-level directory with what the `.git` watch reads\n * stays watched.\n */\nexport class WatchGroup implements UnifiedDisposable {\n\tprivate readonly _sessions = new Map<string, SessionEntry>();\n\tprivate _commonWatcher: FileWatcher | undefined;\n\tprivate _commonReliability: FileWatcherReliability = 'reliable';\n\tprivate _commonTarget: GitDirProbeTarget | undefined;\n\tprivate _disposed = false;\n\n\t/** The shared `.git/config`, by section, as the last read that could parse it found it */\n\tprivate _config: GitConfigSections | undefined;\n\t/** The baseline read is in flight */\n\tprivate _configBaselining = false;\n\tprivate _configReading = false;\n\tprivate _configRereadQueued = false;\n\n\t/**\n\t * Settles once the latest baseline of the admin entries was taken; `undefined` when the group can't tell an\n\t * ignored worktree's, so every report is told at once\n\t */\n\tprivate _worktreesBaseline: Promise<void> | undefined;\n\t/** A baseline is pending, so a report waits for it */\n\tprivate _worktreesBaselining = false;\n\tprivate _worktreesRebaselineQueued = false;\n\t/** Set by the first baseline; while empty, nothing is ignored and every report is told at once */\n\tprivate _ignoredWorktreeRoots: readonly string[] = [];\n\t/** Listings run one at a time, so each compares with the one before it */\n\tprivate _worktreesQueue: Promise<void> = Promise.resolve();\n\t/** The `worktrees` directory's reports waiting on the queued listing; `undefined` while none is queued */\n\tprivate _worktreesRescanQueued:\n\t\t| {\n\t\t\t\treadonly delivers: (() => void)[];\n\t\t\t\t/** One of them is a removal of the directory */\n\t\t\t\tremoval: boolean;\n\t\t }\n\t\t| undefined;\n\t/** The admin entries the last listing found */\n\tprivate _listedWorktreeIds: ReadonlySet<string> = new Set();\n\t/** Admin entries known to exist, by id: whether each is an ignored worktree's */\n\tprivate readonly _worktreeIds = new Map<string, boolean>();\n\t/** What removed admin entries were, by id, oldest first */\n\tprivate readonly _removedWorktreeIds = new Map<string, boolean>();\n\t/**\n\t * Each id's latest `gitdir` read, settling with what it found and what the id was known to be before it; only it\n\t * may update what is known of the id. A removal of the entry drops it\n\t */\n\tprivate readonly _worktreeReads = new Map<\n\t\tstring,\n\t\tPromise<[ignored: boolean | undefined, previous: boolean | undefined]>\n\t>();\n\t/** Admin entries whose `gitdir` couldn't be read, by id, waiting to be read once more */\n\tprivate readonly _worktreeRetries = new Map<\n\t\tstring,\n\t\t{\n\t\t\treadonly timer: ReturnType<typeof setTimeout>;\n\t\t\t/** The reports waiting on it, told unless the entry turns out to be an ignored worktree's */\n\t\t\treadonly delivers: (() => void)[];\n\t\t\t/**\n\t\t\t * What the entry was known to be before its `gitdir` couldn't be read, so a move or a removal is still told\n\t\t\t * as it was\n\t\t\t */\n\t\t\tprevious: boolean | undefined;\n\t\t\t/**\n\t\t\t * Settles with whether the entry is an ignored worktree's; `undefined` when it still can't tell, or it was\n\t\t\t * removed\n\t\t\t */\n\t\t\treadonly decided: Promise<boolean | undefined>;\n\t\t\treadonly resolve: (ignored: boolean | undefined) => void;\n\t\t}\n\t>();\n\t/** The `.git` excludes of a watcher over the common git dir, with the ignored worktrees' folders it may leave out */\n\tprivate readonly _commonGitDirExcludes: readonly string[];\n\n\tconstructor(\n\t\treadonly commonGitDir: string,\n\t\tprivate readonly provider: FileWatchingProvider,\n\t\t/** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */\n\t\tprivate readonly poller?: GitDirPoller,\n\t\t/**\n\t\t * When given, reports of the shared `.git/config` are classified by what changed in it, and admin entries are\n\t\t * read to tell an ignored worktree's (see above)\n\t\t */\n\t\tprivate readonly readFile?: (path: string) => Promise<Uint8Array>,\n\t\t/**\n\t\t * Returns a directory's entries with their types; with `readFile`, the admin entries are listed, only the\n\t\t * directories counting (see above)\n\t\t */\n\t\tprivate readonly readDirectory?: (path: string) => Promise<readonly (readonly [string, number])[]>,\n\t\t/** The absolute, normalized folders whose worktrees are ignored; none reads no admin entry */\n\t\tignoredWorktreeRoots?: Promise<readonly string[]>,\n\t\t/** The folders whose worktrees are ignored, relative to the common git dir */\n\t\tignoredWorktreeFolders?: readonly string[],\n\t) {\n\t\tconst folders = getIgnoredWorktreeFolderExcludes(ignoredWorktreeFolders);\n\t\tthis._commonGitDirExcludes = folders.length ? [...gitDirExcludes, ...folders] : gitDirExcludes;\n\t\tif (readFile != null) {\n\t\t\tvoid this.readConfigBaseline();\n\t\t}\n\t\tif (readFile != null && readDirectory != null && ignoredWorktreeRoots != null) {\n\t\t\tthis.queueWorktreesBaseline(async () => {\n\t\t\t\tconst roots = await this.withTimeout(() => Promise.resolve(ignoredWorktreeRoots));\n\t\t\t\tif (this._disposed || !Array.isArray(roots) || !roots.length) return;\n\n\t\t\t\tthis._ignoredWorktreeRoots = roots;\n\t\t\t\tawait this.listWorktrees('baseline');\n\t\t\t});\n\t\t}\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\t/** Dispose the entire group (common watcher + all root watchers). */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tentry.rootWatcher.dispose();\n\t\t\tthis.stopPolling(entry);\n\t\t}\n\t\tthis._sessions.clear();\n\n\t\tthis.disposeCommonWatcher();\n\n\t\tfor (const retry of this._worktreeRetries.values()) {\n\t\t\tclearTimeout(retry.timer);\n\t\t\tretry.resolve(undefined);\n\t\t}\n\t\tthis._worktreeRetries.clear();\n\t}\n\n\tget sessions(): ReadonlyMap<string, SessionEntry> {\n\t\treturn this._sessions;\n\t}\n\n\t/** True when a watcher the session hears through — its root watcher, or a linked session's common one — can't watch */\n\tisUnwatchable(repoPath: string): boolean {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return false;\n\n\t\treturn (\n\t\t\tentry.reliability === 'unwatchable' || (!entry.coversCommon && this._commonReliability === 'unwatchable')\n\t\t);\n\t}\n\n\t/** Add a session to this group. Creates the root watcher for the session's gitDir. */\n\taddSession(repoPath: string, gitDir: GitDir, hooks: WatchGroupHooks): void {\n\t\tif (this._disposed) return;\n\t\tif (this._sessions.has(repoPath)) return; // Already tracked\n\n\t\t// Determine the glob pattern for the root watcher\n\t\tconst isStandard = gitDir.commonUri == null;\n\t\tconst rootPattern = isStandard ? dotGitGlobCombined : dotGitGlobRoot;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns, so before the entry exists\n\t\tlet entry: SessionEntry | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\n\t\tconst rootWatcher = this.provider.createWatcher(\n\t\t\tgitDir.uri.fsPath,\n\t\t\trootPattern,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tconst e = this._sessions.get(repoPath);\n\t\t\t\tif (e != null) {\n\t\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\t\te.target?.forget(event.path);\n\t\t\t\t\t}\n\t\t\t\t\tthis.onRootEvent(event, e);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.getWatcherOptions(gitDir.uri.fsPath, state => {\n\t\t\t\tif (entry == null) {\n\t\t\t\t\tinitialReliability = state;\n\t\t\t\t} else if (this._sessions.get(repoPath) === entry) {\n\t\t\t\t\tthis.setRootReliability(entry, state);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tconst entryGitDirPath = gitDir.uri.fsPath;\n\t\tentry = {\n\t\t\trepoPath: repoPath,\n\t\t\tgitDirPath: entryGitDirPath,\n\t\t\trootWatcher: rootWatcher,\n\t\t\tcoversCommon: isStandard,\n\t\t\tworktreeId: isStandard ? undefined : this.getWorktreeId(entryGitDirPath),\n\t\t\thooks: hooks,\n\t\t\treliability: 'reliable',\n\t\t};\n\n\t\tthis._sessions.set(repoPath, entry);\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setRootReliability(entry, initialReliability, true);\n\t\t}\n\n\t\t// Start the common watcher if this is a worktree repo and we haven't yet.\n\t\t// Standard repos don't need a separate common watcher because their root\n\t\t// watcher uses dotGitGlobCombined which already covers all patterns.\n\t\tif (!isStandard) {\n\t\t\tthis.ensureCommonWatcher();\n\t\t}\n\t}\n\n\t/** Remove a session from this group. Disposes its root watcher. */\n\tremoveSession(repoPath: string): void {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return;\n\n\t\tentry.rootWatcher.dispose();\n\t\tthis.stopPolling(entry);\n\t\tthis._sessions.delete(repoPath);\n\n\t\t// Once no linked session is left, dispose the common watcher — it delivers to linked sessions only\n\t\tif (this._commonWatcher != null && !some(this._sessions.values(), e => !e.coversCommon)) {\n\t\t\tthis.disposeCommonWatcher();\n\t\t}\n\t}\n\n\tprivate getWorktreeId(gitDirPath: string): string | undefined {\n\t\tif (!isDescendant(gitDirPath, this.commonGitDir)) return undefined;\n\n\t\tconst rel = relative(this.commonGitDir, gitDirPath);\n\t\tif (!rel.startsWith('worktrees/')) return undefined;\n\n\t\tconst id = rel.substring(10);\n\t\treturn id.length > 0 && !id.includes('/') ? id : undefined;\n\t}\n\n\tprivate ensureCommonWatcher(): void {\n\t\tif (this._commonWatcher != null || this._disposed) return;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns\n\t\tlet watcher: FileWatcher | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\t\tconst options = this.getWatcherOptions(this.commonGitDir, state => {\n\t\t\tif (watcher == null) {\n\t\t\t\tinitialReliability = state;\n\t\t\t} else if (this._commonWatcher === watcher) {\n\t\t\t\tthis.setCommonReliability(state);\n\t\t\t}\n\t\t});\n\n\t\twatcher = this.provider.createWatcher(\n\t\t\tthis.commonGitDir,\n\t\t\tdotGitGlobCommon,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\tthis._commonTarget?.forget(event.path);\n\t\t\t\t}\n\t\t\t\tthis.onCommonEvent(event);\n\t\t\t},\n\t\t\toptions,\n\t\t);\n\t\tthis._commonWatcher = watcher;\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setCommonReliability(initialReliability, true);\n\t\t}\n\t}\n\n\tprivate disposeCommonWatcher(): void {\n\t\tthis._commonWatcher?.dispose();\n\t\tthis._commonWatcher = undefined;\n\t\tthis._commonReliability = 'reliable';\n\t\tif (this._commonTarget != null) {\n\t\t\tthis.poller?.delete(this._commonTarget);\n\t\t\tthis._commonTarget = undefined;\n\t\t}\n\t}\n\n\t/** Hosts that didn't opt into polling get exactly the options they always had */\n\tprivate getWatcherOptions(\n\t\tbasePath: string,\n\t\tonDidChangeReliability: (state: FileWatcherReliability) => void,\n\t): FileWatcherOptions {\n\t\tconst excludes = this.getExcludes(basePath);\n\t\treturn this.poller != null\n\t\t\t? { excludes: excludes, onDidChangeReliability: onDidChangeReliability }\n\t\t\t: { excludes: excludes };\n\t}\n\n\t/**\n\t * The ignored worktrees' folders join the `.git` excludes of a watcher over the common git dir: a tool that keeps its\n\t * throwaway checkouts inside the git dir would otherwise have each one's every directory watched, and every file it\n\t * writes reported. Every other watcher sits on a linked worktree's admin entry, which holds none of them\n\t */\n\tprivate getExcludes(basePath: string): readonly string[] {\n\t\tif (this._commonGitDirExcludes === gitDirExcludes) return gitDirExcludes;\n\n\t\treturn normalizePath(basePath) === normalizePath(this.commonGitDir)\n\t\t\t? this._commonGitDirExcludes\n\t\t\t: gitDirExcludes;\n\t}\n\n\tprivate setRootReliability(entry: SessionEntry, state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = entry.reliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tentry.reliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tthis.stopPolling(entry);\n\t\t} else if (entry.target == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tentry.gitDirPath,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getRootProbes(entry),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\t(event, probe) => {\n\t\t\t\t\tif (entry.target !== target) return;\n\n\t\t\t\t\tthis.onRootEvent(event, entry);\n\t\t\t\t\tif (probe.path === 'logs/HEAD' && !entry.coversCommon) {\n\t\t\t\t\t\tthis.onLinkedHeadMoved();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tentry.target = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\t// The watch went down or came back: whatever changed meanwhile went unseen\n\t\tif (!initial) {\n\t\t\tthis.deliverOverflow(entry);\n\t\t\tif (entry.coversCommon) {\n\t\t\t\tthis.rebaselineWorktrees();\n\t\t\t}\n\t\t}\n\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t}\n\n\tprivate setCommonReliability(state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = this._commonReliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tthis._commonReliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tif (this._commonTarget != null) {\n\t\t\t\tthis.poller.delete(this._commonTarget);\n\t\t\t\tthis._commonTarget = undefined;\n\t\t\t}\n\t\t} else if (this._commonTarget == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tthis.commonGitDir,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getCommonProbes(),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\tevent => {\n\t\t\t\t\tif (this._commonTarget === target) {\n\t\t\t\t\t\tthis.onCommonEvent(event);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._commonTarget = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (!initial) {\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t\t}\n\t\tif (!initial) {\n\t\t\tthis.rebaselineWorktrees();\n\t\t}\n\t}\n\n\tprivate stopPolling(entry: SessionEntry): void {\n\t\tif (entry.target == null) return;\n\n\t\tthis.poller?.delete(entry.target);\n\t\tentry.target = undefined;\n\t}\n\n\t/**\n\t * A session rooted at the common dir polls the common files too, with the refs of every session in the group: its\n\t * combined watcher would hear them all. While the common dir is polled too, the poller stats each shared file once\n\t * for both, and each keeps its own readings, so what one watcher reported is held back from its own sessions only\n\t */\n\tprivate getRootProbes(entry: SessionEntry): readonly GitDirProbe[] {\n\t\tif (!entry.coversCommon) return gitDirRootProbes;\n\n\t\tconst refs = new Set<string>();\n\t\tfor (const e of this._sessions.values()) {\n\t\t\tfor (const ref of e.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirRootProbes, ...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\t/**\n\t * A linked worktree's `logs/HEAD` moved — a commit or reset there, on a branch a polled session rooted at the common\n\t * dir may not probe — so each such session hears a `heads` change its combined watcher would have heard\n\t */\n\tprivate onLinkedHeadMoved(): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (!entry.coversCommon || entry.target == null) continue;\n\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['heads']);\n\t\t}\n\t}\n\n\t/** The common files, with the refs of every linked session (the only ones the common watcher delivers to) */\n\tprivate getCommonProbes(): GitDirProbe[] {\n\t\tconst refs = new Set<string>();\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tfor (const ref of entry.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\tprivate onCommonEvent(event: FileWatchEvent): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tfor (const entry of this._sessions.values()) {\n\t\t\t\tif (entry.coversCommon) continue;\n\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\tthis.rebaselineWorktrees();\n\t\t\treturn;\n\t\t}\n\n\t\t// Compute relative path from common git dir\n\t\tif (!isDescendant(event.path, this.commonGitDir)) return;\n\n\t\tconst relativePath = relative(this.commonGitDir, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\tif (this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst isFetchHead = relativePath === 'FETCH_HEAD';\n\t\tconst isExclude = relativePath === 'info/exclude';\n\t\tconst changes = isFetchHead\n\t\t\t? undefined\n\t\t\t: withWorktreeEntryHead(classifyGitDirChange(relativePath), relativePath, event.reason);\n\t\tif (changes == null && !isFetchHead && !isExclude) return;\n\n\t\tconst ref = changes != null ? getChangedRef(relativePath) : undefined;\n\n\t\t// `worktrees/<n>/HEAD`: the owner hears its own HEAD from its root watcher. Its `locked` and `gitdir` aren't in\n\t\t// the root glob, so the owner hears those only from here\n\t\tlet ownerId: string | undefined;\n\t\tif (changes != null && relativePath.length > 10 && relativePath.startsWith('worktrees/')) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd !== -1 && relativePath.substring(idEnd + 1) === 'HEAD') {\n\t\t\t\townerId = relativePath.substring(10, idEnd);\n\t\t\t}\n\t\t}\n\n\t\tif (changes != null && this.isWorktreesReport(changes)) {\n\t\t\tconst sessions = [...this._sessions.values()].filter(\n\t\t\t\te => !e.coversCommon && (ownerId == null || e.worktreeId !== ownerId),\n\t\t\t);\n\t\t\tthis.onWorktreesReported(relativePath, event.reason, () => {\n\t\t\t\tfor (const entry of sessions) {\n\t\t\t\t\tif (this._disposed || this._sessions.get(entry.repoPath) !== entry) continue;\n\n\t\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, ref);\n\t\t\t\t}\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\t// A session rooted at the common dir sees every common file (hooks included) through its own\n\t\t\t// combined root watcher, so delivering here too would duplicate it\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (isFetchHead) {\n\t\t\t\t// FETCH_HEAD lives in the main .git dir; each worktree updates its last-fetched state\n\t\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (changes != null && (ownerId == null || entry.worktreeId !== ownerId)) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, ref);\n\t\t\t}\n\n\t\t\tif (isExclude) {\n\t\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate deliverOverflow(entry: SessionEntry): void {\n\t\t// Every kind, no ref (ref kinds stay unnamed). Never `'unknown'` — the cache would unregister the path\n\t\tentry.hooks.onRepoChanged(entry.repoPath, [...allWatchedRepositoryChanges], undefined, true);\n\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t}\n\n\tprivate onRootEvent(event: FileWatchEvent, entry: SessionEntry): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tthis.deliverOverflow(entry);\n\t\t\tif (entry.coversCommon) {\n\t\t\t\tthis.rebaselineWorktrees();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isDescendant(event.path, entry.gitDirPath)) return;\n\n\t\tconst relativePath = relative(entry.gitDirPath, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\t// Check for FETCH_HEAD before interpretation\n\t\tif (relativePath === 'FETCH_HEAD') {\n\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\t// A linked worktree's git dir isn't the common one, so no file there is the shared config\n\t\tif (entry.coversCommon && this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst changes = withWorktreeEntryHead(classifyGitDirChange(relativePath), relativePath, event.reason);\n\t\t// A linked worktree's git dir isn't the common one, so no path there is a worktree's admin entry\n\t\tif (changes != null && entry.coversCommon && this.isWorktreesReport(changes)) {\n\t\t\tthis.onWorktreesReported(relativePath, event.reason, () => {\n\t\t\t\tif (this._disposed || this._sessions.get(entry.repoPath) !== entry) return;\n\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, getChangedRef(relativePath));\n\t\t\t});\n\t\t} else if (changes != null) {\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, getChangedRef(relativePath));\n\t\t}\n\n\t\tif (relativePath === 'info/exclude') {\n\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\t/** A report of the `worktrees` directory, an admin entry or a file in it, once the group may tell them apart */\n\tprivate isWorktreesReport(changes: readonly RepositoryChange[]): boolean {\n\t\treturn (\n\t\t\tthis._worktreesBaseline != null &&\n\t\t\tchanges.includes('worktrees') &&\n\t\t\tchanges.every(c => c === 'worktrees' || c === 'worktreeHead')\n\t\t);\n\t}\n\n\t/** `deliver` tells the sessions the report reaches of it, should the report concern them (see above) */\n\tprivate onWorktreesReported(relativePath: string, reason: FileWatchEvent['reason'], deliver: () => void): void {\n\t\tif (this._worktreesBaselining && this._worktreesBaseline != null) {\n\t\t\t// The baseline may have read the entry before the change reported, so the report is told; it is still decided\n\t\t\t// once the baseline settles, so what is known of the entry catches up with it\n\t\t\tdeliver();\n\t\t\tvoid this._worktreesBaseline.then(() => this.decideWorktreesReport(relativePath, reason, noop));\n\t\t\treturn;\n\t\t}\n\n\t\tthis.decideWorktreesReport(relativePath, reason, deliver);\n\t}\n\n\tprivate decideWorktreesReport(relativePath: string, reason: FileWatchEvent['reason'], deliver: () => void): void {\n\t\tif (this._disposed) return;\n\t\tif (!this._ignoredWorktreeRoots.length) {\n\t\t\tdeliver();\n\t\t\treturn;\n\t\t}\n\n\t\tif (relativePath === 'worktrees') {\n\t\t\tthis.queueWorktreesRescan(reason, deliver);\n\t\t\treturn;\n\t\t}\n\n\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\tconst id = idEnd === -1 ? relativePath.substring(10) : relativePath.substring(10, idEnd);\n\t\tconst file = idEnd === -1 ? undefined : relativePath.substring(idEnd + 1);\n\n\t\t// Only the entry or its `gitdir` going is a removal: an unlock deletes `locked` from an entry that stays, and a\n\t\t// watcher may report git's lock file renamed over `HEAD` as `HEAD` deleted and created again\n\t\tif (reason === 'delete' && (file == null || file === 'gitdir')) {\n\t\t\tif (this.forgetWorktreeId(id) !== true) {\n\t\t\t\tdeliver();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// What a removed entry was decides the removal's other reports. Any other report of its id is read: one that finds\n\t\t// the entry is of an entry created again, which may be another worktree's. One that can't is the removal's own\n\t\t// unless it creates the entry, since a watcher may report the entry's directory changed beside each file the\n\t\t// removal deletes\n\t\tconst removed = this._worktreeIds.has(id) ? undefined : this._removedWorktreeIds.get(id);\n\t\tconst known = this._worktreeIds.get(id) ?? (reason === 'delete' ? removed : undefined);\n\t\t// Only a new entry or a written `gitdir` can name another worktree; anything else is told as the id is known\n\t\tif (known != null && file !== 'gitdir' && !(file == null && reason === 'create')) {\n\t\t\tif (!known) {\n\t\t\t\tdeliver();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst retry = removed == null || reason === 'create';\n\t\tvoid this.readWorktreeId(id, retry).then(([ignored, previous]) => {\n\t\t\tif (this._disposed) return;\n\n\t\t\tif (ignored == null) {\n\t\t\t\tif (retry) {\n\t\t\t\t\tvoid this.waitForWorktreeRetry(id, deliver);\n\t\t\t\t} else if (removed === false) {\n\t\t\t\t\tdeliver();\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Moved into or out of an ignored folder: every session's list gained or lost it\n\t\t\tif (previous != null && ignored !== previous) {\n\t\t\t\tthis.deliverWorktreesChange();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (!ignored) {\n\t\t\t\tdeliver();\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Reads the entry's `gitdir`: whether it names an ignored worktree (`undefined` when it can't be read), and what the\n\t * id was known to be when this read replaced it. Reads of one id can settle out of order (a slow one from before the\n\t * worktree moved out of the folder), so only the latest updates what is known of it; an earlier one settles with\n\t * what the latest finds, without what the id was, since the latest tells a move itself, or once the entry was removed\n\t * since, with what the removal found it was. One that can't read the entry forgets what it was known to be, since git\n\t * may be writing its `gitdir` again, and reads it once more a moment later, unless `retry` is false (it then leaves\n\t * what is known as it was); one that can settles that read, if pending.\n\t */\n\tprivate readWorktreeId(\n\t\tid: string,\n\t\tretry: boolean = true,\n\t): Promise<[ignored: boolean | undefined, previous: boolean | undefined]> {\n\t\tconst read: Promise<[ignored: boolean | undefined, previous: boolean | undefined]> = this.readIgnoredWorktree(\n\t\t\tid,\n\t\t).then(ignored => {\n\t\t\tconst latest = this._worktreeReads.get(id);\n\t\t\tif (latest !== read) {\n\t\t\t\tif (latest == null) return [this._removedWorktreeIds.get(id), undefined];\n\n\t\t\t\treturn latest.then(([found]): [boolean | undefined, undefined] => {\n\t\t\t\t\t// A later read that couldn't read the entry may not read it again, but this one's report may be of\n\t\t\t\t\t// the entry created again, unless it was removed since\n\t\t\t\t\tif (found == null && retry && this._worktreeReads.has(id)) {\n\t\t\t\t\t\tthis.scheduleWorktreeRetry(id, undefined);\n\t\t\t\t\t}\n\t\t\t\t\treturn [found, undefined];\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst previous = this._worktreeIds.get(id);\n\t\t\tif (ignored == null) {\n\t\t\t\tif (!retry) return [undefined, previous];\n\n\t\t\t\tthis._removedWorktreeIds.delete(id);\n\t\t\t\tthis._worktreeIds.delete(id);\n\t\t\t\tthis.scheduleWorktreeRetry(id, previous);\n\t\t\t\treturn [undefined, previous];\n\t\t\t}\n\n\t\t\tthis._removedWorktreeIds.delete(id);\n\t\t\tthis._worktreeIds.set(id, ignored);\n\t\t\tthis.settleWorktreeRetry(id, ignored);\n\t\t\treturn [ignored, previous];\n\t\t});\n\t\tthis._worktreeReads.set(id, read);\n\t\treturn read;\n\t}\n\n\t/** Reads the entry once more a moment later, unless that is already pending; `previous` is what it was known to be */\n\tprivate scheduleWorktreeRetry(id: string, previous: boolean | undefined): void {\n\t\tif (this._disposed) return;\n\n\t\tconst pending = this._worktreeRetries.get(id);\n\t\tif (pending != null) {\n\t\t\tpending.previous ??= previous;\n\t\t\treturn;\n\t\t}\n\n\t\tlet resolve!: (ignored: boolean | undefined) => void;\n\t\tconst decided = new Promise<boolean | undefined>(r => {\n\t\t\tresolve = r;\n\t\t});\n\t\tconst timer = setTimeout(() => {\n\t\t\tvoid this.readWorktreeId(id).then(([ignored]) => {\n\t\t\t\t// A removal, or a later read that could read the entry, may have settled it meanwhile\n\t\t\t\tif (this._worktreeRetries.get(id)?.decided === decided) {\n\t\t\t\t\tthis.settleWorktreeRetry(id, ignored);\n\t\t\t\t}\n\t\t\t});\n\t\t}, worktreeRetryDelayMs);\n\t\tthis._worktreeRetries.set(id, {\n\t\t\ttimer: timer,\n\t\t\tdelivers: [],\n\t\t\tprevious: previous,\n\t\t\tdecided: decided,\n\t\t\tresolve: resolve,\n\t\t});\n\t}\n\n\t/**\n\t * A report whose read couldn't read the entry waits on the entry's next read, and `deliver` is told then, unless that\n\t * read finds an ignored worktree's; one still unreadable is told. Both watchers report each change of an entry, so\n\t * the reports of one id wait on one read. Resolves with what it found, as {@link settleWorktreeRetry} does\n\t */\n\tprivate waitForWorktreeRetry(id: string, deliver?: () => void): Promise<boolean | undefined> {\n\t\t// A later read may already have found what the entry is\n\t\tconst known = this._worktreeIds.get(id);\n\t\tif (known != null) {\n\t\t\tif (!known) {\n\t\t\t\tdeliver?.();\n\t\t\t}\n\t\t\treturn Promise.resolve(known);\n\t\t}\n\n\t\t// None is pending only when the entry was removed since, and the removal is told itself\n\t\tconst retry = this._worktreeRetries.get(id);\n\t\tif (retry == null) return Promise.resolve(undefined);\n\n\t\tif (deliver != null) {\n\t\t\tretry.delivers.push(deliver);\n\t\t}\n\t\treturn retry.decided;\n\t}\n\n\t/**\n\t * Tells the reports waiting on the id's retry what the entry turned out to be: nothing when an ignored worktree's,\n\t * every session when it moved into or out of an ignored folder, and the reports' own sessions otherwise, `ignored`\n\t * being `undefined` when it still couldn't be read\n\t */\n\tprivate settleWorktreeRetry(id: string, ignored: boolean | undefined): void {\n\t\tconst pending = this._worktreeRetries.get(id);\n\t\tif (pending == null) return;\n\n\t\tthis._worktreeRetries.delete(id);\n\t\tclearTimeout(pending.timer);\n\t\t// Settled before any session is told: a listing waits on it, and a hook that throws would otherwise hold back\n\t\t// every later listing for good\n\t\tpending.resolve(ignored);\n\t\tif (ignored != null && pending.previous != null && ignored !== pending.previous) {\n\t\t\tthis.deliverWorktreesChange();\n\t\t} else if (ignored !== true) {\n\t\t\tfor (const deliver of pending.delivers) {\n\t\t\t\tdeliver();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Takes `id` as removed, remembering what it was for the removal's other reports; returns that, or `undefined` when\n\t * the id was never known\n\t */\n\tprivate forgetWorktreeId(id: string): boolean | undefined {\n\t\t// A read still in flight read the entry before it went, and would bring the removed id back as known\n\t\tthis._worktreeReads.delete(id);\n\n\t\tlet ignored = this._worktreeIds.get(id);\n\t\t// The reports waiting to read the entry again are dropped: the removal is told for what the id was, which only\n\t\t// the retry still knows once a read couldn't read the entry\n\t\tconst retry = this._worktreeRetries.get(id);\n\t\tif (retry != null) {\n\t\t\tthis._worktreeRetries.delete(id);\n\t\t\tclearTimeout(retry.timer);\n\t\t\tretry.resolve(undefined);\n\t\t\tignored ??= retry.previous;\n\t\t}\n\n\t\tif (ignored == null) return this._removedWorktreeIds.get(id);\n\n\t\tthis._worktreeIds.delete(id);\n\t\tthis._removedWorktreeIds.delete(id);\n\t\tthis._removedWorktreeIds.set(id, ignored);\n\t\tif (this._removedWorktreeIds.size > removedWorktreeIdsLimit) {\n\t\t\tconst [oldest] = this._removedWorktreeIds.keys();\n\t\t\tthis._removedWorktreeIds.delete(oldest);\n\t\t}\n\t\treturn ignored;\n\t}\n\n\t/** Whether the entry's `gitdir` names an ignored worktree, or `undefined` when it can't be read. Never rejects */\n\tprivate async readIgnoredWorktree(id: string): Promise<boolean | undefined> {\n\t\tconst adminDir = joinPaths(this.commonGitDir, 'worktrees', id);\n\t\tconst content = await this.readWithTimeout(joinPaths(adminDir, 'gitdir'));\n\t\t// Git writes the path of the worktree's `.git` file, relative to the entry under `worktree.useRelativePaths`\n\t\tconst dotGit = content != null ? new TextDecoder().decode(content).trim() : '';\n\t\tconst dotGitPath = dotGit ? normalizePath(isAbsolute(dotGit) ? dotGit : joinPaths(adminDir, dotGit)) : '';\n\t\tif (!dotGitPath.endsWith('/.git')) return undefined;\n\n\t\tconst worktreePath = dirname(dotGitPath);\n\t\treturn this._ignoredWorktreeRoots.some(root => isDescendant(worktreePath, root));\n\t}\n\n\tprivate queueWorktreesBaseline(take: () => Promise<void>): void {\n\t\tthis._worktreesBaselining = true;\n\t\tconst baseline: Promise<void> = this._worktreesQueue\n\t\t\t.then(take)\n\t\t\t.catch(() => {})\n\t\t\t.finally(() => {\n\t\t\t\tif (this._worktreesBaseline === baseline) {\n\t\t\t\t\tthis._worktreesBaselining = false;\n\t\t\t\t}\n\t\t\t});\n\t\tthis._worktreesBaseline = baseline;\n\t\tthis._worktreesQueue = baseline;\n\t}\n\n\t/** A watcher of the `worktrees` directory dropped events: whatever changed meanwhile went unseen */\n\tprivate rebaselineWorktrees(): void {\n\t\tif (this._worktreesBaseline == null || this._worktreesRebaselineQueued || this._disposed) return;\n\t\t// The first baseline found no folder to ignore, and none is asked for again, so there is nothing to retake\n\t\tif (!this._worktreesBaselining && !this._ignoredWorktreeRoots.length) return;\n\n\t\tthis._worktreesRebaselineQueued = true;\n\t\tthis.queueWorktreesBaseline(async () => {\n\t\t\tthis._worktreesRebaselineQueued = false;\n\t\t\tif (this._disposed || !this._ignoredWorktreeRoots.length) return;\n\n\t\t\tawait this.listWorktrees('rebaseline');\n\t\t});\n\t}\n\n\t/** The watchers' reports of the directory pile onto one queued listing, which tells them apart */\n\tprivate queueWorktreesRescan(reason: FileWatchEvent['reason'], deliver: () => void): void {\n\t\tconst queued = this._worktreesRescanQueued;\n\t\tif (queued != null) {\n\t\t\tqueued.delivers.push(deliver);\n\t\t\tqueued.removal ||= reason === 'delete';\n\t\t\treturn;\n\t\t}\n\n\t\tconst rescan = { delivers: [deliver], removal: reason === 'delete' };\n\t\tthis._worktreesRescanQueued = rescan;\n\t\tthis._worktreesQueue = this._worktreesQueue\n\t\t\t.then(() => {\n\t\t\t\tthis._worktreesRescanQueued = undefined;\n\t\t\t\treturn this.listWorktrees('rescan', rescan.delivers, rescan.removal);\n\t\t\t})\n\t\t\t.catch(() => {});\n\t}\n\n\t/**\n\t * Lists the admin entries and compares them with the last listing. A baseline (or a rebaseline, after a watcher\n\t * dropped events) reads every entry; a rescan only those it doesn't know, reading one it can't read once more a\n\t * moment later. Git removes the directory with its last entry, so a directory that isn't there is an empty listing,\n\t * whatever the reports said of it (a poll sees its removal as a change). A listing that fails otherwise is a\n\t * baseline's empty directory, and a rescan's too when one of its reports (`delivers`) removed the directory. For any\n\t * other rescan, it is a change it can't rule out, told to the reports' own sessions\n\t */\n\tprivate async listWorktrees(\n\t\tmode: 'baseline' | 'rebaseline' | 'rescan',\n\t\tdelivers?: readonly (() => void)[],\n\t\tremoval?: boolean,\n\t): Promise<void> {\n\t\tconst readDirectory = this.readDirectory;\n\t\tif (readDirectory == null) return;\n\n\t\tconst entries = await this.withTimeout(() =>\n\t\t\treadDirectory(joinPaths(this.commonGitDir, 'worktrees')).catch((ex: unknown) => {\n\t\t\t\tif (isNotFound(ex)) return [];\n\n\t\t\t\tthrow ex;\n\t\t\t}),\n\t\t);\n\t\tif (this._disposed) return;\n\n\t\t// An admin entry is a directory, so a stray file such as `.DS_Store` is none\n\t\tconst names = entries?.filter(([, type]) => type & FileType.Directory).map(([name]) => name);\n\n\t\tif (names == null && mode === 'rescan' && !removal) {\n\t\t\tfor (const deliver of delivers ?? []) {\n\t\t\t\tdeliver();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst listing = new Set(names);\n\t\tconst previous = this._listedWorktreeIds;\n\t\tthis._listedWorktreeIds = listing;\n\n\t\tlet changed = false;\n\t\tif (mode !== 'baseline') {\n\t\t\t// An entry known only from its own reports since the last listing may be gone too\n\t\t\tfor (const id of new Set([...previous, ...this._worktreeIds.keys()])) {\n\t\t\t\tif (listing.has(id)) continue;\n\n\t\t\t\tif (this.forgetWorktreeId(id) !== true) {\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reads = await Promise.all(\n\t\t\t[...listing]\n\t\t\t\t.filter(id => mode !== 'rescan' || (!previous.has(id) && !this._worktreeIds.has(id)))\n\t\t\t\t.map(async id => {\n\t\t\t\t\tconst [ignored, known] = await this.readWorktreeId(id);\n\t\t\t\t\tif (ignored != null || mode !== 'rescan' || this._disposed) return [id, [ignored, known]] as const;\n\n\t\t\t\t\t// An entry listed before its `gitdir` is written won't be listed as new again\n\t\t\t\t\treturn [id, [await this.waitForWorktreeRetry(id), undefined]] as const;\n\t\t\t\t}),\n\t\t);\n\t\tif (this._disposed || mode === 'baseline') return;\n\n\t\tconst read = new Map(reads);\n\t\tfor (const id of listing) {\n\t\t\tconst result = read.get(id);\n\t\t\tif (!previous.has(id)) {\n\t\t\t\t// An entry whose `gitdir` still can't be read counts as real\n\t\t\t\tif ((result != null ? result[0] : this._worktreeIds.get(id)) !== true) {\n\t\t\t\t\tchanged = true;\n\t\t\t\t}\n\t\t\t} else if (result?.[0] != null && result[1] != null && result[0] !== result[1]) {\n\t\t\t\tchanged = true;\n\t\t\t}\n\t\t}\n\n\t\tif (changed) {\n\t\t\tthis.deliverWorktreesChange();\n\t\t}\n\t}\n\n\tprivate deliverWorktreesChange(): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['worktrees', 'worktreeHead'], undefined);\n\t\t}\n\t}\n\n\tprivate onConfigReported(): void {\n\t\t// The baseline may already hold the change reported, which comparing with it would lose\n\t\tif (this._configBaselining) {\n\t\t\tthis.deliverConfigChange(undefined);\n\t\t\treturn;\n\t\t}\n\t\tif (this._configReading) {\n\t\t\tthis._configRereadQueued = true;\n\t\t\treturn;\n\t\t}\n\n\t\tvoid this.readConfigChanges();\n\t}\n\n\tprivate async readConfigBaseline(): Promise<void> {\n\t\tthis._configBaselining = true;\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tconst config = await this.readConfig();\n\t\t\tif (this._disposed) return;\n\n\t\t\tthis._config = config;\n\t\t} finally {\n\t\t\tthis._configBaselining = false;\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** Reads and compares until no report came in during the last read: it may describe a change the read missed */\n\tprivate async readConfigChanges(): Promise<void> {\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tthis._configRereadQueued = false;\n\t\t\t\tconst config = await this.readConfig();\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tconst previous = this._config;\n\t\t\t\tif (config == null || previous == null) {\n\t\t\t\t\t// Unreadable now, or never readable before: the change may be anywhere. A failed read keeps the last\n\t\t\t\t\t// snapshot, so the next read still compares with what every session was told\n\t\t\t\t\tthis._config ??= config;\n\t\t\t\t\tthis.deliverConfigChange(undefined);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis._config = config;\n\t\t\t\tconst change = diffGitConfigSections(previous, config);\n\t\t\t\tif (change != null) {\n\t\t\t\t\tthis.deliverConfigChange(change);\n\t\t\t\t}\n\t\t\t} while (this._configRereadQueued);\n\t\t} finally {\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** `undefined` when the file couldn't be read or parsed, or the read didn't settle in time */\n\tprivate async readConfig(): Promise<GitConfigSections | undefined> {\n\t\tconst data = await this.readWithTimeout(joinPaths(this.commonGitDir, 'config'));\n\t\ttry {\n\t\t\treturn data != null ? parseGitConfigBytes(data) : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t/** `undefined` when the file couldn't be read, or the read didn't settle in time */\n\tprivate readWithTimeout(path: string): Promise<Uint8Array | undefined> {\n\t\tconst readFile = this.readFile;\n\t\tif (readFile == null) return Promise.resolve(undefined);\n\n\t\treturn this.withTimeout(() => readFile(path));\n\t}\n\n\t/** `undefined` when `read` rejects, or doesn't settle in time */\n\tprivate async withTimeout<T>(read: () => Promise<T>): Promise<T | undefined> {\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\treturn await Promise.race([\n\t\t\t\tread(),\n\t\t\t\tnew Promise<undefined>(resolve => {\n\t\t\t\t\ttimer = setTimeout(resolve, readTimeoutMs, undefined);\n\t\t\t\t}),\n\t\t\t]);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t}\n\n\t/**\n\t * Tells every session of a change to the shared `.git/config`: confined to some branches' sections, or (`undefined`,\n\t * or a change outside them) anywhere, which may have changed a remote's settings too. Either way no ref moved\n\t */\n\tprivate deliverConfigChange(change: GitConfigSectionsChange | undefined): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (change?.confined) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config'], false, false, change.branches);\n\t\t\t} else {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config', 'remotes'], false);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * `changes` with `worktreeHead` for a report that can stand for an entry's HEAD created or deleted with it: the\n * `worktrees` directory, an entry created or deleted, or its `gitdir` deleted. A watcher may coalesce the subtree a\n * removal deletes into one report of its parent, so no report of the HEAD comes. An entry's directory merely changed\n * stays `worktrees` alone: a watcher may report it beside each file in it, a lock's `locked` included\n */\nfunction withWorktreeEntryHead(\n\tchanges: RepositoryChange[] | undefined,\n\trelativePath: string,\n\treason: FileWatchEvent['reason'],\n): RepositoryChange[] | undefined {\n\tif (changes?.length !== 1 || changes[0] !== 'worktrees') return changes;\n\tif (relativePath === 'worktrees') return ['worktrees', 'worktreeHead'];\n\n\tconst idEnd = relativePath.indexOf('/', 10);\n\tif (idEnd === -1) return reason === 'create' || reason === 'delete' ? ['worktrees', 'worktreeHead'] : changes;\n\n\treturn reason === 'delete' && relativePath.substring(idEnd + 1) === 'gitdir'\n\t\t? ['worktrees', 'worktreeHead']\n\t\t: changes;\n}\n\n/** The path doesn't exist: Node's error, or the one VS Code's file system raises */\nfunction isNotFound(ex: unknown): boolean {\n\tconst code = (ex as { code?: unknown } | undefined)?.code;\n\treturn code === 'ENOENT' || code === 'FileNotFound';\n}\n\n/** The shared `.git/config` (or the lock git writes it through), relative to the common git dir */\nfunction isConfigFile(relativePath: string): boolean {\n\treturn relativePath === 'config' || relativePath === 'config.lock';\n}\n\n/**\n * The ref a `.git`-relative path names: a loose ref (or ref directory) under `refs/`, or the `HEAD`/`ORIG_HEAD`\n * pseudo-refs. A lock file names the ref it locks; the only ones classification lets through are the pseudo-refs' and\n * `refs/stash.lock`.\n */\nfunction getChangedRef(relativePath: string): string | false | undefined {\n\tconst ref = relativePath.endsWith('.lock') ? relativePath.substring(0, relativePath.length - 5) : relativePath;\n\t// Remote config still invalidates remote and branch caches, but doesn't itself move any ref.\n\tif (ref === 'config' || ref === 'config.worktree') return false;\n\n\treturn ref.startsWith('refs/') || ref === 'HEAD' || ref === 'ORIG_HEAD' ? ref : undefined;\n}\n"],"mappings":"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"}
|
|
@@ -61,6 +61,19 @@ export interface WatchServiceOptions {
|
|
|
61
61
|
* `excludes` natively (`supportsExcludes`). Never called for one that doesn't. A rejection keeps the static set.
|
|
62
62
|
*/
|
|
63
63
|
readonly getIgnoredDirectories?: (repoPath: string) => Promise<readonly string[]>;
|
|
64
|
+
/**
|
|
65
|
+
* Returns the absolute, normalized folders whose worktrees are ignored (see `GitProvider.getIgnoredWorktreeRoots`):
|
|
66
|
+
* with a `readFile` and a `readDirectory`, the watcher's reports of their admin entries reach no session. Asked
|
|
67
|
+
* once per common git dir, when its first session starts watching, and never without both. When absent, empty or
|
|
68
|
+
* rejected, no admin entry is read.
|
|
69
|
+
*/
|
|
70
|
+
readonly getIgnoredWorktreeRoots?: (repoPath: string) => Promise<readonly string[]>;
|
|
71
|
+
/**
|
|
72
|
+
* Returns the folders whose worktrees are ignored, relative to a repository's common git dir (see
|
|
73
|
+
* `GitProvider.getIgnoredWorktreeFolders`): the `.git` watchers whose tree holds the common git dir leave
|
|
74
|
+
* them unwatched. Asked once per common git dir, when its first session starts watching.
|
|
75
|
+
*/
|
|
76
|
+
readonly getIgnoredWorktreeFolders?: (repoPath: string) => readonly string[];
|
|
64
77
|
/** Opts into polling the watchers that report they can't be trusted. When absent, no watcher is asked. */
|
|
65
78
|
readonly polling?: WatchPollingOptions;
|
|
66
79
|
/**
|
|
@@ -70,6 +83,15 @@ export interface WatchServiceOptions {
|
|
|
70
83
|
* config change concerns everyone, as before.
|
|
71
84
|
*/
|
|
72
85
|
readonly readFile?: (path: string) => Promise<Uint8Array>;
|
|
86
|
+
/**
|
|
87
|
+
* Returns a directory's entries with their types, as the host file system's `readDirectory` does; only the
|
|
88
|
+
* directories count as admin entries (a stray `.DS_Store` is no worktree's). A rejection means it couldn't be
|
|
89
|
+
* listed, and one whose `code` is `ENOENT` or `FileNotFound` that the directory isn't there. With it and a
|
|
90
|
+
* `readFile`, the admin entries of a repository's worktrees are listed and read once as its first session starts
|
|
91
|
+
* watching, so the reports of an ignored worktree's (see `getIgnoredWorktreeRoots`) reach no session, and the others
|
|
92
|
+
* aren't held back.
|
|
93
|
+
*/
|
|
94
|
+
readonly readDirectory?: (path: string) => Promise<readonly (readonly [string, number])[]>;
|
|
73
95
|
}
|
|
74
96
|
export interface WatchHooks {
|
|
75
97
|
/** Called when FETCH_HEAD changed (for last-fetched tracking) */
|
|
@@ -125,9 +147,12 @@ export declare class RepositoryWatchService implements UnifiedDisposable {
|
|
|
125
147
|
private readonly defaultWorkingTreeMaxWaitMs?;
|
|
126
148
|
private readonly getIgnoreFilter?;
|
|
127
149
|
private readonly getIgnoredDirectories?;
|
|
150
|
+
private readonly getIgnoredWorktreeRoots?;
|
|
151
|
+
private readonly getIgnoredWorktreeFolders?;
|
|
128
152
|
private readonly provider;
|
|
129
153
|
private readonly poller;
|
|
130
154
|
private readonly readFile;
|
|
155
|
+
private readonly readDirectory;
|
|
131
156
|
private readonly sessionMap;
|
|
132
157
|
private readonly watchGroups;
|
|
133
158
|
private readonly wtWatchers;
|
|
@@ -61,9 +61,12 @@ var RepositoryWatchService = class {
|
|
|
61
61
|
defaultWorkingTreeMaxWaitMs;
|
|
62
62
|
getIgnoreFilter;
|
|
63
63
|
getIgnoredDirectories;
|
|
64
|
+
getIgnoredWorktreeRoots;
|
|
65
|
+
getIgnoredWorktreeFolders;
|
|
64
66
|
provider;
|
|
65
67
|
poller;
|
|
66
68
|
readFile;
|
|
69
|
+
readDirectory;
|
|
67
70
|
sessionMap = /* @__PURE__ */ new Map();
|
|
68
71
|
watchGroups = /* @__PURE__ */ new Map();
|
|
69
72
|
wtWatchers = /* @__PURE__ */ new Map();
|
|
@@ -78,7 +81,10 @@ var RepositoryWatchService = class {
|
|
|
78
81
|
this.defaultWorkingTreeMaxWaitMs = options.defaultWorkingTreeMaxWaitMs;
|
|
79
82
|
this.getIgnoreFilter = options.getIgnoreFilter;
|
|
80
83
|
this.getIgnoredDirectories = options.getIgnoredDirectories;
|
|
84
|
+
this.getIgnoredWorktreeRoots = options.getIgnoredWorktreeRoots;
|
|
85
|
+
this.getIgnoredWorktreeFolders = options.getIgnoredWorktreeFolders;
|
|
81
86
|
this.readFile = options.readFile;
|
|
87
|
+
this.readDirectory = options.readDirectory;
|
|
82
88
|
if (options.polling != null) this.poller = new GitDirPoller(options.polling);
|
|
83
89
|
}
|
|
84
90
|
[Symbol.dispose]() {
|
|
@@ -228,10 +234,10 @@ var RepositoryWatchService = class {
|
|
|
228
234
|
}
|
|
229
235
|
this.poller?.resume();
|
|
230
236
|
}
|
|
231
|
-
getOrCreateWatchGroup(commonGitDir) {
|
|
237
|
+
getOrCreateWatchGroup(commonGitDir, repoPath) {
|
|
232
238
|
let group = this.watchGroups.get(commonGitDir);
|
|
233
239
|
if (group == null) {
|
|
234
|
-
group = new WatchGroup(commonGitDir, this.provider, this.poller, this.readFile);
|
|
240
|
+
group = new WatchGroup(commonGitDir, this.provider, this.poller, this.readFile, this.readDirectory, this.readFile != null && this.readDirectory != null ? this.getIgnoredWorktreeRoots?.(repoPath) : void 0, this.getIgnoredWorktreeFolders?.(repoPath));
|
|
235
241
|
this.watchGroups.set(commonGitDir, group);
|
|
236
242
|
}
|
|
237
243
|
return group;
|
|
@@ -247,7 +253,7 @@ var RepositoryWatchService = class {
|
|
|
247
253
|
connectToWatchGroup(record) {
|
|
248
254
|
const { session, gitDir, watchHooks } = record;
|
|
249
255
|
const commonGitDir = (gitDir.commonUri ?? gitDir.uri).fsPath;
|
|
250
|
-
this.getOrCreateWatchGroup(commonGitDir).addSession(session.repoPath, gitDir, {
|
|
256
|
+
this.getOrCreateWatchGroup(commonGitDir, session.repoPath).addSession(session.repoPath, gitDir, {
|
|
251
257
|
onRepoChanged: (_repoPath, changes, ref, overflow, configBranches) => {
|
|
252
258
|
if (overflow) session.pushUnfencedRepoChanges(changes);
|
|
253
259
|
else session.pushRepoChanges(changes, ref, configBranches);
|
|
@@ -388,14 +394,28 @@ var RepositoryWatchService = class {
|
|
|
388
394
|
}
|
|
389
395
|
/** Creates a working-tree watcher, with the reliability it reported before returning */
|
|
390
396
|
createWorkingTreeWatcher(repoPath, onEvent, excludes) {
|
|
391
|
-
if (this.poller == null) return {
|
|
392
|
-
watcher: this.provider.createWatcher(repoPath, "**", onEvent, { excludes }),
|
|
393
|
-
reliability: "reliable"
|
|
394
|
-
};
|
|
395
397
|
let watcher;
|
|
398
|
+
let started = false;
|
|
399
|
+
const onDidStart = () => {
|
|
400
|
+
if (started || watcher == null) return;
|
|
401
|
+
started = true;
|
|
402
|
+
const entry = this.wtWatchers.get(repoPath);
|
|
403
|
+
if (entry?.watcher === watcher) entry.session.pushWorkingTreeChanges([], true);
|
|
404
|
+
};
|
|
405
|
+
if (this.poller == null) {
|
|
406
|
+
watcher = this.provider.createWatcher(repoPath, "**", onEvent, {
|
|
407
|
+
excludes,
|
|
408
|
+
onDidStart
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
watcher,
|
|
412
|
+
reliability: "reliable"
|
|
413
|
+
};
|
|
414
|
+
}
|
|
396
415
|
let reliability = "reliable";
|
|
397
416
|
const options = {
|
|
398
417
|
excludes,
|
|
418
|
+
onDidStart,
|
|
399
419
|
onDidChangeReliability: (state) => {
|
|
400
420
|
if (watcher == null) {
|
|
401
421
|
reliability = state;
|