@gitkraken/core-gitlens 0.10.6 → 0.11.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 +33 -1
- package/dist/git/cache.d.ts +34 -2
- package/dist/git/cache.js +53 -7
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +1 -1
- package/dist/git/models/repository.d.ts +3 -2
- package/dist/git/models/repository.js +5 -4
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/providers/branches.d.ts +22 -0
- package/dist/git/providers/config.d.ts +3 -1
- package/dist/git/providers/diff.d.ts +1 -0
- package/dist/git/providers/provider.d.ts +13 -1
- package/dist/git/providers/staging.d.ts +1 -1
- package/dist/git/service.d.ts +17 -1
- package/dist/git/service.js +59 -8
- package/dist/git/service.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +48 -5
- package/dist/git/watching/changeEvent.js +82 -7
- package/dist/git/watching/changeEvent.js.map +1 -1
- package/dist/git/watching/classifyChange.d.ts +13 -2
- package/dist/git/watching/classifyChange.js +40 -5
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/gitIgnoreFilter.d.ts +57 -10
- package/dist/git/watching/gitIgnoreFilter.js +186 -29
- package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
- package/dist/git/watching/initWatcher.d.ts +5 -1
- package/dist/git/watching/initWatcher.js +1 -1
- package/dist/git/watching/initWatcher.js.map +1 -1
- package/dist/git/watching/provider.d.ts +33 -8
- package/dist/git/watching/watchGroup.d.ts +13 -2
- package/dist/git/watching/watchGroup.js +60 -14
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +42 -1
- package/dist/git/watching/watchService.js +172 -20
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +50 -17
- package/dist/git/watching/watchSession.js +274 -83
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +13 -2
- package/dist/git/watching/watcherPatterns.js +22 -2
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +121 -0
- package/dist/git/watching/worktreeSummary.js +427 -0
- package/dist/git/watching/worktreeSummary.js.map +1 -0
- package/dist/git-cli/cliGitProvider.d.ts +6 -1
- package/dist/git-cli/cliGitProvider.js +87 -2
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.js +1 -1
- package/dist/git-cli/providers/blame.js +1 -1
- package/dist/git-cli/providers/branches.d.ts +32 -1
- package/dist/git-cli/providers/branches.js +195 -0
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.js +1 -1
- package/dist/git-cli/providers/config.d.ts +3 -0
- package/dist/git-cli/providers/config.js +14 -8
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.js +1 -1
- package/dist/git-cli/providers/diff.d.ts +3 -0
- package/dist/git-cli/providers/diff.js +3 -2
- package/dist/git-cli/providers/diff.js.map +1 -1
- package/dist/git-cli/providers/graph.js +1 -1
- package/dist/git-cli/providers/maintenance.js +1 -1
- package/dist/git-cli/providers/pausedOperations.js +2 -2
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.js +1 -1
- package/dist/git-cli/providers/staging.js +2 -2
- package/dist/git-cli/providers/status.js +1 -1
- package/dist/git-cli/service.js +1 -0
- package/dist/git-cli/service.js.map +1 -1
- package/dist/ipc/discovery.js +1 -1
- package/dist/ipc/ipcServer.js +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/agents/providers/gkAgentProvider.js +2 -2
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
- package/dist/plus/git-github/api/github.d.ts +1 -1
- package/dist/plus/git-github/api/github.js +2 -1
- package/dist/plus/git-github/api/github.js.map +1 -1
- package/dist/plus/git-github/providers/github/blame.js +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/context.d.ts +1 -1
- package/dist/plus/integrations/integrationService.js +1 -1
- package/dist/plus/integrations/models/integration.d.ts +1 -1
- package/dist/plus/integrations/models/integration.js +2 -2
- package/dist/plus/integrations/models/integration.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.js +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +2 -0
- package/dist/plus/integrations/providers/jira.js +5 -0
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/linear.d.ts +7 -0
- package/dist/plus/integrations/providers/linear.js +19 -0
- package/dist/plus/integrations/providers/linear.js.map +1 -1
- package/dist/plus/integrations/providers/trello.d.ts +8 -0
- package/dist/plus/integrations/providers/trello.js +22 -0
- package/dist/plus/integrations/providers/trello.js.map +1 -1
- package/dist/plus/integrations/reads/currentAccount.d.ts +1 -1
- package/dist/plus/integrations/reads/currentAccount.js +1 -1
- package/dist/plus/integrations/reads/currentAccount.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +1 -0
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/utils/env/node/exec.js +2 -2
- package/dist/utils/event.d.ts +1 -1
- package/docs/integrations.md +6 -6
- package/docs/kepler-read-api-parity.md +2 -2
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchSession.js","names":[],"sources":["../../../../git/src/watching/watchSession.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport type { RepositoryChange } from '../models/repository.js';\nimport type { WorkingTreeChangeEvent } from './changeEvent.js';\nimport { WatcherRepoChangeEvent } from './changeEvent.js';\n\nexport interface RepositorySubscription extends UnifiedDisposable {\n\treadonly onDidChange: Event<WatcherRepoChangeEvent>;\n}\n\nexport interface WorkingTreeSubscription extends UnifiedDisposable {\n\treadonly onDidChangeWorkingTree: Event<WorkingTreeChangeEvent>;\n}\n\n/**\n * Lifecycle callbacks fired by the session when subscriber counts\n * cross the 0↔1 boundary. The host (WatchService) uses these to\n * lazily create/destroy watchers.\n */\nexport interface WatchSessionLifecycle {\n\t/** Called when the first repo-change subscriber joins */\n\tonFirstRepoSubscriber?(): void;\n\t/** Called when the last repo-change subscriber leaves */\n\tonLastRepoSubscriber?(): void;\n\t/** Called when the first working-tree subscriber joins */\n\tonFirstWorkingTreeSubscriber?(): void;\n\t/** Called when the last working-tree subscriber leaves */\n\tonLastWorkingTreeSubscriber?(): void;\n}\n\nexport interface WatchSessionOptions {\n\treadonly repoPath: string;\n\t/** Default debounce for repo changes. Default: 250ms */\n\treadonly defaultRepoDelayMs?: number;\n\t/** Default debounce for working tree changes. Default: 2500ms */\n\treadonly defaultWorkingTreeDelayMs?: number;\n\treadonly lifecycle?: WatchSessionLifecycle;\n\t/** Initial suspended state — new sessions inherit the service-global suspend state. Default: false */\n\treadonly suspended?: boolean;\n\t/**\n\t * Called whenever a debounced repo change event is dispatched to subscribers.\n\t * Does NOT count as a subscriber — used by WatchService for\n\t * global event forwarding without affecting lifecycle callbacks.\n\t */\n\treadonly onDidFireRepoChange?: (event: WatcherRepoChangeEvent) => void;\n\t/**\n\t * Called whenever a debounced working-tree change is dispatched to subscribers. Like\n\t * {@link onDidFireRepoChange}, does NOT count as a subscriber — used by WatchService for global\n\t * event forwarding (the single point that drives the cache's status clock for working-tree edits).\n\t */\n\treadonly onDidFireWorkingTreeChange?: (repoPath: string) => void;\n}\n\nconst defaultRepoDelay = 250;\nconst defaultWorkingTreeDelay = 2500;\n\n/**\n * Cap on retained working-tree paths per flush. The set otherwise grows without bound — while the window\n * is unfocused the session is suspended, so a branch switch or build accumulates every touched path until\n * refocus. Set low deliberately: ordinary edits are a handful of paths, anything past this was a\n * refresh-everything event anyway, and dropping past it only costs the matching consumers a\n * conservative refresh.\n */\nconst maxWorkingTreePaths = 1000;\n\n/**\n * Per-repository session that owns the debounce/coalesce/suspend\n * pipeline for both repo (.git) and working-tree changes.\n *\n * Multiple subscribers share the same pipeline — the shortest\n * requested debounce delay wins.\n */\nexport class RepositoryWatchSession implements UnifiedDisposable {\n\treadonly repoPath: string;\n\n\tprivate readonly repoEmitter = new Emitter<WatcherRepoChangeEvent>();\n\tprivate readonly repoDelays = new Map<number, number>(); // subscriberId → delayMs\n\tprivate repoNextId = 0;\n\tprivate pendingRepoEvent: WatcherRepoChangeEvent | undefined;\n\tprivate repoTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate effectiveRepoMs: number;\n\tprivate readonly defaultRepoMs: number;\n\n\tprivate readonly workingTreeEmitter = new Emitter<WorkingTreeChangeEvent>();\n\tprivate readonly workingTreeDelays = new Map<number, number>();\n\tprivate workingTreeNextId = 0;\n\tprivate pendingWorkingTreePaths = new Set<string>();\n\tprivate pendingWorkingTreeIncomplete = false;\n\tprivate workingTreeTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate effectiveWorkingTreeMs: number;\n\tprivate readonly defaultWorkingTreeMs: number;\n\n\tprivate _etagWorkingTree = 0;\n\tprivate _suspended: boolean;\n\tprivate _disposed = false;\n\n\tprivate readonly lifecycle?: WatchSessionLifecycle;\n\tprivate readonly onDidFireRepoChangeCallback?: (event: WatcherRepoChangeEvent) => void;\n\tprivate readonly onDidFireWorkingTreeChangeCallback?: (repoPath: string) => void;\n\n\tconstructor(options: WatchSessionOptions) {\n\t\tthis.repoPath = options.repoPath;\n\t\tthis.defaultRepoMs = options.defaultRepoDelayMs ?? defaultRepoDelay;\n\t\tthis.defaultWorkingTreeMs = options.defaultWorkingTreeDelayMs ?? defaultWorkingTreeDelay;\n\t\tthis.effectiveRepoMs = this.defaultRepoMs;\n\t\tthis.effectiveWorkingTreeMs = this.defaultWorkingTreeMs;\n\t\tthis.lifecycle = options.lifecycle;\n\t\tthis.onDidFireRepoChangeCallback = options.onDidFireRepoChange;\n\t\tthis.onDidFireWorkingTreeChangeCallback = options.onDidFireWorkingTreeChange;\n\t\tthis._suspended = options.suspended ?? false;\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\n\t\tthis.cancelRepoTimer();\n\t\tthis.cancelWorkingTreeTimer();\n\n\t\tthis.pendingRepoEvent = undefined;\n\t\tthis.pendingWorkingTreePaths.clear();\n\t\tthis.pendingWorkingTreeIncomplete = false;\n\t\tthis.repoDelays.clear();\n\t\tthis.workingTreeDelays.clear();\n\n\t\tthis.repoEmitter.dispose();\n\t\tthis.workingTreeEmitter.dispose();\n\t}\n\n\tget suspended(): boolean {\n\t\treturn this._suspended;\n\t}\n\n\tget hasPendingChanges(): boolean {\n\t\treturn this.pendingRepoEvent != null || this.hasPendingWorkingTreeChanges;\n\t}\n\n\t/**\n\t * An incomplete batch with no surviving paths still has to fire: the caller dropped paths we never saw, so\n\t * gating the pipeline on the retained set alone would swallow the change entirely.\n\t */\n\tprivate get hasPendingWorkingTreeChanges(): boolean {\n\t\treturn this.pendingWorkingTreePaths.size > 0 || this.pendingWorkingTreeIncomplete;\n\t}\n\n\tget repoSubscriberCount(): number {\n\t\treturn this.repoDelays.size;\n\t}\n\n\tget workingTreeSubscriberCount(): number {\n\t\treturn this.workingTreeDelays.size;\n\t}\n\n\t/**\n\t * Monotonic counter incremented immediately when working tree changes\n\t * are pushed (before debounce). Used by `GitCommit.hasFullDetails()`\n\t * to detect staleness of uncommitted commit data.\n\t */\n\tget etagWorkingTree(): number {\n\t\treturn this._etagWorkingTree;\n\t}\n\n\t/**\n\t * Subscribe to .git directory changes with configurable debounce.\n\t * First subscribe fires `onFirstRepoSubscriber`, last dispose\n\t * fires `onLastRepoSubscriber`.\n\t *\n\t * When multiple subscribers exist, the shortest debounce wins —\n\t * all subscribers fire on the same (fastest) cadence.\n\t */\n\tsubscribe(opts?: { delayMs?: number }): RepositorySubscription {\n\t\tif (this._disposed) {\n\t\t\treturn Object.assign(\n\t\t\t\tcreateDisposable(() => {}),\n\t\t\t\t{\n\t\t\t\t\tonDidChange: this.repoEmitter.event,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.repoNextId++;\n\t\tconst delay = opts?.delayMs ?? this.defaultRepoMs;\n\t\tconst wasEmpty = this.repoDelays.size === 0;\n\t\tconst prevDelay = this.effectiveRepoMs;\n\n\t\tthis.repoDelays.set(id, delay);\n\t\tthis.recalcRepoDelay();\n\n\t\tif (wasEmpty) {\n\t\t\tthis.lifecycle?.onFirstRepoSubscriber?.();\n\t\t}\n\n\t\t// If the effective delay got shorter while a timer is running, reschedule sooner\n\t\tif (this.effectiveRepoMs < prevDelay && this.repoTimer != null) {\n\t\t\tthis.scheduleRepoFlush();\n\t\t}\n\n\t\tlet subDisposed = false;\n\t\treturn Object.assign(\n\t\t\tcreateDisposable(() => {\n\t\t\t\tif (subDisposed) return;\n\n\t\t\t\tsubDisposed = true;\n\n\t\t\t\tthis.repoDelays.delete(id);\n\t\t\t\tthis.recalcRepoDelay();\n\n\t\t\t\tif (this.repoDelays.size === 0) {\n\t\t\t\t\tthis.cancelRepoTimer();\n\t\t\t\t\tthis.pendingRepoEvent = undefined;\n\t\t\t\t\tthis.lifecycle?.onLastRepoSubscriber?.();\n\t\t\t\t}\n\t\t\t}),\n\t\t\t{ onDidChange: this.repoEmitter.event },\n\t\t);\n\t}\n\n\tsubscribeToWorkingTree(opts?: { delayMs?: number }): WorkingTreeSubscription {\n\t\tif (this._disposed) {\n\t\t\treturn Object.assign(\n\t\t\t\tcreateDisposable(() => {}),\n\t\t\t\t{\n\t\t\t\t\tonDidChangeWorkingTree: this.workingTreeEmitter.event,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.workingTreeNextId++;\n\t\tconst delay = opts?.delayMs ?? this.defaultWorkingTreeMs;\n\t\tconst wasEmpty = this.workingTreeDelays.size === 0;\n\t\tconst prevDelay = this.effectiveWorkingTreeMs;\n\n\t\tthis.workingTreeDelays.set(id, delay);\n\t\tthis.recalcWorkingTreeDelay();\n\n\t\tif (wasEmpty) {\n\t\t\tthis.lifecycle?.onFirstWorkingTreeSubscriber?.();\n\t\t}\n\n\t\t// If the effective delay got shorter while a timer is running, reschedule sooner\n\t\tif (this.effectiveWorkingTreeMs < prevDelay && this.workingTreeTimer != null) {\n\t\t\tthis.scheduleWorkingTreeFlush();\n\t\t}\n\n\t\tlet subDisposed = false;\n\t\treturn Object.assign(\n\t\t\tcreateDisposable(() => {\n\t\t\t\tif (subDisposed) return;\n\n\t\t\t\tsubDisposed = true;\n\n\t\t\t\tthis.workingTreeDelays.delete(id);\n\t\t\t\tthis.recalcWorkingTreeDelay();\n\n\t\t\t\tif (this.workingTreeDelays.size === 0) {\n\t\t\t\t\tthis.cancelWorkingTreeTimer();\n\t\t\t\t\tthis.pendingWorkingTreePaths.clear();\n\t\t\t\t\t// Dropped with the paths it describes — a stale flag would make the next subscriber's\n\t\t\t\t\t// first ordinary flush claim a partial set and force needless full refreshes.\n\t\t\t\t\tthis.pendingWorkingTreeIncomplete = false;\n\t\t\t\t\tthis.lifecycle?.onLastWorkingTreeSubscriber?.();\n\t\t\t\t}\n\t\t\t}),\n\t\t\t{ onDidChangeWorkingTree: this.workingTreeEmitter.event },\n\t\t);\n\t}\n\n\tsuspend(): void {\n\t\tthis._suspended = true;\n\t\t// Cancel active timers — events continue to coalesce but don't fire\n\t\tthis.cancelRepoTimer();\n\t\tthis.cancelWorkingTreeTimer();\n\t}\n\n\tresume(delayMs?: number): void {\n\t\tif (!this._suspended) return;\n\n\t\tthis._suspended = false;\n\n\t\t// Flush accumulated changes\n\t\tif (this.pendingRepoEvent != null) {\n\t\t\tthis.scheduleRepoFlush(delayMs);\n\t\t}\n\t\tif (this.hasPendingWorkingTreeChanges) {\n\t\t\tthis.scheduleWorkingTreeFlush(delayMs);\n\t\t}\n\t}\n\n\t/** Inject changes (e.g., after a git operation the caller performed). Goes through debounce + suspend. */\n\tfireChange(...changes: RepositoryChange[]): void {\n\t\tif (this._disposed || changes.length === 0) return;\n\n\t\tthis.coalesceRepo(changes);\n\t}\n\n\t/** Immediate fire, bypassing debounce + suspend (for Closed/Opened). */\n\tfireChangeImmediate(...changes: RepositoryChange[]): void {\n\t\tif (this._disposed || changes.length === 0) return;\n\n\t\tconst event = new WatcherRepoChangeEvent(this.repoPath, changes);\n\t\t// Same contract as `flushRepo`: notify the global forwarder (which advances the cache's status clock) BEFORE\n\t\t// subscribers. Skipping it here would route changes to subscribers that no one advanced the clock for —\n\t\t// subscribers see them as session-routed and (correctly) don't re-advance, so the cache would go stale.\n\t\tthis.onDidFireRepoChangeCallback?.(event);\n\t\tthis.repoEmitter.fire(event);\n\t}\n\n\t/** Push interpreted repo changes into the pipeline */\n\tpushRepoChanges(changes: RepositoryChange[]): void {\n\t\tif (this._disposed) return;\n\n\t\tthis.coalesceRepo(changes);\n\t}\n\n\t/** Push working tree file changes into the pipeline */\n\t/** `incomplete` marks the batch as partial when the caller already dropped paths of its own. */\n\tpushWorkingTreeChanges(paths: Iterable<string>, incomplete?: boolean): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._etagWorkingTree++;\n\t\tif (incomplete) {\n\t\t\tthis.pendingWorkingTreeIncomplete = true;\n\t\t}\n\t\tthis.coalesceWorkingTrees(paths);\n\t}\n\n\tprivate static minDelay(delays: Map<number, number>, fallback: number): number {\n\t\tif (delays.size === 0) return fallback;\n\n\t\tlet min = fallback;\n\t\tfor (const d of delays.values()) {\n\t\t\tif (d < min) {\n\t\t\t\tmin = d;\n\t\t\t}\n\t\t}\n\t\treturn min;\n\t}\n\n\tprivate recalcRepoDelay(): void {\n\t\tthis.effectiveRepoMs = RepositoryWatchSession.minDelay(this.repoDelays, this.defaultRepoMs);\n\t}\n\n\tprivate recalcWorkingTreeDelay(): void {\n\t\tthis.effectiveWorkingTreeMs = RepositoryWatchSession.minDelay(\n\t\t\tthis.workingTreeDelays,\n\t\t\tthis.defaultWorkingTreeMs,\n\t\t);\n\t}\n\n\tprivate flushRepo(): void {\n\t\tthis.repoTimer = undefined;\n\t\tconst event = this.pendingRepoEvent;\n\t\tif (event == null) return;\n\n\t\tthis.pendingRepoEvent = undefined;\n\t\t// Advance any cache clock BEFORE notifying subscribers (same rationale as `flushWorkingTree`): a subscriber\n\t\t// that synchronously reads status must see the post-change generation, not a joinable pre-change run.\n\t\tthis.onDidFireRepoChangeCallback?.(event);\n\t\tthis.repoEmitter.fire(event);\n\t}\n\n\tprivate scheduleRepoFlush(delayOverride?: number): void {\n\t\tif (this._suspended || this.pendingRepoEvent == null) return;\n\n\t\tif (this.repoTimer != null) {\n\t\t\tclearTimeout(this.repoTimer);\n\t\t}\n\t\tthis.repoTimer = setTimeout(() => this.flushRepo(), delayOverride ?? this.effectiveRepoMs);\n\t}\n\n\tprivate coalesceRepo(changes: RepositoryChange[]): void {\n\t\tif (changes.length === 0) return;\n\n\t\tthis.pendingRepoEvent =\n\t\t\tthis.pendingRepoEvent == null\n\t\t\t\t? new WatcherRepoChangeEvent(this.repoPath, changes)\n\t\t\t\t: this.pendingRepoEvent.with(changes);\n\n\t\tthis.scheduleRepoFlush();\n\t}\n\n\tprivate flushWorkingTree(): void {\n\t\tthis.workingTreeTimer = undefined;\n\t\tif (!this.hasPendingWorkingTreeChanges) return;\n\n\t\tconst paths = this.pendingWorkingTreePaths;\n\t\tconst incomplete = this.pendingWorkingTreeIncomplete;\n\t\tthis.pendingWorkingTreePaths = new Set();\n\t\tthis.pendingWorkingTreeIncomplete = false;\n\t\t// Advance the cache's status clock (via WatchService's global forwarding) BEFORE notifying subscribers,\n\t\t// so a subscriber that synchronously reads status sees the post-change generation, not a joinable pre-change run.\n\t\tthis.onDidFireWorkingTreeChangeCallback?.(this.repoPath);\n\t\tthis.workingTreeEmitter.fire({ repoPath: this.repoPath, paths: paths, incomplete: incomplete });\n\t}\n\n\tprivate scheduleWorkingTreeFlush(delayOverride?: number): void {\n\t\tif (this._suspended || !this.hasPendingWorkingTreeChanges) return;\n\n\t\tif (this.workingTreeTimer != null) {\n\t\t\tclearTimeout(this.workingTreeTimer);\n\t\t}\n\t\tthis.workingTreeTimer = setTimeout(() => this.flushWorkingTree(), delayOverride ?? this.effectiveWorkingTreeMs);\n\t}\n\n\tprivate coalesceWorkingTrees(paths: Iterable<string>): void {\n\t\tfor (const p of paths) {\n\t\t\t// Stop retaining paths past the cap, but keep draining the iterable — the flag is what makes the\n\t\t\t// partial set safe to consume, and dropping out early would leave later paths for the next window.\n\t\t\tif (this.pendingWorkingTreePaths.size >= maxWorkingTreePaths && !this.pendingWorkingTreePaths.has(p)) {\n\t\t\t\tthis.pendingWorkingTreeIncomplete = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.pendingWorkingTreePaths.add(p);\n\t\t}\n\t\tthis.scheduleWorkingTreeFlush();\n\t}\n\n\tprivate cancelRepoTimer(): void {\n\t\tif (this.repoTimer != null) {\n\t\t\tclearTimeout(this.repoTimer);\n\t\t\tthis.repoTimer = undefined;\n\t\t}\n\t}\n\n\tprivate cancelWorkingTreeTimer(): void {\n\t\tif (this.workingTreeTimer != null) {\n\t\t\tclearTimeout(this.workingTreeTimer);\n\t\t\tthis.workingTreeTimer = undefined;\n\t\t}\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"watchSession.js","names":[],"sources":["../../../../git/src/watching/watchSession.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport type { RepositoryChange } from '../models/repository.js';\nimport type { RefChangeKind, WorkingTreeChangeEvent } from './changeEvent.js';\nimport { findCoveringRefName, getRefChangeKind, isRefChangeKind, WatcherRepoChangeEvent } from './changeEvent.js';\n\nexport interface RepositorySubscription extends UnifiedDisposable {\n\treadonly onDidChange: Event<WatcherRepoChangeEvent>;\n}\n\nexport interface WorkingTreeSubscription extends UnifiedDisposable {\n\treadonly onDidChangeWorkingTree: Event<WorkingTreeChangeEvent>;\n}\n\n/**\n * Lifecycle callbacks fired by the session when subscriber counts\n * cross the 0↔1 boundary. The host (WatchService) uses these to\n * lazily create/destroy watchers.\n */\nexport interface WatchSessionLifecycle {\n\t/** Called when the first repo-change subscriber joins */\n\tonFirstRepoSubscriber?(): void;\n\t/** Called when the last repo-change subscriber leaves */\n\tonLastRepoSubscriber?(): void;\n\t/** Called when the first working-tree subscriber joins */\n\tonFirstWorkingTreeSubscriber?(): void;\n\t/** Called when the last working-tree subscriber leaves */\n\tonLastWorkingTreeSubscriber?(): void;\n}\n\nexport interface WatchSessionOptions {\n\treadonly repoPath: string;\n\t/** Default debounce for repo changes. Default: 250ms */\n\treadonly defaultRepoDelayMs?: number;\n\t/** Default debounce for working tree changes. Default: 2500ms */\n\treadonly defaultWorkingTreeDelayMs?: number;\n\t/**\n\t * Default cap on how long repo changes can be held back by continuous activity — a flush happens at most this long\n\t * after the first pending change, however long the burst lasts. `Infinity` disables the cap. Default: 2000ms\n\t */\n\treadonly defaultRepoMaxWaitMs?: number;\n\t/** Default cap on how long working tree changes can be held back by continuous activity. `Infinity` disables the cap. Default: 10000ms */\n\treadonly defaultWorkingTreeMaxWaitMs?: number;\n\treadonly lifecycle?: WatchSessionLifecycle;\n\t/** Initial suspended state — new sessions inherit the service-global suspend state. Default: false */\n\treadonly suspended?: boolean;\n\t/**\n\t * Called whenever a debounced repo change event is dispatched to subscribers.\n\t * Does NOT count as a subscriber — used by WatchService for\n\t * global event forwarding without affecting lifecycle callbacks.\n\t */\n\treadonly onDidFireRepoChange?: (event: WatcherRepoChangeEvent) => void;\n\t/**\n\t * Called whenever a debounced working-tree change is dispatched to subscribers. Like\n\t * {@link onDidFireRepoChange}, does NOT count as a subscriber — used by WatchService for global\n\t * event forwarding (the single point that drives the cache's status clock for working-tree edits).\n\t */\n\treadonly onDidFireWorkingTreeChange?: (repoPath: string) => void;\n}\n\nconst defaultRepoDelay = 250;\nconst defaultWorkingTreeDelay = 2500;\nconst defaultRepoMaxWait = 2000;\nconst defaultWorkingTreeMaxWait = 10000;\n\n/** Largest delay `setTimeout` honors (a signed 32-bit int); anything longer fires immediately */\nconst maxTimerDelay = 0x7fffffff;\n\ninterface DebounceTiming {\n\treadonly delayMs: number;\n\treadonly maxWaitMs: number;\n}\n\n/**\n * Trailing-deadline debounce with a max-wait, shared by the repo and working-tree pipelines.\n *\n * Recording an event costs a `performance.now()` and, only when no timer is armed, one `setTimeout` — never a\n * `clearTimeout`/`setTimeout` pair per event, which is what made a busy repo allocate (and starve) on every raw\n * watcher event. The armed timer is a deadline, not a countdown: when it fires it recomputes the due time from the\n * latest timestamps and either flushes or re-arms for the remainder. The due time is\n * `min(lastEventAt + delay, firstPendingAt + maxWait)`, so continuous activity is flushed at the max-wait instead of\n * never. Monotonic time, so a wall-clock change can't push a deadline out.\n */\nclass DebounceClock {\n\tprivate active = false;\n\tprivate paused = false;\n\tprivate firstAt = 0;\n\tprivate lastAt = 0;\n\tprivate timer: ReturnType<typeof setTimeout> | undefined;\n\tprivate timerDueAt = 0;\n\t/** Absolute flush time set by {@link resume}; dropped by the next event, which returns to normal debounce */\n\tprivate resumeDueAt: number | undefined;\n\n\tconstructor(\n\t\tprivate readonly getDelayMs: () => number,\n\t\tprivate readonly getMaxWaitMs: () => number,\n\t\tprivate readonly onDue: () => void,\n\t) {}\n\n\t/** Records an event; arms the timer only if none is armed. Cheap enough to call per raw event. */\n\tnote(): void {\n\t\tconst now = performance.now();\n\t\tif (!this.active) {\n\t\t\tthis.active = true;\n\t\t\tthis.firstAt = now;\n\t\t}\n\t\tthis.lastAt = now;\n\n\t\tif (this.paused) return;\n\n\t\tif (this.resumeDueAt != null) {\n\t\t\t// The first event after a resume goes back to normal debounce — which may be due sooner than the resume deadline\n\t\t\tthis.resumeDueAt = undefined;\n\t\t\tthis.rearmIfEarlier();\n\t\t}\n\n\t\tif (this.timer == null) {\n\t\t\tthis.arm(this.getDueAt(), now);\n\t\t}\n\t}\n\n\t/** Moves the armed timer earlier when the due time shrank (a subscriber joined with a shorter delay or max-wait) */\n\trearmIfEarlier(): void {\n\t\tif (!this.active || this.paused || this.timer == null) return;\n\n\t\tconst dueAt = this.getDueAt();\n\t\tif (dueAt >= this.timerDueAt) return;\n\n\t\tclearTimeout(this.timer);\n\t\tthis.arm(dueAt, performance.now());\n\t}\n\n\t/** Cancels the timer; events keep accumulating but nothing is due until {@link resume} */\n\tsuspend(): void {\n\t\tthis.paused = true;\n\t\tthis.clearTimer();\n\t}\n\n\t/**\n\t * Restarts the max-wait window from now and schedules the flush for `delayMs` from now (the effective delay when\n\t * omitted). Does nothing if no events are pending.\n\t */\n\tresume(delayMs?: number): void {\n\t\tthis.paused = false;\n\t\tif (!this.active) return;\n\n\t\tconst now = performance.now();\n\t\tthis.firstAt = now;\n\t\tthis.resumeDueAt = now + (delayMs ?? this.getDelayMs());\n\t\tthis.clearTimer();\n\t\tthis.arm(this.resumeDueAt, now);\n\t}\n\n\t/** Drops the pending window and the timer */\n\tcancel(): void {\n\t\tthis.active = false;\n\t\tthis.resumeDueAt = undefined;\n\t\tthis.clearTimer();\n\t}\n\n\tprivate getDueAt(): number {\n\t\treturn this.resumeDueAt ?? Math.min(this.lastAt + this.getDelayMs(), this.firstAt + this.getMaxWaitMs());\n\t}\n\n\tprivate arm(dueAt: number, now: number): void {\n\t\tthis.timerDueAt = dueAt;\n\t\tthis.timer = setTimeout(() => this.onTimer(), Math.min(Math.max(0, dueAt - now), maxTimerDelay));\n\t}\n\n\tprivate onTimer(): void {\n\t\tthis.timer = undefined;\n\t\tif (!this.active || this.paused) return;\n\n\t\tconst now = performance.now();\n\t\tconst dueAt = this.getDueAt();\n\t\tif (dueAt > now) {\n\t\t\tthis.arm(dueAt, now);\n\t\t\treturn;\n\t\t}\n\n\t\t// Reset before firing so an event raised by a subscriber starts a fresh window\n\t\tthis.cancel();\n\t\tthis.onDue();\n\t}\n\n\tprivate clearTimer(): void {\n\t\tif (this.timer != null) {\n\t\t\tclearTimeout(this.timer);\n\t\t\tthis.timer = undefined;\n\t\t}\n\t}\n}\n\n/**\n * Cap on retained working-tree paths per flush. The set otherwise grows without bound — while the window\n * is unfocused the session is suspended, so a branch switch or build accumulates every touched path until\n * refocus. Set low deliberately: ordinary edits are a handful of paths, anything past this was a\n * refresh-everything event anyway, and dropping past it only costs the matching consumers a\n * conservative refresh.\n */\nconst maxWorkingTreePaths = 1000;\n\n/**\n * Cap on retained ref names per flush, for the same reason (a fetch of many refs while suspended). Past it, a ref kind\n * is recorded unnamed — any ref of that kind may have moved — which only makes `mayHaveChangedRef` more conservative.\n */\nconst maxPendingRefNames = 1000;\n\n/**\n * Per-repository session that owns the debounce/coalesce/suspend\n * pipeline for both repo (.git) and working-tree changes.\n *\n * Multiple subscribers share the same pipeline — the shortest\n * requested debounce delay wins, and so does the shortest max-wait.\n */\nexport class RepositoryWatchSession implements UnifiedDisposable {\n\treadonly repoPath: string;\n\n\tprivate readonly repoEmitter = new Emitter<WatcherRepoChangeEvent>();\n\tprivate readonly repoSubscribers = new Map<number, DebounceTiming>(); // subscriberId → timing\n\tprivate repoNextId = 0;\n\tprivate readonly pendingRepoChanges = new Set<RepositoryChange>();\n\t/** Full ref names, ref directories and pseudo-refs whose files changed, flushed with {@link pendingRepoChanges} */\n\tprivate readonly pendingRepoRefs = new Set<string>();\n\t/** Ref kinds that changed without a name, flushed with {@link pendingRepoChanges} */\n\tprivate readonly pendingRepoUnnamedRefKinds = new Set<RefChangeKind>();\n\tprivate readonly repoClock: DebounceClock;\n\tprivate effectiveRepoMs: number;\n\tprivate effectiveRepoMaxWaitMs: number;\n\tprivate readonly defaultRepoMs: number;\n\tprivate readonly defaultRepoMaxWaitMs: number;\n\n\tprivate readonly workingTreeEmitter = new Emitter<WorkingTreeChangeEvent>();\n\tprivate readonly workingTreeSubscribers = new Map<number, DebounceTiming>();\n\tprivate workingTreeNextId = 0;\n\tprivate pendingWorkingTreePaths = new Set<string>();\n\tprivate pendingWorkingTreeIncomplete = false;\n\tprivate readonly workingTreeClock: DebounceClock;\n\tprivate effectiveWorkingTreeMs: number;\n\tprivate effectiveWorkingTreeMaxWaitMs: number;\n\tprivate readonly defaultWorkingTreeMs: number;\n\tprivate readonly defaultWorkingTreeMaxWaitMs: number;\n\n\tprivate _etagWorkingTree = 0;\n\tprivate _suspended: boolean;\n\tprivate _disposed = false;\n\n\tprivate readonly lifecycle?: WatchSessionLifecycle;\n\tprivate readonly onDidFireRepoChangeCallback?: (event: WatcherRepoChangeEvent) => void;\n\tprivate readonly onDidFireWorkingTreeChangeCallback?: (repoPath: string) => void;\n\n\tconstructor(options: WatchSessionOptions) {\n\t\tthis.repoPath = options.repoPath;\n\t\tthis.defaultRepoMs = options.defaultRepoDelayMs ?? defaultRepoDelay;\n\t\tthis.defaultWorkingTreeMs = options.defaultWorkingTreeDelayMs ?? defaultWorkingTreeDelay;\n\t\tthis.defaultRepoMaxWaitMs = options.defaultRepoMaxWaitMs ?? defaultRepoMaxWait;\n\t\tthis.defaultWorkingTreeMaxWaitMs = options.defaultWorkingTreeMaxWaitMs ?? defaultWorkingTreeMaxWait;\n\t\tthis.effectiveRepoMs = this.defaultRepoMs;\n\t\tthis.effectiveRepoMaxWaitMs = this.defaultRepoMaxWaitMs;\n\t\tthis.effectiveWorkingTreeMs = this.defaultWorkingTreeMs;\n\t\tthis.effectiveWorkingTreeMaxWaitMs = this.defaultWorkingTreeMaxWaitMs;\n\t\tthis.repoClock = new DebounceClock(\n\t\t\t() => this.effectiveRepoMs,\n\t\t\t() => this.effectiveRepoMaxWaitMs,\n\t\t\t() => this.flushRepo(),\n\t\t);\n\t\tthis.workingTreeClock = new DebounceClock(\n\t\t\t() => this.effectiveWorkingTreeMs,\n\t\t\t() => this.effectiveWorkingTreeMaxWaitMs,\n\t\t\t() => this.flushWorkingTree(),\n\t\t);\n\t\tthis.lifecycle = options.lifecycle;\n\t\tthis.onDidFireRepoChangeCallback = options.onDidFireRepoChange;\n\t\tthis.onDidFireWorkingTreeChangeCallback = options.onDidFireWorkingTreeChange;\n\t\tthis._suspended = options.suspended ?? false;\n\t\tif (this._suspended) {\n\t\t\tthis.repoClock.suspend();\n\t\t\tthis.workingTreeClock.suspend();\n\t\t}\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\n\t\tthis.repoClock.cancel();\n\t\tthis.workingTreeClock.cancel();\n\n\t\tthis.clearPendingRepoChanges();\n\t\tthis.pendingWorkingTreePaths.clear();\n\t\tthis.pendingWorkingTreeIncomplete = false;\n\t\tthis.repoSubscribers.clear();\n\t\tthis.workingTreeSubscribers.clear();\n\n\t\tthis.repoEmitter.dispose();\n\t\tthis.workingTreeEmitter.dispose();\n\t}\n\n\tget suspended(): boolean {\n\t\treturn this._suspended;\n\t}\n\n\tget hasPendingChanges(): boolean {\n\t\treturn this.pendingRepoChanges.size > 0 || this.hasPendingWorkingTreeChanges;\n\t}\n\n\t/**\n\t * An incomplete batch with no surviving paths still has to fire: the caller dropped paths we never saw, so\n\t * gating the pipeline on the retained set alone would swallow the change entirely.\n\t */\n\tprivate get hasPendingWorkingTreeChanges(): boolean {\n\t\treturn this.pendingWorkingTreePaths.size > 0 || this.pendingWorkingTreeIncomplete;\n\t}\n\n\tget repoSubscriberCount(): number {\n\t\treturn this.repoSubscribers.size;\n\t}\n\n\tget workingTreeSubscriberCount(): number {\n\t\treturn this.workingTreeSubscribers.size;\n\t}\n\n\t/**\n\t * Monotonic counter incremented immediately when working tree changes\n\t * are pushed (before debounce). Used by `GitCommit.hasFullDetails()`\n\t * to detect staleness of uncommitted commit data.\n\t */\n\tget etagWorkingTree(): number {\n\t\treturn this._etagWorkingTree;\n\t}\n\n\t/**\n\t * Subscribe to .git directory changes with configurable debounce.\n\t * First subscribe fires `onFirstRepoSubscriber`, last dispose\n\t * fires `onLastRepoSubscriber`.\n\t *\n\t * When multiple subscribers exist, the shortest debounce wins —\n\t * all subscribers fire on the same (fastest) cadence. `maxWaitMs` bounds how long continuous\n\t * activity can defer a flush (the shortest wins too); `Infinity` means no cap.\n\t *\n\t * `relevantRefs` narrows THIS subscription's events to the refs it cares about, given as full ref names\n\t * (`refs/heads/main`, `refs/remotes/origin/main`, `refs/tags/v1`, `refs/stash`). It is read at delivery, so it can\n\t * return the subscriber's current list. A `heads`/`remotes`/`tags`/`stash` change is delivered only when a listed\n\t * ref of that kind may have moved, when the change isn't attributable to a name (e.g. a `packed-refs` rewrite),\n\t * or, for `heads`, when a pseudo-ref (`HEAD`, `ORIG_HEAD`) moved — so `stash` and `tags` changes are dropped unless\n\t * a ref of that kind is listed. Other change kinds always pass, and an event left with no changes isn't delivered.\n\t * The filter never affects other subscribers, the debounce, or the global `onDidFireRepoChange` forwarding.\n\t */\n\tsubscribe(opts?: {\n\t\tdelayMs?: number;\n\t\tmaxWaitMs?: number;\n\t\trelevantRefs?: () => Iterable<string>;\n\t}): RepositorySubscription {\n\t\tif (this._disposed) {\n\t\t\treturn Object.assign(\n\t\t\t\tcreateDisposable(() => {}),\n\t\t\t\t{\n\t\t\t\t\tonDidChange: this.repoEmitter.event,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.repoNextId++;\n\t\tconst wasEmpty = this.repoSubscribers.size === 0;\n\n\t\tthis.repoSubscribers.set(id, {\n\t\t\tdelayMs: opts?.delayMs ?? this.defaultRepoMs,\n\t\t\tmaxWaitMs: opts?.maxWaitMs ?? this.defaultRepoMaxWaitMs,\n\t\t});\n\t\tthis.recalcRepoTiming();\n\n\t\tif (wasEmpty) {\n\t\t\tthis.lifecycle?.onFirstRepoSubscriber?.();\n\t\t}\n\n\t\t// If the effective delay or max-wait got shorter while a flush is pending, move it sooner\n\t\tthis.repoClock.rearmIfEarlier();\n\n\t\tlet subDisposed = false;\n\t\treturn Object.assign(\n\t\t\tcreateDisposable(() => {\n\t\t\t\tif (subDisposed) return;\n\n\t\t\t\tsubDisposed = true;\n\n\t\t\t\tthis.repoSubscribers.delete(id);\n\t\t\t\tthis.recalcRepoTiming();\n\n\t\t\t\tif (this.repoSubscribers.size === 0) {\n\t\t\t\t\tthis.repoClock.cancel();\n\t\t\t\t\tthis.clearPendingRepoChanges();\n\t\t\t\t\tthis.lifecycle?.onLastRepoSubscriber?.();\n\t\t\t\t}\n\t\t\t}),\n\t\t\t{\n\t\t\t\tonDidChange:\n\t\t\t\t\topts?.relevantRefs != null\n\t\t\t\t\t\t? createRelevantRefsEvent(this.repoEmitter.event, opts.relevantRefs)\n\t\t\t\t\t\t: this.repoEmitter.event,\n\t\t\t},\n\t\t);\n\t}\n\n\t/** Subscribe to working tree changes; `delayMs` and `maxWaitMs` behave as in {@link subscribe}. */\n\tsubscribeToWorkingTree(opts?: { delayMs?: number; maxWaitMs?: number }): WorkingTreeSubscription {\n\t\tif (this._disposed) {\n\t\t\treturn Object.assign(\n\t\t\t\tcreateDisposable(() => {}),\n\t\t\t\t{\n\t\t\t\t\tonDidChangeWorkingTree: this.workingTreeEmitter.event,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\tconst id = this.workingTreeNextId++;\n\t\tconst wasEmpty = this.workingTreeSubscribers.size === 0;\n\n\t\tthis.workingTreeSubscribers.set(id, {\n\t\t\tdelayMs: opts?.delayMs ?? this.defaultWorkingTreeMs,\n\t\t\tmaxWaitMs: opts?.maxWaitMs ?? this.defaultWorkingTreeMaxWaitMs,\n\t\t});\n\t\tthis.recalcWorkingTreeTiming();\n\n\t\tif (wasEmpty) {\n\t\t\tthis.lifecycle?.onFirstWorkingTreeSubscriber?.();\n\t\t}\n\n\t\t// If the effective delay or max-wait got shorter while a flush is pending, move it sooner\n\t\tthis.workingTreeClock.rearmIfEarlier();\n\n\t\tlet subDisposed = false;\n\t\treturn Object.assign(\n\t\t\tcreateDisposable(() => {\n\t\t\t\tif (subDisposed) return;\n\n\t\t\t\tsubDisposed = true;\n\n\t\t\t\tthis.workingTreeSubscribers.delete(id);\n\t\t\t\tthis.recalcWorkingTreeTiming();\n\n\t\t\t\tif (this.workingTreeSubscribers.size === 0) {\n\t\t\t\t\tthis.workingTreeClock.cancel();\n\t\t\t\t\tthis.pendingWorkingTreePaths.clear();\n\t\t\t\t\t// Dropped with the paths it describes — a stale flag would make the next subscriber's\n\t\t\t\t\t// first ordinary flush claim a partial set and force needless full refreshes.\n\t\t\t\t\tthis.pendingWorkingTreeIncomplete = false;\n\t\t\t\t\tthis.lifecycle?.onLastWorkingTreeSubscriber?.();\n\t\t\t\t}\n\t\t\t}),\n\t\t\t{ onDidChangeWorkingTree: this.workingTreeEmitter.event },\n\t\t);\n\t}\n\n\tsuspend(): void {\n\t\tthis._suspended = true;\n\t\t// Cancel active timers — events continue to coalesce but don't fire\n\t\tthis.repoClock.suspend();\n\t\tthis.workingTreeClock.suspend();\n\t}\n\n\tresume(delayMs?: number): void {\n\t\tif (!this._suspended) return;\n\n\t\tthis._suspended = false;\n\n\t\t// Flush accumulated changes (a clock with nothing pending stays idle); the max-wait window restarts from now\n\t\tthis.repoClock.resume(delayMs);\n\t\tthis.workingTreeClock.resume(delayMs);\n\t}\n\n\t/**\n\t * Inject changes (e.g., after a git operation the caller performed). Goes through debounce + suspend. Ref kinds\n\t * among them are unnamed — any ref of that kind may have moved.\n\t */\n\tfireChange(...changes: RepositoryChange[]): void {\n\t\tif (this._disposed || changes.length === 0) return;\n\n\t\tthis.coalesceRepo(changes);\n\t}\n\n\t/** Immediate fire, bypassing debounce + suspend (for Closed/Opened). Ref kinds among them are unnamed. */\n\tfireChangeImmediate(...changes: RepositoryChange[]): void {\n\t\tif (this._disposed || changes.length === 0) return;\n\n\t\tconst event = new WatcherRepoChangeEvent(this.repoPath, changes);\n\t\t// Same contract as `flushRepo`: notify the global forwarder (which advances the cache's status clock) BEFORE\n\t\t// subscribers. Skipping it here would route changes to subscribers that no one advanced the clock for —\n\t\t// subscribers see them as session-routed and (correctly) don't re-advance, so the cache would go stale.\n\t\tthis.onDidFireRepoChangeCallback?.(event);\n\t\tthis.repoEmitter.fire(event);\n\t}\n\n\t/**\n\t * Push interpreted repo changes into the pipeline. `ref` is the full ref name (or ref directory, or pseudo-ref) whose\n\t * file changed; a ref kind in `changes` is attributed to it when the kinds match, and is otherwise unnamed.\n\t */\n\tpushRepoChanges(changes: RepositoryChange[], ref?: string): void {\n\t\tif (this._disposed) return;\n\n\t\tthis.coalesceRepo(changes, ref);\n\t}\n\n\t/** Push working tree file changes into the pipeline */\n\t/** `incomplete` marks the batch as partial when the caller already dropped paths of its own. */\n\tpushWorkingTreeChanges(paths: Iterable<string>, incomplete?: boolean): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._etagWorkingTree++;\n\t\tif (incomplete) {\n\t\t\tthis.pendingWorkingTreeIncomplete = true;\n\t\t}\n\t\tthis.coalesceWorkingTrees(paths);\n\t}\n\n\tprivate static minDelay(subscribers: Map<number, DebounceTiming>, fallback: number): number {\n\t\tlet min = fallback;\n\t\tfor (const { delayMs } of subscribers.values()) {\n\t\t\tif (delayMs < min) {\n\t\t\t\tmin = delayMs;\n\t\t\t}\n\t\t}\n\t\treturn min;\n\t}\n\n\t/** Unlike the delay, the default isn't a ceiling — a subscriber can opt out of the cap with `Infinity` */\n\tprivate static minMaxWait(subscribers: Map<number, DebounceTiming>, fallback: number): number {\n\t\tif (subscribers.size === 0) return fallback;\n\n\t\tlet min = Infinity;\n\t\tfor (const { maxWaitMs } of subscribers.values()) {\n\t\t\tif (maxWaitMs < min) {\n\t\t\t\tmin = maxWaitMs;\n\t\t\t}\n\t\t}\n\t\treturn min;\n\t}\n\n\tprivate recalcRepoTiming(): void {\n\t\tthis.effectiveRepoMs = RepositoryWatchSession.minDelay(this.repoSubscribers, this.defaultRepoMs);\n\t\tthis.effectiveRepoMaxWaitMs = RepositoryWatchSession.minMaxWait(\n\t\t\tthis.repoSubscribers,\n\t\t\tthis.defaultRepoMaxWaitMs,\n\t\t);\n\t}\n\n\tprivate recalcWorkingTreeTiming(): void {\n\t\tthis.effectiveWorkingTreeMs = RepositoryWatchSession.minDelay(\n\t\t\tthis.workingTreeSubscribers,\n\t\t\tthis.defaultWorkingTreeMs,\n\t\t);\n\t\tthis.effectiveWorkingTreeMaxWaitMs = RepositoryWatchSession.minMaxWait(\n\t\t\tthis.workingTreeSubscribers,\n\t\t\tthis.defaultWorkingTreeMaxWaitMs,\n\t\t);\n\t}\n\n\tprivate flushRepo(): void {\n\t\tif (this.pendingRepoChanges.size === 0) return;\n\n\t\t// The event is built only here — raw events just add to the pending sets (which the event copies)\n\t\tconst event = new WatcherRepoChangeEvent(this.repoPath, [...this.pendingRepoChanges], {\n\t\t\tnames: this.pendingRepoRefs,\n\t\t\tunnamed: this.pendingRepoUnnamedRefKinds,\n\t\t});\n\t\tthis.clearPendingRepoChanges();\n\t\t// Advance any cache clock BEFORE notifying subscribers (same rationale as `flushWorkingTree`): a subscriber\n\t\t// that synchronously reads status must see the post-change generation, not a joinable pre-change run.\n\t\tthis.onDidFireRepoChangeCallback?.(event);\n\t\tthis.repoEmitter.fire(event);\n\t}\n\n\tprivate coalesceRepo(changes: RepositoryChange[], ref?: string): void {\n\t\tif (changes.length === 0) return;\n\n\t\tconst refKind = ref != null ? getRefChangeKind(ref) : undefined;\n\t\tfor (const change of changes) {\n\t\t\tthis.pendingRepoChanges.add(change);\n\t\t\tif (!isRefChangeKind(change)) continue;\n\n\t\t\tif (\n\t\t\t\tref != null &&\n\t\t\t\trefKind === change &&\n\t\t\t\t(this.pendingRepoRefs.size < maxPendingRefNames || this.pendingRepoRefs.has(ref))\n\t\t\t) {\n\t\t\t\tthis.pendingRepoRefs.add(ref);\n\t\t\t} else {\n\t\t\t\tthis.pendingRepoUnnamedRefKinds.add(change);\n\t\t\t}\n\t\t}\n\t\tthis.repoClock.note();\n\t}\n\n\tprivate clearPendingRepoChanges(): void {\n\t\tthis.pendingRepoChanges.clear();\n\t\tthis.pendingRepoRefs.clear();\n\t\tthis.pendingRepoUnnamedRefKinds.clear();\n\t}\n\n\tprivate flushWorkingTree(): void {\n\t\tif (!this.hasPendingWorkingTreeChanges) return;\n\n\t\tconst paths = this.pendingWorkingTreePaths;\n\t\tconst incomplete = this.pendingWorkingTreeIncomplete;\n\t\tthis.pendingWorkingTreePaths = new Set();\n\t\tthis.pendingWorkingTreeIncomplete = false;\n\t\t// Advance the cache's status clock (via WatchService's global forwarding) BEFORE notifying subscribers,\n\t\t// so a subscriber that synchronously reads status sees the post-change generation, not a joinable pre-change run.\n\t\tthis.onDidFireWorkingTreeChangeCallback?.(this.repoPath);\n\t\tthis.workingTreeEmitter.fire({ repoPath: this.repoPath, paths: paths, incomplete: incomplete });\n\t}\n\n\tprivate coalesceWorkingTrees(paths: Iterable<string>): void {\n\t\tfor (const p of paths) {\n\t\t\t// Stop retaining paths past the cap, but keep draining the iterable — the flag is what makes the\n\t\t\t// partial set safe to consume, and dropping out early would leave later paths for the next window.\n\t\t\tif (this.pendingWorkingTreePaths.size >= maxWorkingTreePaths && !this.pendingWorkingTreePaths.has(p)) {\n\t\t\t\tthis.pendingWorkingTreeIncomplete = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis.pendingWorkingTreePaths.add(p);\n\t\t}\n\n\t\tif (this.hasPendingWorkingTreeChanges) {\n\t\t\tthis.workingTreeClock.note();\n\t\t}\n\t}\n}\n\n/** Wraps a session's repo event so each delivery is narrowed to the subscriber's relevant refs (see `subscribe`) */\nfunction createRelevantRefsEvent(\n\tevent: Event<WatcherRepoChangeEvent>,\n\trelevantRefs: () => Iterable<string>,\n): Event<WatcherRepoChangeEvent> {\n\treturn (listener, thisArgs, disposables) =>\n\t\tevent(\n\t\t\te => {\n\t\t\t\tconst narrowed = narrowToRelevantRefs(e, relevantRefs);\n\t\t\t\tif (narrowed != null) {\n\t\t\t\t\tlistener.call(thisArgs, narrowed);\n\t\t\t\t}\n\t\t\t},\n\t\t\tundefined,\n\t\t\tdisposables,\n\t\t);\n}\n\n/**\n * Narrows an event to the refs a subscriber cares about. Returns the event itself when no change kind is removed (the\n * kept names then stay a superset, which only makes `mayHaveChangedRef` more conservative), `undefined` when every kind\n * is removed, and otherwise a new event with the kept kinds, names and unnamed kinds.\n */\nfunction narrowToRelevantRefs(\n\te: WatcherRepoChangeEvent,\n\trelevantRefs: () => Iterable<string>,\n): WatcherRepoChangeEvent | undefined {\n\t// Ref kinds that pass only if a relevant ref of that kind may have moved\n\tlet unresolved: Set<RefChangeKind> | undefined;\n\tfor (const change of e.changes) {\n\t\tif (!isRefChangeKind(change) || e.unnamedRefKinds.has(change)) continue;\n\n\t\t(unresolved ??= new Set()).add(change);\n\t}\n\tif (unresolved == null) return e;\n\n\tconst pseudoRefs: string[] = [];\n\tfor (const name of e.refs) {\n\t\tif (!name.startsWith('refs/')) {\n\t\t\tpseudoRefs.push(name);\n\t\t}\n\t}\n\tif (pseudoRefs.length !== 0) {\n\t\t// A pseudo-ref (`HEAD`, `ORIG_HEAD`) moving is a `heads` change for every subscriber\n\t\tunresolved.delete('heads');\n\t\tif (unresolved.size === 0) return e;\n\t}\n\n\t// Names that equal or cover a relevant ref — collected as we go, since `relevantRefs` may be a one-shot iterable\n\tconst keptNames = new Set<string>(pseudoRefs);\n\tfor (const ref of relevantRefs()) {\n\t\tconst kind = getRefChangeKind(ref);\n\t\tif (kind == null || !e.changes.has(kind)) continue;\n\n\t\t// `HEAD` is usually a symref to the checked-out branch, so any `heads` change may have moved it\n\t\tif (ref === 'HEAD') {\n\t\t\tfor (const name of e.refs) {\n\t\t\t\tif (getRefChangeKind(name) === 'heads') {\n\t\t\t\t\tkeptNames.add(name);\n\t\t\t\t}\n\t\t\t}\n\t\t\tunresolved.delete(kind);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst covering = findCoveringRefName(e.refs, ref);\n\t\tif (covering == null) continue;\n\n\t\tkeptNames.add(covering);\n\t\tunresolved.delete(kind);\n\t}\n\tif (unresolved.size === 0) return e;\n\n\tconst keptChanges: RepositoryChange[] = [];\n\tfor (const change of e.changes) {\n\t\tif (!isRefChangeKind(change) || !unresolved.has(change)) {\n\t\t\tkeptChanges.push(change);\n\t\t}\n\t}\n\tif (keptChanges.length === 0) return undefined;\n\n\treturn new WatcherRepoChangeEvent(e.repoPath, keptChanges, {\n\t\tnames: keptNames,\n\t\tunnamed: e.unnamedRefKinds,\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
//#region ../git/src/watching/watcherPatterns.d.ts
|
|
2
2
|
/** For standard repos (no worktrees): single watcher covering all .git files */
|
|
3
|
-
export declare const dotGitGlobCombined = "{index,HEAD,*_HEAD,MERGE_*,rebase-apply,rebase-apply/**,rebase-merge,rebase-merge/**,sequencer,sequencer/**,config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,worktrees,worktrees/*,worktrees
|
|
3
|
+
export declare const dotGitGlobCombined = "{index,HEAD,*_HEAD,MERGE_*,rebase-apply,rebase-apply/**,rebase-merge,rebase-merge/**,sequencer,sequencer/**,config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,worktrees,worktrees/*,worktrees/*/HEAD,worktrees/*/locked,worktrees/*/gitdir}";
|
|
4
4
|
/** For worktree repos: root watcher (worktree-specific files only) */
|
|
5
5
|
export declare const dotGitGlobRoot = "{index,HEAD,*_HEAD,MERGE_*,rebase-apply,rebase-apply/**,rebase-merge,rebase-merge/**,sequencer,sequencer/**}";
|
|
6
6
|
/** For worktree repos: common watcher (shared config, refs, info, etc.) */
|
|
7
|
-
export declare const dotGitGlobCommon = "{config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,worktrees,worktrees/*,worktrees
|
|
7
|
+
export declare const dotGitGlobCommon = "{config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,worktrees,worktrees/*,worktrees/*/HEAD,worktrees/*/locked,worktrees/*/gitdir}";
|
|
8
|
+
/**
|
|
9
|
+
* Directories to leave unwatched under the `.git` directory (relative to it), for a provider that prunes natively.
|
|
10
|
+
* Only directories nothing in the globs above reads: never anything under `refs`, `worktrees/*` itself, `gk`, `info`,
|
|
11
|
+
* or the rebase/sequencer directories.
|
|
12
|
+
*/
|
|
13
|
+
export declare const gitDirExcludes: readonly string[];
|
|
14
|
+
/**
|
|
15
|
+
* Directories to leave unwatched in the working tree (relative to it), for a provider that prunes natively —
|
|
16
|
+
* the same ones {@link shouldIgnoreWorkingTreePath} drops from events.
|
|
17
|
+
*/
|
|
18
|
+
export declare const workingTreeExcludes: readonly string[];
|
|
8
19
|
/** Glob for watching .gitignore files in the working tree */
|
|
9
20
|
export declare const gitIgnoreGlob = ".gitignore";
|
|
10
21
|
/** Glob for detecting .git directory creation (repository init/clone) */
|
|
@@ -1,12 +1,32 @@
|
|
|
1
1
|
//#region ../git/src/watching/watcherPatterns.ts
|
|
2
2
|
const dotGitFiles = "index,HEAD,*_HEAD,MERGE_*,rebase-apply,rebase-apply/**,rebase-merge,rebase-merge/**,sequencer,sequencer/**";
|
|
3
|
-
const dotGitCommonFiles = `config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,worktrees,worktrees/*,worktrees
|
|
3
|
+
const dotGitCommonFiles = `config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,worktrees,worktrees/*,worktrees/*/HEAD,worktrees/*/locked,worktrees/*/gitdir`;
|
|
4
4
|
/** For standard repos (no worktrees): single watcher covering all .git files */
|
|
5
5
|
const dotGitGlobCombined = `{${dotGitFiles},${dotGitCommonFiles}}`;
|
|
6
6
|
/** For worktree repos: root watcher (worktree-specific files only) */
|
|
7
7
|
const dotGitGlobRoot = `{${dotGitFiles}}`;
|
|
8
8
|
/** For worktree repos: common watcher (shared config, refs, info, etc.) */
|
|
9
9
|
const dotGitGlobCommon = `{${dotGitCommonFiles}}`;
|
|
10
|
+
/**
|
|
11
|
+
* Directories to leave unwatched under the `.git` directory (relative to it), for a provider that prunes natively.
|
|
12
|
+
* Only directories nothing in the globs above reads: never anything under `refs`, `worktrees/*` itself, `gk`, `info`,
|
|
13
|
+
* or the rebase/sequencer directories.
|
|
14
|
+
*/
|
|
15
|
+
const gitDirExcludes = [
|
|
16
|
+
"objects",
|
|
17
|
+
"logs",
|
|
18
|
+
"hooks",
|
|
19
|
+
"lfs",
|
|
20
|
+
"modules",
|
|
21
|
+
"rr-cache",
|
|
22
|
+
"fsmonitor--daemon",
|
|
23
|
+
"worktrees/*/logs"
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Directories to leave unwatched in the working tree (relative to it), for a provider that prunes natively —
|
|
27
|
+
* the same ones {@link shouldIgnoreWorkingTreePath} drops from events.
|
|
28
|
+
*/
|
|
29
|
+
const workingTreeExcludes = ["**/node_modules", "**/.git"];
|
|
10
30
|
/** Glob for watching .gitignore files in the working tree */
|
|
11
31
|
const gitIgnoreGlob = ".gitignore";
|
|
12
32
|
/** Glob for detecting .git directory creation (repository init/clone) */
|
|
@@ -28,6 +48,6 @@ function shouldIgnoreWorkingTreePath(absolutePath) {
|
|
|
28
48
|
return ignoredWorkingTreePathRegex.test(absolutePath);
|
|
29
49
|
}
|
|
30
50
|
//#endregion
|
|
31
|
-
export { dotGitGlobCombined, dotGitGlobCommon, dotGitGlobRoot, gitIgnoreGlob, gitInitGlob, shouldIgnoreRepoPath, shouldIgnoreWorkingTreePath };
|
|
51
|
+
export { dotGitGlobCombined, dotGitGlobCommon, dotGitGlobRoot, gitDirExcludes, gitIgnoreGlob, gitInitGlob, shouldIgnoreRepoPath, shouldIgnoreWorkingTreePath, workingTreeExcludes };
|
|
32
52
|
|
|
33
53
|
//# sourceMappingURL=watcherPatterns.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watcherPatterns.js","names":[],"sources":["../../../../git/src/watching/watcherPatterns.ts"],"sourcesContent":["// Glob patterns for watching files inside the .git directory.\n// They represent pure Git knowledge — the library owns these patterns and\n// provides them to the pluggable FileWatcherFactory.\n\nconst dotGitFiles =\n\t'index,HEAD,*_HEAD,MERGE_*,rebase-apply,rebase-apply/**,rebase-merge,rebase-merge/**,sequencer,sequencer/**';\n\n// `worktrees` (the bare entry, no `/*` suffix) is its own pattern: `worktree remove` deletes the whole\n// `worktrees/<id>` subtree in one syscall, and some platforms/watcher backends coalesce a same-instant\n// subtree delete into a single event for the PARENT directory — `worktrees/*` alone never matches that.\n// `classifyGitDirChange`
|
|
1
|
+
{"version":3,"file":"watcherPatterns.js","names":[],"sources":["../../../../git/src/watching/watcherPatterns.ts"],"sourcesContent":["// Glob patterns for watching files inside the .git directory.\n// They represent pure Git knowledge — the library owns these patterns and\n// provides them to the pluggable FileWatcherFactory.\n\nconst dotGitFiles =\n\t'index,HEAD,*_HEAD,MERGE_*,rebase-apply,rebase-apply/**,rebase-merge,rebase-merge/**,sequencer,sequencer/**';\n\n// `worktrees` (the bare entry, no `/*` suffix) is its own pattern: `worktree remove` deletes the whole\n// `worktrees/<id>` subtree in one syscall, and some platforms/watcher backends coalesce a same-instant\n// subtree delete into a single event for the PARENT directory — `worktrees/*` alone never matches that.\n// `classifyGitDirChange` classifies a bare `worktrees` path, so this is watch-coverage only.\n//\n// Only what `git worktree list` reports per worktree is watched here (its HEAD, lock and location). A\n// linked worktree's own index, `*_HEAD`, `MERGE_*`, rebase and sequencer state is watched by that\n// worktree's root watcher (`dotGitGlobRoot`) instead, so it never wakes sibling worktrees.\nconst dotGitWorktreeFiles = 'worktrees,worktrees/*,worktrees/*/HEAD,worktrees/*/locked,worktrees/*/gitdir';\n\nconst dotGitCommonFiles = `config,gk/config,refs/**,packed-refs,shallow,info/exclude,FETCH_HEAD,${dotGitWorktreeFiles}`;\n\n/** For standard repos (no worktrees): single watcher covering all .git files */\nexport const dotGitGlobCombined = `{${dotGitFiles},${dotGitCommonFiles}}`;\n\n/** For worktree repos: root watcher (worktree-specific files only) */\nexport const dotGitGlobRoot = `{${dotGitFiles}}`;\n\n/** For worktree repos: common watcher (shared config, refs, info, etc.) */\nexport const dotGitGlobCommon = `{${dotGitCommonFiles}}`;\n\n/**\n * Directories to leave unwatched under the `.git` directory (relative to it), for a provider that prunes natively.\n * Only directories nothing in the globs above reads: never anything under `refs`, `worktrees/*` itself, `gk`, `info`,\n * or the rebase/sequencer directories.\n */\nexport const gitDirExcludes: readonly string[] = [\n\t'objects',\n\t'logs',\n\t'hooks',\n\t'lfs',\n\t'modules',\n\t'rr-cache',\n\t'fsmonitor--daemon',\n\t'worktrees/*/logs',\n];\n\n/**\n * Directories to leave unwatched in the working tree (relative to it), for a provider that prunes natively —\n * the same ones {@link shouldIgnoreWorkingTreePath} drops from events.\n */\nexport const workingTreeExcludes: readonly string[] = ['**/node_modules', '**/.git'];\n\n/** Glob for watching .gitignore files in the working tree */\nexport const gitIgnoreGlob = '.gitignore';\n\n/** Glob for detecting .git directory creation (repository init/clone) */\nexport const gitInitGlob = '**/.git';\n\n// Regex-based filters that complement the globs above. The globs tell\n// the watcher *what* to observe; these filters tell it what to *skip*\n// at runtime (transient lock files, daemon artifacts, etc.).\n\nconst ignoredRepoPathRegex = /(?:\\/|\\\\)fsmonitor--daemon(?:\\/|\\\\)|(?:^|\\/)index\\.lock$/;\n\n/**\n * Returns true if the `.git`-relative path should be ignored\n * (transient filesystem artifacts, not meaningful git state changes).\n */\nexport function shouldIgnoreRepoPath(relativePath: string): boolean {\n\treturn ignoredRepoPathRegex.test(relativePath);\n}\n\nconst ignoredWorkingTreePathRegex =\n\t/(?:(?:\\/|\\\\)node_modules(?:\\/|\\\\|$)|\\.git(?:\\/index\\.lock)?(?:\\/|\\\\|$)|\\.watchman-cookie-)/;\n\n/**\n * Returns true if the working tree path should be ignored\n * (node_modules, .git internals, watchman cookies).\n */\nexport function shouldIgnoreWorkingTreePath(absolutePath: string): boolean {\n\treturn ignoredWorkingTreePathRegex.test(absolutePath);\n}\n"],"mappings":";AAIA,MAAM,cACL;AAYD,MAAM,oBAAoB;;AAG1B,MAAa,qBAAqB,IAAI,YAAY,GAAG,kBAAkB;;AAGvE,MAAa,iBAAiB,IAAI,YAAY;;AAG9C,MAAa,mBAAmB,IAAI,kBAAkB;;;;;;AAOtD,MAAa,iBAAoC;CAChD;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;;;;;AAMA,MAAa,sBAAyC,CAAC,mBAAmB,SAAS;;AAGnF,MAAa,gBAAgB;;AAG7B,MAAa,cAAc;AAM3B,MAAM,uBAAuB;;;;;AAM7B,SAAgB,qBAAqB,cAA+B;CACnE,OAAO,qBAAqB,KAAK,YAAY;AAC9C;AAEA,MAAM,8BACL;;;;;AAMD,SAAgB,4BAA4B,cAA+B;CAC1E,OAAO,4BAA4B,KAAK,YAAY;AACrD"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { UnifiedDisposable } from "../../utils/disposable.js";
|
|
2
|
+
import { Event } from "../../utils/event.js";
|
|
3
|
+
import { ResourceUsage } from "../../utils/resourceUsage.js";
|
|
4
|
+
import { RepositoryWatchService } from "./watchService.js";
|
|
5
|
+
import { RepositoryService } from "../repositoryService.js";
|
|
6
|
+
//#region ../git/src/watching/worktreeSummary.d.ts
|
|
7
|
+
export interface WorktreeSummaryUpstream {
|
|
8
|
+
/** The upstream's short name (`origin/main`) */
|
|
9
|
+
readonly name: string;
|
|
10
|
+
/**
|
|
11
|
+
* The remote it tracks: the longest of the repository's remote names that {@link name} starts with, followed by
|
|
12
|
+
* `/` (a remote name can hold a slash). `undefined`, with {@link branch}, when it names no known remote (a branch
|
|
13
|
+
* tracking another local branch, or a remote since removed)
|
|
14
|
+
*/
|
|
15
|
+
readonly remote: string | undefined;
|
|
16
|
+
/** The branch on {@link remote}; `undefined` whenever {@link remote} is */
|
|
17
|
+
readonly branch: string | undefined;
|
|
18
|
+
readonly missing: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface WorktreeSummaryBase {
|
|
21
|
+
/** The ref the count was taken against (see `AheadOfBase.base`) */
|
|
22
|
+
readonly ref: string;
|
|
23
|
+
readonly ahead: number;
|
|
24
|
+
}
|
|
25
|
+
export interface WorktreeSummary {
|
|
26
|
+
/** The repository path in {@link getRepositoryKey} form */
|
|
27
|
+
readonly repoPath: string;
|
|
28
|
+
/** The checked-out branch; `undefined` when detached (or unborn with no name) */
|
|
29
|
+
readonly branch: string | undefined;
|
|
30
|
+
readonly detached: boolean;
|
|
31
|
+
readonly upstream: WorktreeSummaryUpstream | undefined;
|
|
32
|
+
/** Commits ahead of the upstream; 0 without one */
|
|
33
|
+
readonly ahead: number;
|
|
34
|
+
/** Commits behind the upstream; 0 without one */
|
|
35
|
+
readonly behind: number;
|
|
36
|
+
/** Whether there is any staged, unstaged or untracked (non-ignored) change */
|
|
37
|
+
readonly dirty: boolean;
|
|
38
|
+
/** How far the branch is past its base; `undefined` when detached, unsupported, or there is no base */
|
|
39
|
+
readonly base: WorktreeSummaryBase | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* The latest recompute failed (worktree gone, lock contention); the other fields hold the last values read. Also set
|
|
42
|
+
* when only the base read failed: the other fields are fresh, and `base` is its last value, or `undefined` if the
|
|
43
|
+
* branch changed.
|
|
44
|
+
*/
|
|
45
|
+
readonly stale: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface WorktreeSummarySubscribeOptions {
|
|
48
|
+
/** Debounce for repo (`.git`) changes this subscriber asks of the watch session; the shortest across subscribers wins */
|
|
49
|
+
readonly delayMs?: number;
|
|
50
|
+
/** Cap on how long continuous repo activity can defer a recompute; `Infinity` means no cap */
|
|
51
|
+
readonly maxWaitMs?: number;
|
|
52
|
+
/** Debounce for working-tree changes this subscriber asks of the watch session */
|
|
53
|
+
readonly workingTreeDelayMs?: number;
|
|
54
|
+
/** Cap on how long continuous working-tree activity can defer a recompute; `Infinity` means no cap */
|
|
55
|
+
readonly workingTreeMaxWaitMs?: number;
|
|
56
|
+
}
|
|
57
|
+
export interface WorktreeSummarySubscription extends UnifiedDisposable {
|
|
58
|
+
/** The latest summary; `undefined` until the first read succeeds, or after disposal */
|
|
59
|
+
readonly current: WorktreeSummary | undefined;
|
|
60
|
+
/** Fires when the summary's value changes */
|
|
61
|
+
readonly onDidChange: Event<WorktreeSummary>;
|
|
62
|
+
}
|
|
63
|
+
export interface WorktreeSummaryServiceOptions {
|
|
64
|
+
readonly watchService: RepositoryWatchService;
|
|
65
|
+
readonly getRepository: (repoPath: string) => RepositoryService | undefined;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A cached, change-notifying summary of each subscribed worktree — its branch, upstream and ahead/behind, whether it
|
|
69
|
+
* has working changes, and how far it is past its base — kept current by the repository watch layer.
|
|
70
|
+
*
|
|
71
|
+
* **Lifecycle.** Entries are per repository key and ref-counted across {@link subscribe} calls. The first subscriber
|
|
72
|
+
* starts watching the worktree and reads everything; the last one to leave aborts any in-flight read, releases the
|
|
73
|
+
* watch and drops the value. Each subscriber's timing options join the watch session's cadence (the shortest wins);
|
|
74
|
+
* the service's own listening subscriptions never constrain it.
|
|
75
|
+
*
|
|
76
|
+
* **Field-scoped recompute.** The summary is read in three groups — `dirty` (working changes), `branch` (branch,
|
|
77
|
+
* upstream, ahead/behind) and `base` (ahead of base) — and an event recomputes only the groups it can affect: a
|
|
78
|
+
* working-tree edit, an index or ignore-rule change reads only `dirty`; the upstream moving reads only `branch`; a
|
|
79
|
+
* ref the base depends on reads only `base`; a `gk` config change (mostly bookkeeping writes to the shared
|
|
80
|
+
* `.git/gk/config`) re-reads the branch's stored merge targets and recorded base, and reads `base` only when one of
|
|
81
|
+
* them changed; the checked-out branch moving, or a HEAD change, reads all of them. Tags, stash, unrelated branches
|
|
82
|
+
* and the like read nothing. One run is in flight per worktree at a
|
|
83
|
+
* time; events during a run are merged into a single follow-up run.
|
|
84
|
+
*
|
|
85
|
+
* **Value-change-only notification.** {@link WorktreeSummarySubscription.onDidChange} and {@link onDidChangeSummary}
|
|
86
|
+
* fire only when some field's value differs from the previous summary — a recompute that reads the same answer is
|
|
87
|
+
* silent, so a consumer can re-render on every event without diffing. A failed recompute keeps the last values and
|
|
88
|
+
* sets `stale` (firing once); the next successful one clears it.
|
|
89
|
+
*/
|
|
90
|
+
export declare class WorktreeSummaryService implements UnifiedDisposable {
|
|
91
|
+
private readonly _onDidChangeSummary;
|
|
92
|
+
/** Fires when the summary of any subscribed worktree changes value */
|
|
93
|
+
get onDidChangeSummary(): Event<WorktreeSummary>;
|
|
94
|
+
private readonly entries;
|
|
95
|
+
private readonly watchService;
|
|
96
|
+
private readonly getRepository;
|
|
97
|
+
private _disposed;
|
|
98
|
+
constructor(options: WorktreeSummaryServiceOptions);
|
|
99
|
+
[Symbol.dispose](): void;
|
|
100
|
+
dispose(): void;
|
|
101
|
+
/** Subscribes to a worktree's summary, starting to watch and read it if this is its first subscriber */
|
|
102
|
+
subscribe(repoPath: string, options?: WorktreeSummarySubscribeOptions): WorktreeSummarySubscription;
|
|
103
|
+
/**
|
|
104
|
+
* Returns a worktree's summary. For a subscribed worktree, the cached value — after waiting for any run in flight
|
|
105
|
+
* or pending; it rejects with the read's error when there is still no value. Otherwise a one-off full read that
|
|
106
|
+
* is neither cached nor watched.
|
|
107
|
+
*/
|
|
108
|
+
getSummary(repoPath: string, cancellation?: AbortSignal): Promise<WorktreeSummary>;
|
|
109
|
+
getResourceUsage(): ResourceUsage;
|
|
110
|
+
private start;
|
|
111
|
+
private onRepoChanged;
|
|
112
|
+
private queue;
|
|
113
|
+
/** Runs until nothing is pending. Never rejects — every failure becomes entry state. */
|
|
114
|
+
private drain;
|
|
115
|
+
private run;
|
|
116
|
+
private fail;
|
|
117
|
+
private fireChange;
|
|
118
|
+
private release;
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
//# sourceMappingURL=worktreeSummary.d.ts.map
|