@gitkraken/core-gitlens 0.16.5 → 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.
Files changed (63) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/dist/git/cache.d.ts +8 -2
  3. package/dist/git/cache.js +15 -6
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/models/repository.d.ts +4 -2
  6. package/dist/git/models/repository.js +1 -0
  7. package/dist/git/models/repository.js.map +1 -1
  8. package/dist/git/models/worktree.d.ts +5 -1
  9. package/dist/git/models/worktree.js +5 -3
  10. package/dist/git/models/worktree.js.map +1 -1
  11. package/dist/git/providers/provider.d.ts +17 -0
  12. package/dist/git/providers/worktrees.d.ts +17 -2
  13. package/dist/git/run.types.d.ts +7 -0
  14. package/dist/git/service.d.ts +5 -2
  15. package/dist/git/service.js +11 -5
  16. package/dist/git/service.js.map +1 -1
  17. package/dist/git/watching/classifyChange.d.ts +4 -3
  18. package/dist/git/watching/classifyChange.js +7 -4
  19. package/dist/git/watching/classifyChange.js.map +1 -1
  20. package/dist/git/watching/provider.d.ts +6 -0
  21. package/dist/git/watching/watchGroup.d.ts +142 -3
  22. package/dist/git/watching/watchGroup.js +424 -19
  23. package/dist/git/watching/watchGroup.js.map +1 -1
  24. package/dist/git/watching/watchService.d.ts +25 -0
  25. package/dist/git/watching/watchService.js +27 -7
  26. package/dist/git/watching/watchService.js.map +1 -1
  27. package/dist/git/watching/watchSession.d.ts +13 -8
  28. package/dist/git/watching/watchSession.js +50 -10
  29. package/dist/git/watching/watchSession.js.map +1 -1
  30. package/dist/git/watching/watcherPatterns.d.ts +14 -0
  31. package/dist/git/watching/watcherPatterns.js +38 -1
  32. package/dist/git/watching/watcherPatterns.js.map +1 -1
  33. package/dist/git/watching/worktreeSummary.d.ts +64 -2
  34. package/dist/git/watching/worktreeSummary.js +326 -13
  35. package/dist/git/watching/worktreeSummary.js.map +1 -1
  36. package/dist/git-cli/cliGitProvider.d.ts +26 -0
  37. package/dist/git-cli/cliGitProvider.js +66 -5
  38. package/dist/git-cli/cliGitProvider.js.map +1 -1
  39. package/dist/git-cli/exec/commandClassifier.d.ts +9 -3
  40. package/dist/git-cli/exec/commandClassifier.js +98 -17
  41. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  42. package/dist/git-cli/exec/git.d.ts +20 -0
  43. package/dist/git-cli/exec/git.js +69 -7
  44. package/dist/git-cli/exec/git.js.map +1 -1
  45. package/dist/git-cli/exec/headState.d.ts +5 -0
  46. package/dist/git-cli/exec/headState.js +14 -1
  47. package/dist/git-cli/exec/headState.js.map +1 -1
  48. package/dist/git-cli/parsers/worktreeParser.js +2 -2
  49. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  50. package/dist/git-cli/providers/branches.js +8 -4
  51. package/dist/git-cli/providers/branches.js.map +1 -1
  52. package/dist/git-cli/providers/graph.js +1 -1
  53. package/dist/git-cli/providers/graph.js.map +1 -1
  54. package/dist/git-cli/providers/operations.d.ts +3 -0
  55. package/dist/git-cli/providers/operations.js +14 -5
  56. package/dist/git-cli/providers/operations.js.map +1 -1
  57. package/dist/git-cli/providers/worktrees.d.ts +25 -3
  58. package/dist/git-cli/providers/worktrees.js +125 -32
  59. package/dist/git-cli/providers/worktrees.js.map +1 -1
  60. package/dist/git-cli/service.d.ts +2 -0
  61. package/dist/git-cli/service.js +4 -2
  62. package/dist/git-cli/service.js.map +1 -1
  63. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"worktreeSummary.js","names":[],"sources":["../../../../git/src/watching/worktreeSummary.ts"],"sourcesContent":["import { areEqual } from '@gitlens/utils/array.js';\nimport { CancellationError, isCancellationError, raceWithSignal } from '@gitlens/utils/cancellation.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ResourceUsage } from '@gitlens/utils/resourceUsage.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from '../cache.js';\nimport type { GitTrackingUpstream } from '../models/branch.js';\nimport type { GitDir } from '../models/repository.js';\nimport type { AheadOfBase, AheadOfBaseTier } from '../providers/branches.js';\nimport type { RepositoryService } from '../repositoryService.js';\nimport type { WatcherRepoChangeEvent } from './changeEvent.js';\nimport { getRefChangeKind } from './changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle } from './watchService.js';\nimport type { RepositoryWatchSession } from './watchSession.js';\n\nexport interface WorktreeSummaryUpstream {\n\t/** The upstream's short name (`origin/main`) */\n\treadonly name: string;\n\t/**\n\t * The remote it tracks, as git reported it; on git too old to report it, the longest of the repository's remote\n\t * names that {@link name} starts with, followed by `/` (a remote name can hold a slash). `undefined`, with\n\t * {@link branch}, when it names no known remote (a branch tracking another local branch, or a remote since removed)\n\t */\n\treadonly remote: string | undefined;\n\t/**\n\t * The branch on {@link remote} — git's name for it there, which a custom fetch refspec can make differ from the end\n\t * of {@link name}; `undefined` whenever {@link remote} is\n\t */\n\treadonly branch: string | undefined;\n\treadonly missing: boolean;\n}\n\nexport interface WorktreeSummaryBase {\n\t/** The ref the count was taken against (see `AheadOfBase.base`) */\n\treadonly ref: string;\n\treadonly ahead: number;\n\t/** The commit {@link ref} pointed at when the count was read; `undefined` when it couldn't be resolved */\n\treadonly sha: string | undefined;\n\t/** Where HEAD forked from {@link ref} (see `AheadOfBase.base`) */\n\treadonly forkPoint: string | undefined;\n\t/** The source the base came from (see `AheadOfBase.base`) */\n\treadonly tier: AheadOfBaseTier;\n}\n\nexport interface WorktreeSummary {\n\t/** The repository path in {@link getRepositoryKey} form */\n\treadonly repoPath: string;\n\t/** The checked-out branch; `undefined` when detached (or unborn with no name) */\n\treadonly branch: string | undefined;\n\treadonly detached: boolean;\n\t/** HEAD's commit; `undefined` when HEAD has no commit yet */\n\treadonly sha: string | undefined;\n\treadonly upstream: WorktreeSummaryUpstream | undefined;\n\t/** Commits ahead of the upstream; 0 without one */\n\treadonly ahead: number;\n\t/** Commits behind the upstream; 0 without one */\n\treadonly behind: number;\n\t/** Whether there is any staged, unstaged or untracked (non-ignored) change */\n\treadonly dirty: boolean;\n\t/**\n\t * How far the branch — or a detached HEAD, measured against the default branch — is past its base; `undefined` when\n\t * unsupported or there is no base\n\t */\n\treadonly base: WorktreeSummaryBase | undefined;\n\t/**\n\t * Full ref names whose movement can change {@link base}, as `getAheadOfBase` reports them (see\n\t * `AheadOfBase.dependsOn`) — given with or without a base, so a watcher also learns when one appears. Empty when\n\t * the base isn't read (an unborn HEAD with no name, or a provider without `getAheadOfBase`), and when the base read\n\t * failed with no earlier list for this HEAD (the first read, or the one after a checkout).\n\t */\n\treadonly baseDependsOn: readonly string[];\n\t/**\n\t * The latest recompute failed (worktree gone, lock contention); the other fields hold the last values read. Also set\n\t * when only the base read failed: the other fields are fresh, and `base` and `baseDependsOn` are their last values,\n\t * or `undefined` and empty if the branch changed.\n\t */\n\treadonly stale: boolean;\n}\n\nexport interface WorktreeSummarySubscribeOptions {\n\t/** Debounce for repo (`.git`) changes this subscriber asks of the watch session; the shortest across subscribers wins */\n\treadonly delayMs?: number;\n\t/** Cap on how long continuous repo activity can defer a recompute; `Infinity` means no cap */\n\treadonly maxWaitMs?: number;\n\t/** Debounce for working-tree changes this subscriber asks of the watch session */\n\treadonly workingTreeDelayMs?: number;\n\t/** Cap on how long continuous working-tree activity can defer a recompute; `Infinity` means no cap */\n\treadonly workingTreeMaxWaitMs?: number;\n}\n\nexport interface WorktreeSummarySubscription extends UnifiedDisposable {\n\t/** The latest summary; `undefined` until the first read succeeds, or after disposal */\n\treadonly current: WorktreeSummary | undefined;\n\t/** Fires when the summary's value changes */\n\treadonly onDidChange: Event<WorktreeSummary>;\n}\n\nexport interface WorktreeSummaryServiceOptions {\n\treadonly watchService: RepositoryWatchService;\n\treadonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\t/**\n\t * Fires as core writes a branch's merge target or base, before the store's promise resolves; the summary re-reads\n\t * that branch's stored values then, so a `getSummary` right after an awaited store sees the new base\n\t */\n\treadonly onDidChangeMergeTargets?: Event<MergeTargetsChangeEvent>;\n\t/**\n\t * Maps a worktree path to the repository common path that {@link onDidChangeMergeTargets} events carry; only that\n\t * repository's summaries react. Without it, or when it returns `undefined`, every summary reacts.\n\t */\n\treadonly getCommonPath?: (repoPath: string) => string | undefined;\n\t/**\n\t * For tests only; production never passes it. Called whenever the base re-measure answers in place of a full base\n\t * read, with its answer and what a full `getAheadOfBase` of the same branch answers right after (`undefined` when\n\t * that rejects or the provider has none), so a test can fail where the shortcut keeps an answer the full read would\n\t * not. The summary keeps the re-measure's answer either way. The full read goes through the same provider and\n\t * caches as the shortcut, so it checks the shortcut's reasoning, not whether those caches are current. The callback\n\t * should record a mismatch for the test to fail on rather than throw: a throw is taken for a failed base read.\n\t * Unset, it costs one check of the option.\n\t */\n\treadonly verifyBaseRemeasure?: (remeasured: AheadOfBase, full: AheadOfBase | undefined) => void;\n}\n\n/** Read groups, as bit flags so a pending set is a number rather than an allocation per event */\nconst dirtyField = 1;\nconst branchField = 2;\nconst baseField = 4;\n/** Re-reads only the stored values the base read consults, and the base itself only when they changed */\nconst baseKeysField = 8;\n/** Re-reads the provider's Git config inputs before deciding whether the base needs a refresh */\nconst baseConfigField = 16;\n/**\n * Refreshes the base after only refs it depends on moved, each named exactly (in {@link SummaryEntry.baseMoves}): when\n * the base's own ref just moved forward, it is re-measured rather than resolved again. {@link baseField} overrides it.\n */\nconst baseMoveField = 32;\n/** Every group a full read covers — a base read refreshes the {@link BaseKeys} snapshot itself */\nconst allFields = dirtyField | branchField | baseField;\n\n// Shared config and ref changes reach every checkout of a repository together; bound that repository's Git fan-out so\n// input can keep moving. Per repository, so a burst in one, or reads hung in it, never holds up another's.\nconst maxConcurrentReadsPerRepository = 4;\n// And across repositories, so a start (or an overflow rescan) over many of them never runs four reads for each at once.\n// Each read runs a couple of git processes side by side, so eight leave most of the process queue to foreground\n// commands and the CPU to the host; a hung read still frees its slot once git's command timeout ends it.\nconst maxConcurrentReads = 8;\n\nconst emptyRefs: readonly string[] = Object.freeze([]);\n\n/**\n * The current branch's stored merge targets (user, detected) and recorded base, as `getAheadOfBase` consults them.\n * A detached HEAD has no stored values, so it holds {@link noBaseKeys}.\n */\ntype BaseKeys = readonly [string | undefined, string | undefined, string | undefined];\n\nconst noBaseKeys: BaseKeys = [undefined, undefined, undefined];\n\n/**\n * The provider's Git config inputs to the base as one read found them: its entries (see\n * `getAheadOfBaseConfigEntries`), or the opaque key of a provider without them\n */\ntype BaseConfig = readonly (readonly [key: string, value: string | undefined])[] | string;\n\ninterface ReadResult {\n\treadonly summary: WorktreeSummary;\n\treadonly baseKeys: BaseKeys | undefined;\n\treadonly baseConfigKey: string | undefined;\n\t/** The default branch the base read resolved (see `AheadOfBase.defaultBranch`); `undefined` when unknown */\n\treadonly baseDefaultBranch: string | null | undefined;\n\t/** Why the base read failed when the rest of the summary was read; the summary is then stale */\n\treadonly baseError: unknown;\n}\n\ninterface SummaryEntry {\n\treadonly key: string;\n\t/**\n\t * The repository's common git directory, which keys its read slots; set once the watch starts, and kept after the\n\t * last subscriber leaves, since a read still in flight then frees the slots it took under it\n\t */\n\tslotsKey: string | undefined;\n\t/** Aborted when the last subscriber leaves, cancelling any in-flight read */\n\treadonly abort: AbortController;\n\treadonly subscribers: Set<SummarySubscription>;\n\thandle: WatchHandle | undefined;\n\t/** The timing-neutral listening subscriptions and their event listeners */\n\tlisteners: UnifiedDisposable[] | undefined;\n\tvalue: WorktreeSummary | undefined;\n\t/** The stored base values as of the last base read; a `gk` config change re-reads the base only when they differ */\n\tbaseKeys: BaseKeys | undefined;\n\t/**\n\t * The config inputs the last base read consulted, as of just before it (see {@link getBaseConfigKey}); a config\n\t * change re-reads the base only when they differ\n\t */\n\tbaseConfigKey: string | undefined;\n\t/** The default branch the last base read resolved, for a re-measure to check; `undefined` when unknown */\n\tbaseDefaultBranch: string | null | undefined;\n\t/** Why the latest read failed while there was no value to keep */\n\terror: Error | undefined;\n\t/** Read groups due on the next run */\n\tpending: number;\n\t/** The refs the base depends on that moved, by name, for a pending {@link baseMoveField} */\n\tbaseMoves: Set<string> | undefined;\n\t/** The active run (or the start-up, which ends in one); cleared in the same turn the loop sees nothing pending */\n\trunning: Promise<void> | undefined;\n\tdisposed: boolean;\n}\n\nclass SummarySubscription implements WorktreeSummarySubscription {\n\tprivate readonly emitter = new Emitter<WorktreeSummary>();\n\t/** Listener-less session subscriptions that carry this subscriber's timing into the session */\n\tprivate timing: UnifiedDisposable[] | undefined;\n\tprivate disposed = false;\n\n\tconstructor(\n\t\tprivate readonly entry: SummaryEntry,\n\t\tprivate readonly options: WorktreeSummarySubscribeOptions | undefined,\n\t\tprivate readonly onDispose: (subscription: SummarySubscription) => void,\n\t) {}\n\n\tget current(): WorktreeSummary | undefined {\n\t\treturn this.disposed ? undefined : this.entry.value;\n\t}\n\n\tget onDidChange(): Event<WorktreeSummary> {\n\t\treturn this.emitter.event;\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\t\tif (this.timing != null) {\n\t\t\tfor (const d of this.timing) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tthis.timing = undefined;\n\t\t}\n\t\tthis.emitter.dispose();\n\t\tthis.onDispose(this);\n\t}\n\n\tattach(session: RepositoryWatchSession): void {\n\t\tif (this.disposed || this.timing != null) return;\n\n\t\tthis.timing = [\n\t\t\tsession.subscribe({ delayMs: this.options?.delayMs, maxWaitMs: this.options?.maxWaitMs }),\n\t\t\tsession.subscribeToWorkingTree({\n\t\t\t\tdelayMs: this.options?.workingTreeDelayMs,\n\t\t\t\tmaxWaitMs: this.options?.workingTreeMaxWaitMs,\n\t\t\t}),\n\t\t];\n\t}\n\n\tfire(summary: WorktreeSummary): void {\n\t\tthis.emitter.fire(summary);\n\t}\n}\n\n/**\n * A cached, change-notifying summary of each subscribed worktree — its branch, upstream and ahead/behind, whether it\n * has working changes, and how far it is past its base — kept current by the repository watch layer.\n *\n * **Lifecycle.** Entries are per repository key and ref-counted across {@link subscribe} calls. The first subscriber\n * starts watching the worktree and reads everything; the last one to leave aborts any in-flight read, releases the\n * watch and drops the value. Each subscriber's timing options join the watch session's cadence (the shortest wins);\n * the service's own listening subscriptions never constrain it.\n *\n * **Field-scoped recompute.** The summary is read in three groups — `dirty` (working changes), `branch` (branch,\n * HEAD's commit, upstream, ahead/behind) and `base` (ahead of base, the base's tip, and the refs it depends on) — and\n * an event recomputes only the groups it can affect: a working-tree edit, an index or ignore-rule change reads only\n * `dirty`; the upstream moving reads only `branch`; a ref the base depends on reads only `base`, and when the refs that\n * moved are named and include the base's own ref, as a fetch names them, the provider first tries re-measuring the\n * base from the previous one (one count per worktree when it only moved forward); a `gk` config change\n * (mostly bookkeeping writes to the shared `.git/gk/config`), or core's own write of the branch's merge target or\n * base, re-reads the branch's stored merge targets and recorded base, and reads `base` only when one of them changed;\n * a `.git/config` change reads `branch` and the provider's config inputs to the base (one cached read shared by the\n * repository's checkouts, or one per checkout where config can differ by checkout), and reads `base` only when the\n * inputs its last base read consulted (every branch's tracking but those of branches it didn't depend on), the stored\n * values, HEAD's commit or the upstream changed, unless it was confined to the\n * `branch.<name>` sections of branches the summary neither is on, tracks nor bases on (its `configBranches`), which\n * reads nothing; the checked-out branch moving, or a HEAD change, reads all of them. Stash, unrelated tags and\n * branches, and the like read nothing. One run is in flight per worktree at a time, at most four per repository (the\n * worktrees sharing a common git directory), and at most eight across the service. Runs take turns first come, first\n * served, except that one whose repository is at its cap lets later runs of other repositories go first, so a burst in\n * one repository never delays another's reads: events while a run waits for its turn join it, and events during a run\n * are merged into a single follow-up run.\n *\n * **Value-change-only notification.** {@link WorktreeSummarySubscription.onDidChange} and {@link onDidChangeSummary}\n * fire only when some field's value differs from the previous summary — a recompute that reads the same answer is\n * silent, so a consumer can re-render on every event without diffing. A failed recompute keeps the last values and\n * sets `stale` (firing once); the next successful one clears it.\n */\nexport class WorktreeSummaryService implements UnifiedDisposable {\n\tprivate readonly _onDidChangeSummary = new Emitter<WorktreeSummary>();\n\t/** Fires when the summary of any subscribed worktree changes value */\n\tget onDidChangeSummary(): Event<WorktreeSummary> {\n\t\treturn this._onDidChangeSummary.event;\n\t}\n\n\tprivate readonly entries = new Map<string, SummaryEntry>();\n\t/** Reads running for each repository, by its slots key; a repository with none has no entry */\n\tprivate readonly activeReadsByRepository = new Map<string, number>();\n\tprivate activeReads = 0;\n\t/** Entries waiting for a read slot, oldest first */\n\tprivate readonly readWaiters = new Map<SummaryEntry, (acquired: boolean) => void>();\n\tprivate readonly watchService: RepositoryWatchService;\n\tprivate readonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\tprivate readonly getCommonPath: ((repoPath: string) => string | undefined) | undefined;\n\tprivate readonly verifyBaseRemeasure: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];\n\tprivate readonly mergeTargetsListener: UnifiedDisposable | undefined;\n\tprivate _disposed = false;\n\n\tconstructor(options: WorktreeSummaryServiceOptions) {\n\t\tthis.watchService = options.watchService;\n\t\tthis.getRepository = options.getRepository;\n\t\tthis.getCommonPath = options.getCommonPath;\n\t\tthis.verifyBaseRemeasure = options.verifyBaseRemeasure;\n\t\tthis.mergeTargetsListener = options.onDidChangeMergeTargets?.(e => this.onMergeTargetsChanged(e));\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\t\tfor (const resolve of this.readWaiters.values()) {\n\t\t\tresolve(false);\n\t\t}\n\t\tthis.readWaiters.clear();\n\t\tthis.mergeTargetsListener?.dispose();\n\t\tfor (const entry of [...this.entries.values()]) {\n\t\t\tfor (const subscription of [...entry.subscribers]) {\n\t\t\t\tsubscription.dispose();\n\t\t\t}\n\t\t}\n\t\tthis.entries.clear();\n\t\tthis._onDidChangeSummary.dispose();\n\t}\n\n\t/** Subscribes to a worktree's summary, starting to watch and read it if this is its first subscriber */\n\tsubscribe(repoPath: string, options?: WorktreeSummarySubscribeOptions): WorktreeSummarySubscription {\n\t\tconst key = getRepositoryKey(repoPath);\n\n\t\tlet entry = this._disposed ? undefined : this.entries.get(key);\n\t\tconst isNew = entry == null;\n\t\tif (entry == null) {\n\t\t\tentry = createEntry(key);\n\t\t\tif (this._disposed) {\n\t\t\t\tentry.disposed = true;\n\t\t\t} else {\n\t\t\t\tthis.entries.set(key, entry);\n\t\t\t}\n\t\t}\n\n\t\tconst target = entry;\n\t\tconst subscription = new SummarySubscription(target, options, s => this.release(target, s));\n\t\ttarget.subscribers.add(subscription);\n\n\t\tif (isNew) {\n\t\t\tif (!target.disposed) {\n\t\t\t\ttarget.pending = allFields | baseConfigField;\n\t\t\t\ttarget.running = this.start(target);\n\t\t\t}\n\t\t} else if (target.handle != null) {\n\t\t\tsubscription.attach(target.handle.session);\n\t\t} else if (target.running == null && !target.disposed) {\n\t\t\t// The start failed (no repository or git directory yet); a new subscriber retries it\n\t\t\ttarget.pending = allFields | baseConfigField;\n\t\t\ttarget.running = this.start(target);\n\t\t}\n\n\t\treturn subscription;\n\t}\n\n\t/**\n\t * Returns a worktree's summary. For a subscribed worktree, the cached value — after waiting for any run in flight\n\t * or pending; it rejects with the read's error when there is still no value. Otherwise a one-off full read that\n\t * is neither cached nor watched.\n\t */\n\tasync getSummary(repoPath: string, cancellation?: AbortSignal): Promise<WorktreeSummary> {\n\t\tconst key = getRepositoryKey(repoPath);\n\t\tconst entry = this.entries.get(key);\n\t\tif (entry == null) {\n\t\t\tconst repo = this.getRepository(key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${key}'`);\n\n\t\t\tconst result = await readSummary(\n\t\t\t\trepo,\n\t\t\t\tkey,\n\t\t\t\tallFields,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tcancellation,\n\t\t\t\tthis.verifyBaseRemeasure,\n\t\t\t);\n\t\t\treturn result.summary;\n\t\t}\n\n\t\tif (entry.running != null) {\n\t\t\tawait (cancellation != null ? raceWithSignal(entry.running, cancellation) : entry.running);\n\t\t}\n\t\tif (entry.disposed) throw new CancellationError();\n\t\tif (entry.value != null) return entry.value;\n\n\t\tthrow entry.error ?? new Error(`No summary available for '${key}'`);\n\t}\n\n\tgetResourceUsage(): ResourceUsage {\n\t\treturn { 'worktreeSummary.entries.count': this.entries.size };\n\t}\n\n\tprivate async start(entry: SummaryEntry): Promise<void> {\n\t\tlet gitDir: GitDir | undefined;\n\t\ttry {\n\t\t\tgitDir = await this.getRepository(entry.key)?.config.getGitDir?.();\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed) return;\n\n\t\t\tentry.running = undefined;\n\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\tthis.fail(entry, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tconst handle = gitDir != null ? this.watchService.watch(entry.key, gitDir) : undefined;\n\t\tif (gitDir == null || handle == null) {\n\t\t\tentry.running = undefined;\n\t\t\tthis.fail(entry, new Error(`Unable to watch '${entry.key}': no repository or git directory`));\n\t\t\treturn;\n\t\t}\n\n\t\tentry.handle = handle;\n\t\tentry.slotsKey = getRepositoryKey(gitDir.commonUri ?? gitDir.uri);\n\t\tconst session = handle.session;\n\t\t// Timing-neutral: the cadence comes from the subscribers' own session subscriptions (see `attach`)\n\t\tconst repoSub = session.subscribe({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tconst workingTreeSub = session.subscribeToWorkingTree({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tentry.listeners = [\n\t\t\trepoSub.onDidChange(e => this.onRepoChanged(entry, e)),\n\t\t\tworkingTreeSub.onDidChangeWorkingTree(() => this.queue(entry, dirtyField)),\n\t\t\trepoSub,\n\t\t\tworkingTreeSub,\n\t\t];\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.attach(session);\n\t\t}\n\n\t\tawait this.drain(entry);\n\t}\n\n\tprivate onRepoChanged(entry: SummaryEntry, e: WatcherRepoChangeEvent): void {\n\t\tconst value = entry.value;\n\t\tif (value == null || e.changed('unknown', 'head')) {\n\t\t\tthis.queue(entry, allFields | (e.changed('config') ? baseConfigField : 0));\n\t\t\treturn;\n\t\t}\n\n\t\tlet fields = 0;\n\t\tif (e.changed('index', 'ignores')) {\n\t\t\tfields |= dirtyField;\n\t\t}\n\t\tif (e.changed('config') && e.mayHaveChangedConfig(getConfigRelevantRefs(value))) {\n\t\t\tfields |= branchField | baseKeysField | baseConfigField;\n\t\t}\n\t\tif (e.changed('gkConfig')) {\n\t\t\tfields |= baseKeysField;\n\t\t}\n\t\tif (value.branch != null && e.mayHaveChangedRef(`refs/heads/${value.branch}`)) {\n\t\t\tfields |= allFields;\n\t\t}\n\t\t// The upstream's short name doesn't say whether it's remote or local (`--track main`), so test both forms\n\t\tif (\n\t\t\tvalue.upstream != null &&\n\t\t\t(e.mayHaveChangedRef(`refs/remotes/${value.upstream.name}`) ||\n\t\t\t\te.mayHaveChangedRef(`refs/heads/${value.upstream.name}`))\n\t\t) {\n\t\t\tfields |= branchField;\n\t\t}\n\t\tlet baseMoves: string[] | undefined;\n\t\tif ((fields & baseField) === 0) {\n\t\t\tfor (const ref of value.baseDependsOn) {\n\t\t\t\tif (!e.mayHaveChangedRef(ref)) continue;\n\n\t\t\t\t// Only a ref named exactly is known to be the one that moved: a ref directory or a ref kind that changed\n\t\t\t\t// without names (a packed-refs rewrite, an overflow, a poll) may hide any move\n\t\t\t\tconst kind = getRefChangeKind(ref);\n\t\t\t\tif (!e.refs.has(ref) || kind == null || e.unnamedRefKinds.has(kind)) {\n\t\t\t\t\tfields |= baseField;\n\t\t\t\t\tbaseMoves = undefined;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbaseMoves ??= [];\n\t\t\t\tbaseMoves.push(ref);\n\t\t\t}\n\t\t\tif (baseMoves != null) {\n\t\t\t\tfields |= baseMoveField;\n\t\t\t}\n\t\t}\n\n\t\tthis.queue(entry, fields, baseMoves);\n\t}\n\n\t/**\n\t * Queued synchronously, so a store awaited before `getSummary` finds the run in flight. The watcher's later\n\t * `gkConfig` event for the same write then finds the keys unchanged and leaves the base alone.\n\t */\n\tprivate onMergeTargetsChanged(e: MergeTargetsChangeEvent): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\t// Before the watch starts there's nothing to refresh: the start's own read comes after the write\n\t\t\tif (entry.handle == null) continue;\n\n\t\t\t// With no value yet, the first read may have read the stored values before the write, so `queue` reads\n\t\t\t// everything again\n\t\t\tconst value = entry.value;\n\t\t\tif (value != null && (value.branch == null || (e.refs != null && !e.refs.has(value.branch)))) continue;\n\n\t\t\tconst commonPath = this.getCommonPath?.(entry.key);\n\t\t\tif (commonPath != null && commonPath !== e.repoPath) continue;\n\n\t\t\tthis.queue(entry, baseKeysField);\n\t\t}\n\t}\n\n\tprivate queue(entry: SummaryEntry, fields: number, baseMoves?: readonly string[]): void {\n\t\tif (entry.disposed || fields === 0) return;\n\n\t\t// Without a value a partial read has nothing to merge into, and a stale value's fields can't be trusted: a\n\t\t// partial read would clear `stale` while vouching for groups it never re-read\n\t\tentry.pending |= entry.value == null || entry.value.stale ? fields | allFields | baseConfigField : fields;\n\t\tif (baseMoves != null) {\n\t\t\tentry.baseMoves ??= new Set();\n\t\t\tfor (const ref of baseMoves) {\n\t\t\t\tentry.baseMoves.add(ref);\n\t\t\t}\n\t\t}\n\t\tentry.running ??= this.drain(entry);\n\t}\n\n\t/** Runs until nothing is pending. Never rejects — every failure becomes entry state. */\n\tprivate async drain(entry: SummaryEntry): Promise<void> {\n\t\twhile (entry.pending !== 0 && !entry.disposed) {\n\t\t\tif (!(await this.acquireRead(entry))) break;\n\n\t\t\ttry {\n\t\t\t\tif (entry.disposed) break;\n\n\t\t\t\t// Consume after acquiring: changes that arrived while queued belong to this same read.\n\t\t\t\tconst fields = entry.pending;\n\t\t\t\tconst baseMoves = entry.baseMoves;\n\t\t\t\tentry.pending = 0;\n\t\t\t\tentry.baseMoves = undefined;\n\t\t\t\tawait this.run(entry, fields, baseMoves);\n\t\t\t} finally {\n\t\t\t\tthis.releaseRead(entry);\n\t\t\t}\n\t\t}\n\t\t// Same turn as the loop's last check, so an event can't queue fields that no run will pick up\n\t\tentry.running = undefined;\n\t}\n\n\t/**\n\t * A read takes one of its repository's slots and one of the service's. Each release hands what it frees to the\n\t * waiters first, so a new read takes a slot only when no waiter can use it.\n\t */\n\tprivate acquireRead(entry: SummaryEntry): Promise<boolean> {\n\t\tif (entry.disposed || this._disposed) return Promise.resolve(false);\n\n\t\tif (this.tryTakeReadSlots(entry)) return Promise.resolve(true);\n\n\t\treturn new Promise(resolve => this.readWaiters.set(entry, resolve));\n\t}\n\n\tprivate releaseRead(entry: SummaryEntry): void {\n\t\tconst key = entry.slotsKey ?? entry.key;\n\t\tconst active = this.activeReadsByRepository.get(key);\n\t\tif (active == null) return;\n\n\t\tif (active > 1) {\n\t\t\tthis.activeReadsByRepository.set(key, active - 1);\n\t\t} else {\n\t\t\tthis.activeReadsByRepository.delete(key);\n\t\t}\n\t\tthis.activeReads--;\n\n\t\t// The oldest waiter whose repository has a free slot goes next, so a repository at its own cap never holds up\n\t\t// another's waiters, and a busy checkout rejoins the tail for a follow-up rather than starving its siblings\n\t\tfor (const [waiter, resolve] of this.readWaiters) {\n\t\t\tif (this.activeReads >= maxConcurrentReads) break;\n\n\t\t\tif (waiter.disposed || this._disposed) {\n\t\t\t\tthis.readWaiters.delete(waiter);\n\t\t\t\tresolve(false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!this.tryTakeReadSlots(waiter)) continue;\n\n\t\t\tthis.readWaiters.delete(waiter);\n\t\t\tresolve(true);\n\t\t}\n\t}\n\n\tprivate tryTakeReadSlots(entry: SummaryEntry): boolean {\n\t\tif (this.activeReads >= maxConcurrentReads) return false;\n\n\t\tconst key = entry.slotsKey ?? entry.key;\n\t\tconst active = this.activeReadsByRepository.get(key) ?? 0;\n\t\tif (active >= maxConcurrentReadsPerRepository) return false;\n\n\t\tthis.activeReadsByRepository.set(key, active + 1);\n\t\tthis.activeReads++;\n\t\treturn true;\n\t}\n\n\tprivate async run(entry: SummaryEntry, fields: number, baseMoves: ReadonlySet<string> | undefined): Promise<void> {\n\t\tconst previous = entry.value;\n\n\t\tlet result: ReadResult;\n\t\ttry {\n\t\t\tconst repo = this.getRepository(entry.key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${entry.key}'`);\n\n\t\t\tresult = await readSummary(\n\t\t\t\trepo,\n\t\t\t\tentry.key,\n\t\t\t\tfields,\n\t\t\t\tprevious,\n\t\t\t\tentry.baseKeys,\n\t\t\t\tentry.baseConfigKey,\n\t\t\t\tentry.baseDefaultBranch,\n\t\t\t\tbaseMoves,\n\t\t\t\tentry.abort.signal,\n\t\t\t\tthis.verifyBaseRemeasure,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed || isCancellationError(ex)) return;\n\n\t\t\tthis.fail(entry, ex);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tif (result.summary.stale && (previous == null || !previous.stale)) {\n\t\t\tLogger.warn(\n\t\t\t\t`WorktreeSummaryService: reading the base of '${entry.key}' failed: ${String(result.baseError)}`,\n\t\t\t);\n\t\t}\n\n\t\tentry.error = undefined;\n\t\tentry.baseKeys = result.baseKeys;\n\t\tentry.baseConfigKey = entry.pending & baseConfigField ? undefined : result.baseConfigKey;\n\t\tentry.baseDefaultBranch = result.baseDefaultBranch;\n\t\tif (previous != null && summariesEqual(previous, result.summary)) return;\n\n\t\tentry.value = result.summary;\n\t\tthis.fireChange(entry, result.summary);\n\t}\n\n\tprivate fail(entry: SummaryEntry, ex: unknown): void {\n\t\tconst previous = entry.value;\n\t\tif (previous == null) {\n\t\t\tif (entry.error == null) {\n\t\t\t\tLogger.warn(`WorktreeSummaryService: reading '${entry.key}' failed: ${String(ex)}`);\n\t\t\t}\n\t\t\tentry.error = ex instanceof Error ? ex : new Error(String(ex));\n\t\t\treturn;\n\t\t}\n\t\tif (previous.stale) return;\n\n\t\tLogger.warn(`WorktreeSummaryService: refreshing '${entry.key}' failed: ${String(ex)}`);\n\t\tconst summary: WorktreeSummary = { ...previous, stale: true };\n\t\tentry.value = summary;\n\t\tthis.fireChange(entry, summary);\n\t}\n\n\tprivate fireChange(entry: SummaryEntry, summary: WorktreeSummary): void {\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.fire(summary);\n\t\t}\n\t\tthis._onDidChangeSummary.fire(summary);\n\t}\n\n\tprivate release(entry: SummaryEntry, subscription: SummarySubscription): void {\n\t\tentry.subscribers.delete(subscription);\n\t\tif (entry.subscribers.size !== 0) return;\n\n\t\tentry.disposed = true;\n\t\t// A waiter holds no slot, so dropping it frees none\n\t\tconst cancelRead = this.readWaiters.get(entry);\n\t\tif (cancelRead != null) {\n\t\t\tthis.readWaiters.delete(entry);\n\t\t\tcancelRead(false);\n\t\t}\n\t\tentry.abort.abort();\n\t\tif (entry.listeners != null) {\n\t\t\tfor (const d of entry.listeners) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tentry.listeners = undefined;\n\t\t}\n\t\tentry.handle?.dispose();\n\t\tentry.handle = undefined;\n\t\tentry.value = undefined;\n\t\tentry.baseKeys = undefined;\n\t\tentry.baseConfigKey = undefined;\n\t\tentry.baseDefaultBranch = undefined;\n\t\tentry.pending = 0;\n\t\tentry.baseMoves = undefined;\n\t\tentry.running = undefined;\n\n\t\tif (this.entries.get(entry.key) === entry) {\n\t\t\tthis.entries.delete(entry.key);\n\t\t}\n\t}\n}\n\n/**\n * The local branches whose config sections a summary reads: its branch's (upstream and base keys), and those of the\n * branches it tracks or bases on. The upstream's short name doesn't say whether it's local, so it counts as local too.\n */\nfunction* getConfigRelevantRefs(value: WorktreeSummary): Iterable<string> {\n\tif (value.branch != null) {\n\t\tyield `refs/heads/${value.branch}`;\n\t}\n\tif (value.upstream != null) {\n\t\tyield `refs/heads/${value.upstream.name}`;\n\t}\n\tyield* value.baseDependsOn;\n}\n\nfunction createEntry(key: string): SummaryEntry {\n\treturn {\n\t\tkey: key,\n\t\tslotsKey: undefined,\n\t\tabort: new AbortController(),\n\t\tsubscribers: new Set(),\n\t\thandle: undefined,\n\t\tlisteners: undefined,\n\t\tvalue: undefined,\n\t\tbaseKeys: undefined,\n\t\tbaseConfigKey: undefined,\n\t\tbaseDefaultBranch: undefined,\n\t\terror: undefined,\n\t\tpending: 0,\n\t\tbaseMoves: undefined,\n\t\trunning: undefined,\n\t\tdisposed: false,\n\t};\n}\n\n/**\n * Reads the due groups and fills the rest from `previous` (every group when there is none). The branch read comes\n * before the base read, which needs its name; the dirty read runs alongside both. A branch read that finds a\n * different branch (or detached state) also reads the base and dirty groups, and stored base values that differ\n * from `previousBaseKeys` read the base group. A base due only because `baseMoves` moved, with HEAD, the stored\n * values and the config inputs as before, is first re-measured from `previous` and the default branch its read\n * resolved, `previousBaseDefaultBranch` (see `getAheadOfBaseAfterBaseMove`), and read in full only when that can't\n * answer. A failed base read doesn't fail the rest: the summary comes back stale, keeping the previous\n * base and `baseDependsOn` only while the branch is the same.\n */\nasync function readSummary(\n\trepo: RepositoryService,\n\tkey: string,\n\tfields: number,\n\tprevious: WorktreeSummary | undefined,\n\tpreviousBaseKeys: BaseKeys | undefined,\n\tpreviousBaseConfigKey: string | undefined,\n\tpreviousBaseDefaultBranch: string | null | undefined,\n\tbaseMoves: ReadonlySet<string> | undefined,\n\tsignal: AbortSignal | undefined,\n\tverifyBaseRemeasure: WorktreeSummaryServiceOptions['verifyBaseRemeasure'],\n): Promise<ReadResult> {\n\tif (previous == null) {\n\t\tfields |= allFields;\n\t}\n\n\tlet dirtyRead = fields & dirtyField ? readDirty(repo, signal) : undefined;\n\n\tlet branch = previous?.branch;\n\tlet detached = previous?.detached ?? false;\n\tlet sha = previous?.sha;\n\tlet upstream = previous?.upstream;\n\tlet ahead = previous?.ahead ?? 0;\n\tlet behind = previous?.behind ?? 0;\n\tif (fields & branchField) {\n\t\tconst current = await repo.branches.getBranch(undefined, undefined, signal);\n\t\tdetached = current?.detached ?? false;\n\t\tbranch = detached ? undefined : current?.name || undefined;\n\t\tsha = current?.sha;\n\t\tupstream = current?.upstream != null ? await toSummaryUpstream(repo, current.upstream, signal) : undefined;\n\t\tahead = current?.upstream?.state.ahead ?? 0;\n\t\tbehind = current?.upstream?.state.behind ?? 0;\n\n\t\tif (previous != null && (branch !== previous.branch || detached !== previous.detached)) {\n\t\t\tfields |= baseField;\n\t\t\tdirtyRead ??= readDirty(repo, signal);\n\t\t}\n\t}\n\n\tlet base = previous?.base;\n\tlet dependsOn = previous?.baseDependsOn ?? emptyRefs;\n\tlet baseKeys = previousBaseKeys;\n\tlet baseKeysRead = false;\n\tlet baseConfigKey = previousBaseConfigKey;\n\tlet baseConfig: BaseConfig | undefined;\n\tlet baseConfigRead = false;\n\tlet baseDefaultBranch = previousBaseDefaultBranch;\n\tlet baseFailed = false;\n\tlet baseError: unknown;\n\t// A detached HEAD is measured as `undefined`; an unborn HEAD with no name has nothing to measure\n\tconst baseReadable = repo.branches.getAheadOfBase != null && (detached || branch != null);\n\tif (fields & baseConfigField && !(fields & baseField) && baseReadable) {\n\t\tbaseConfig = await readBaseConfig(repo, signal);\n\t\tbaseConfigRead = true;\n\t\t// Without a base read due, the branch and upstream are as before, so the inputs to compare are the ones the last\n\t\t// base read consulted\n\t\tconst key = previous != null ? getBaseConfigKey(baseConfig, previous) : undefined;\n\t\tif (\n\t\t\tkey == null ||\n\t\t\tkey !== previousBaseConfigKey ||\n\t\t\tsha !== previous?.sha ||\n\t\t\t!upstreamsEqual(upstream, previous?.upstream)\n\t\t) {\n\t\t\tfields |= baseField;\n\t\t}\n\t}\n\tif (fields & baseKeysField && !(fields & baseField) && baseReadable) {\n\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\tbaseKeysRead = true;\n\t\tif (baseKeys == null || previousBaseKeys == null || !baseKeysEqual(baseKeys, previousBaseKeys)) {\n\t\t\tfields |= baseField;\n\t\t}\n\t}\n\tif (fields & (baseField | baseMoveField)) {\n\t\tif (!baseReadable) {\n\t\t\tbase = undefined;\n\t\t\tdependsOn = emptyRefs;\n\t\t\tbaseKeys = undefined;\n\t\t\tbaseConfigKey = undefined;\n\t\t\tbaseDefaultBranch = undefined;\n\t\t} else {\n\t\t\t// Snapshot before the base read, so a write it misses is one the next `gk` config event sees as a change\n\t\t\tif (!baseKeysRead) {\n\t\t\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\t\t}\n\t\t\tif (!baseConfigRead && (previous != null || (fields & baseConfigField) !== 0)) {\n\t\t\t\tbaseConfig = await readBaseConfig(repo, signal);\n\t\t\t\tbaseConfigRead = true;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tlet aheadOfBase: AheadOfBase | undefined;\n\t\t\t\t// The config inputs too, though no config change is due: a write can land between the last read and its\n\t\t\t\t// event (core evicts its own write's caches before the event fires), and the re-measure reads no config,\n\t\t\t\t// so the key kept with it would vouch for config the base never saw\n\t\t\t\tif (\n\t\t\t\t\t!(fields & baseField) &&\n\t\t\t\t\tbaseMoves != null &&\n\t\t\t\t\tbaseMoves.size !== 0 &&\n\t\t\t\t\tprevious?.base != null &&\n\t\t\t\t\tsha != null &&\n\t\t\t\t\tsha === previous.sha &&\n\t\t\t\t\tbaseKeys != null &&\n\t\t\t\t\tpreviousBaseKeys != null &&\n\t\t\t\t\tbaseKeysEqual(baseKeys, previousBaseKeys) &&\n\t\t\t\t\tgetBaseConfigKey(baseConfig, previous) === previousBaseConfigKey\n\t\t\t\t) {\n\t\t\t\t\taheadOfBase = await repo.branches.getAheadOfBaseAfterBaseMove?.(\n\t\t\t\t\t\tbranch,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thead: sha,\n\t\t\t\t\t\t\tbase: previous.base,\n\t\t\t\t\t\t\tdependsOn: previous.baseDependsOn,\n\t\t\t\t\t\t\t...(previousBaseDefaultBranch !== undefined\n\t\t\t\t\t\t\t\t? { defaultBranch: previousBaseDefaultBranch }\n\t\t\t\t\t\t\t\t: undefined),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[...baseMoves],\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t);\n\t\t\t\t\tif (aheadOfBase != null && verifyBaseRemeasure != null) {\n\t\t\t\t\t\tlet full: AheadOfBase | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfull = await repo.branches.getAheadOfBase?.(branch, undefined, signal);\n\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\tif (isCancellationError(ex)) throw ex;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tverifyBaseRemeasure(aheadOfBase, full);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\taheadOfBase ??= await repo.branches.getAheadOfBase?.(branch, undefined, signal);\n\t\t\t\tbase = aheadOfBase?.base;\n\t\t\t\tdependsOn = aheadOfBase?.dependsOn ?? emptyRefs;\n\t\t\t\tbaseDefaultBranch = aheadOfBase?.defaultBranch;\n\t\t\t} catch (ex) {\n\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\tbaseFailed = true;\n\t\t\t\tbaseError = ex;\n\t\t\t\t// Keep `baseDependsOn` for the same branch: a fetch moving one of its refs is what retries the base\n\t\t\t\tconst sameBranch = previous != null && branch === previous.branch && detached === previous.detached;\n\t\t\t\tbase = sameBranch ? previous.base : undefined;\n\t\t\t\tdependsOn = sameBranch ? previous.baseDependsOn : emptyRefs;\n\t\t\t\tbaseKeys = undefined;\n\t\t\t\tbaseConfigRead = false;\n\t\t\t\tbaseConfigKey = undefined;\n\t\t\t\tbaseDefaultBranch = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst dirty = dirtyRead != null ? await dirtyRead : (previous?.dirty ?? false);\n\n\tconst summary: WorktreeSummary = {\n\t\trepoPath: key,\n\t\tbranch: branch,\n\t\tdetached: detached,\n\t\tsha: sha,\n\t\tupstream: upstream,\n\t\tahead: ahead,\n\t\tbehind: behind,\n\t\tdirty: dirty,\n\t\tbase: base,\n\t\tbaseDependsOn: dependsOn,\n\t\tstale: baseFailed,\n\t};\n\t// Keyed on what this run's base read (or the one it kept) consulted, from the inputs read before it\n\tif (baseConfigRead) {\n\t\tbaseConfigKey = getBaseConfigKey(baseConfig, summary);\n\t}\n\n\treturn {\n\t\tsummary: summary,\n\t\tbaseKeys: baseKeys,\n\t\tbaseConfigKey: baseConfigKey,\n\t\tbaseDefaultBranch: baseDefaultBranch,\n\t\tbaseError: baseError,\n\t};\n}\n\nasync function readBaseConfig(\n\trepo: RepositoryService,\n\tsignal: AbortSignal | undefined,\n): Promise<BaseConfig | undefined> {\n\ttry {\n\t\tif (repo.branches.getAheadOfBaseConfigEntries != null) {\n\t\t\treturn await repo.branches.getAheadOfBaseConfigEntries(signal);\n\t\t}\n\t\treturn await repo.branches.getAheadOfBaseConfigKey?.(signal);\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\n/**\n * The key of the config inputs a base read for `value` consulted: every entry except the `branch.<name>` ones of local\n * branches outside its {@link getConfigRelevantRefs}, which the read never resolved. So a write to another branch's\n * section, confined (which wakes no summary but changes the inputs) or not, leaves the base alone. An opaque key is\n * taken whole.\n */\nfunction getBaseConfigKey(config: BaseConfig | undefined, value: WorktreeSummary): string | undefined {\n\tif (config == null || typeof config === 'string') return config;\n\n\tconst branches = new Set<string>();\n\tfor (const ref of getConfigRelevantRefs(value)) {\n\t\tif (ref.startsWith('refs/heads/')) {\n\t\t\tbranches.add(ref.substring('refs/heads/'.length));\n\t\t}\n\t}\n\n\tlet key = '';\n\tfor (const [name, entry] of config) {\n\t\t// The subsection is everything between the first and last dot, so a branch name can hold dots\n\t\tconst last = name.lastIndexOf('.');\n\t\tif (\n\t\t\tname.startsWith('branch.') &&\n\t\t\tlast > 'branch.'.length &&\n\t\t\t!branches.has(name.substring('branch.'.length, last))\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tkey += entry != null ? `${name}\\n${entry}\\0` : `${name}\\0`;\n\t}\n\treturn key;\n}\n\n/**\n * Reads {@link BaseKeys} for `branch`, normalized the way `getAheadOfBase` reads them. `undefined` when any read\n * failed — an unknown snapshot, so the next `gk` config change reads the base rather than trusting it. A detached\n * HEAD (`branch` undefined) has no stored values to read.\n */\nasync function readBaseKeys(repo: RepositoryService, branch: string | undefined): Promise<BaseKeys | undefined> {\n\tif (branch == null) return noBaseKeys;\n\n\ttry {\n\t\tconst [user, detected, recorded] = await Promise.all([\n\t\t\trepo.branches.getStoredUserMergeTargetBranchName?.(branch),\n\t\t\trepo.branches.getStoredDetectedMergeTargetBranchName?.(branch),\n\t\t\trepo.config.getGkConfig?.(`branch.${branch}.gk-merge-base`),\n\t\t]);\n\t\treturn [user?.trim() || undefined, detected?.trim() || undefined, recorded?.trim() || undefined];\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\nfunction baseKeysEqual(a: BaseKeys, b: BaseKeys): boolean {\n\treturn a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n}\n\nfunction readDirty(repo: RepositoryService, signal: AbortSignal | undefined): Promise<boolean> {\n\tconst read = repo.status.hasWorkingChanges({ untracked: true }, signal);\n\t// Awaited only after the branch and base reads; if one of those throws first, this rejection has no other handler\n\tread.catch(noop);\n\treturn read;\n}\n\nfunction noop(): void {}\n\n/**\n * Uses the remote and branch git reported for the upstream; only when it reported neither does this split the name\n * at the longest remote name it starts with (no split when none matches or nothing follows it)\n */\nasync function toSummaryUpstream(\n\trepo: RepositoryService,\n\tupstream: GitTrackingUpstream,\n\tsignal: AbortSignal | undefined,\n): Promise<WorktreeSummaryUpstream> {\n\tconst { name, missing } = upstream;\n\n\tif (upstream.local) return { name: name, remote: undefined, branch: undefined, missing: missing };\n\n\tif (upstream.remote) {\n\t\tconst { remote } = upstream;\n\t\tconst branch =\n\t\t\tupstream.remoteBranch || (name.startsWith(`${remote}/`) ? name.substring(remote.length + 1) : undefined);\n\t\treturn { name: name, remote: branch ? remote : undefined, branch: branch || undefined, missing: missing };\n\t}\n\n\tlet remote: string | undefined;\n\tfor (const r of await repo.remotes.getRemotes(undefined, signal)) {\n\t\tif (r.name && name.startsWith(`${r.name}/`) && (remote == null || r.name.length > remote.length)) {\n\t\t\tremote = r.name;\n\t\t}\n\t}\n\n\tconst branch = remote != null ? name.substring(remote.length + 1) : undefined;\n\treturn {\n\t\tname: name,\n\t\tremote: branch ? remote : undefined,\n\t\tbranch: branch || undefined,\n\t\tmissing: missing,\n\t};\n}\n\nfunction upstreamsEqual(a: WorktreeSummaryUpstream | undefined, b: WorktreeSummaryUpstream | undefined): boolean {\n\treturn (\n\t\ta === b ||\n\t\t(a != null &&\n\t\t\tb != null &&\n\t\t\ta.name === b.name &&\n\t\t\ta.remote === b.remote &&\n\t\t\ta.branch === b.branch &&\n\t\t\ta.missing === b.missing)\n\t);\n}\n\nfunction summariesEqual(a: WorktreeSummary, b: WorktreeSummary): boolean {\n\treturn (\n\t\ta.repoPath === b.repoPath &&\n\t\ta.branch === b.branch &&\n\t\ta.detached === b.detached &&\n\t\ta.sha === b.sha &&\n\t\ta.ahead === b.ahead &&\n\t\ta.behind === b.behind &&\n\t\ta.dirty === b.dirty &&\n\t\ta.stale === b.stale &&\n\t\tareEqual(a.baseDependsOn, b.baseDependsOn) &&\n\t\tupstreamsEqual(a.upstream, b.upstream) &&\n\t\t(a.base === b.base ||\n\t\t\t(a.base != null &&\n\t\t\t\tb.base != null &&\n\t\t\t\ta.base.ref === b.base.ref &&\n\t\t\t\ta.base.ahead === b.base.ahead &&\n\t\t\t\ta.base.sha === b.base.sha &&\n\t\t\t\ta.base.forkPoint === b.base.forkPoint &&\n\t\t\t\ta.base.tier === b.base.tier))\n\t);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"worktreeSummary.js","names":[],"sources":["../../../../git/src/watching/worktreeSummary.ts"],"sourcesContent":["import { areEqual } from '@gitlens/utils/array.js';\nimport { CancellationError, isCancellationError, raceWithSignal } from '@gitlens/utils/cancellation.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { LruMap } from '@gitlens/utils/lruMap.js';\nimport { joinPaths } from '@gitlens/utils/path.js';\nimport type { ResourceUsage } from '@gitlens/utils/resourceUsage.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from '../cache.js';\nimport type { GitTrackingUpstream } from '../models/branch.js';\nimport type { GitRefTip } from '../models/reference.js';\nimport type { GitDir } from '../models/repository.js';\nimport type { AheadOfBase, AheadOfBaseTier } from '../providers/branches.js';\nimport type { RepositoryService } from '../repositoryService.js';\nimport type { WatcherRepoChangeEvent } from './changeEvent.js';\nimport { getRefChangeKind } from './changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle } from './watchService.js';\nimport type { RepositoryWatchSession } from './watchSession.js';\n\nexport interface WorktreeSummaryUpstream {\n\t/** The upstream's short name (`origin/main`) */\n\treadonly name: string;\n\t/**\n\t * The remote it tracks, as git reported it; on git too old to report it, the longest of the repository's remote\n\t * names that {@link name} starts with, followed by `/` (a remote name can hold a slash). `undefined`, with\n\t * {@link branch}, when it names no known remote (a branch tracking another local branch, or a remote since removed)\n\t */\n\treadonly remote: string | undefined;\n\t/**\n\t * The branch on {@link remote} — git's name for it there, which a custom fetch refspec can make differ from the end\n\t * of {@link name}; `undefined` whenever {@link remote} is\n\t */\n\treadonly branch: string | undefined;\n\treadonly missing: boolean;\n}\n\nexport interface WorktreeSummaryBase {\n\t/** The ref the count was taken against (see `AheadOfBase.base`) */\n\treadonly ref: string;\n\treadonly ahead: number;\n\t/** The commit {@link ref} pointed at when the count was read; `undefined` when it couldn't be resolved */\n\treadonly sha: string | undefined;\n\t/** Where HEAD forked from {@link ref} (see `AheadOfBase.base`) */\n\treadonly forkPoint: string | undefined;\n\t/** The source the base came from (see `AheadOfBase.base`) */\n\treadonly tier: AheadOfBaseTier;\n}\n\nexport interface WorktreeSummary {\n\t/** The repository path in {@link getRepositoryKey} form */\n\treadonly repoPath: string;\n\t/** The checked-out branch; `undefined` when detached (or unborn with no name) */\n\treadonly branch: string | undefined;\n\treadonly detached: boolean;\n\t/** HEAD's commit; `undefined` when HEAD has no commit yet */\n\treadonly sha: string | undefined;\n\treadonly upstream: WorktreeSummaryUpstream | undefined;\n\t/** Commits ahead of the upstream; 0 without one */\n\treadonly ahead: number;\n\t/** Commits behind the upstream; 0 without one */\n\treadonly behind: number;\n\t/** Whether there is any staged, unstaged or untracked (non-ignored) change */\n\treadonly dirty: boolean;\n\t/**\n\t * How far the branch — or a detached HEAD, measured against the default branch — is past its base; `undefined` when\n\t * unsupported or there is no base\n\t */\n\treadonly base: WorktreeSummaryBase | undefined;\n\t/**\n\t * Full ref names whose movement can change {@link base}, as `getAheadOfBase` reports them (see\n\t * `AheadOfBase.dependsOn`) — given with or without a base, so a watcher also learns when one appears. Empty when\n\t * the base isn't read (an unborn HEAD with no name, or a provider without `getAheadOfBase`), and when the base read\n\t * failed with no earlier list for this HEAD (the first read, or the one after a checkout).\n\t */\n\treadonly baseDependsOn: readonly string[];\n\t/**\n\t * The latest recompute failed (worktree gone, lock contention); the other fields hold the last values read. Also set\n\t * when only the base read failed: the other fields are fresh, and `base` and `baseDependsOn` are their last values,\n\t * or `undefined` and empty if the branch changed.\n\t */\n\treadonly stale: boolean;\n}\n\nexport interface WorktreeSummarySnapshot {\n\treadonly summary: WorktreeSummary;\n\t/**\n\t * When {@link WorktreeSummary.dirty} was read (`Date.now()` as the read started), for a worktree without a\n\t * subscription, whose working tree nothing watches; `undefined` for a subscribed worktree, whose summary follows\n\t * its working tree\n\t */\n\treadonly dirtyReadAt: number | undefined;\n}\n\nexport interface WorktreeSummarySubscribeOptions {\n\t/** Debounce for repo (`.git`) changes this subscriber asks of the watch session; the shortest across subscribers wins */\n\treadonly delayMs?: number;\n\t/** Cap on how long continuous repo activity can defer a recompute; `Infinity` means no cap */\n\treadonly maxWaitMs?: number;\n\t/** Debounce for working-tree changes this subscriber asks of the watch session */\n\treadonly workingTreeDelayMs?: number;\n\t/** Cap on how long continuous working-tree activity can defer a recompute; `Infinity` means no cap */\n\treadonly workingTreeMaxWaitMs?: number;\n}\n\nexport interface WorktreeSummarySubscription extends UnifiedDisposable {\n\t/** The latest summary; `undefined` until the first read succeeds, or after disposal */\n\treadonly current: WorktreeSummary | undefined;\n\t/** Fires when the summary's value changes */\n\treadonly onDidChange: Event<WorktreeSummary>;\n}\n\nexport interface WorktreeSummaryServiceOptions {\n\treadonly watchService: RepositoryWatchService;\n\treadonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\t/**\n\t * Fires as core writes a branch's merge target or base, before the store's promise resolves; the summary re-reads\n\t * that branch's stored values then, so a `getSummary` right after an awaited store sees the new base\n\t */\n\treadonly onDidChangeMergeTargets?: Event<MergeTargetsChangeEvent>;\n\t/**\n\t * Maps a worktree path to the repository common path that {@link onDidChangeMergeTargets} events carry; only that\n\t * repository's summaries react. Without it, or when it returns `undefined`, every summary reacts.\n\t */\n\treadonly getCommonPath?: (repoPath: string) => string | undefined;\n\t/**\n\t * Reads a path's modification time (ms) and size; `undefined` when it doesn't exist, and a rejection means unknown.\n\t * With it, a read of a worktree without a subscription re-reads the dirty flag only when the worktree's index file\n\t * moved since the last one (or when asked to); without it, on every such read.\n\t */\n\treadonly stat?: (path: string) => Promise<{ readonly mtime: number; readonly size: number } | undefined>;\n\t/**\n\t * For tests only; production never passes it. Called whenever the base re-measure answers in place of a full base\n\t * read, with its answer and what a full `getAheadOfBase` of the same branch answers right after (`undefined` when\n\t * that rejects or the provider has none), so a test can fail where the shortcut keeps an answer the full read would\n\t * not. The summary keeps the re-measure's answer either way. The full read goes through the same provider and\n\t * caches as the shortcut, so it checks the shortcut's reasoning, not whether those caches are current. The callback\n\t * should record a mismatch for the test to fail on rather than throw: a throw is taken for a failed base read.\n\t * Unset, it costs one check of the option.\n\t */\n\treadonly verifyBaseRemeasure?: (remeasured: AheadOfBase, full: AheadOfBase | undefined) => void;\n}\n\n/** Read groups, as bit flags so a pending set is a number rather than an allocation per event */\nconst dirtyField = 1;\nconst branchField = 2;\nconst baseField = 4;\n/** Re-reads only the stored values the base read consults, and the base itself only when they changed */\nconst baseKeysField = 8;\n/** Re-reads the provider's Git config inputs before deciding whether the base needs a refresh */\nconst baseConfigField = 16;\n/**\n * Refreshes the base after only refs it depends on moved, each named exactly (in {@link SummaryEntry.baseMoves}): when\n * the base's own ref just moved forward, it is re-measured rather than resolved again. {@link baseField} overrides it.\n */\nconst baseMoveField = 32;\n/** Every group a full read covers — a base read refreshes the {@link BaseKeys} snapshot itself */\nconst allFields = dirtyField | branchField | baseField;\n\n// Shared config and ref changes reach every checkout of a repository together; bound that repository's Git fan-out so\n// input can keep moving. Per repository, so a burst in one, or reads hung in it, never holds up another's.\nconst maxConcurrentReadsPerRepository = 4;\n// And across repositories, so a start (or an overflow rescan) over many of them never runs four reads for each at once.\n// Each read runs a couple of git processes side by side, so eight leave most of the process queue to foreground\n// commands and the CPU to the host; a hung read still frees its slot once git's command timeout ends it.\nconst maxConcurrentReads = 8;\n// Worktrees without a subscription whose last read is kept to answer from: a host listing more than this re-reads the\n// least recently asked in full\nconst maxUnwatchedReads = 256;\n\nconst emptyRefs: readonly string[] = Object.freeze([]);\n\n/**\n * The current branch's stored merge targets (user, detected) and recorded base, as `getAheadOfBase` consults them.\n * A detached HEAD has no stored values, so it holds {@link noBaseKeys}.\n */\ntype BaseKeys = readonly [string | undefined, string | undefined, string | undefined];\n\nconst noBaseKeys: BaseKeys = [undefined, undefined, undefined];\n\n/**\n * The provider's Git config inputs to the base as one read found them: its entries (see\n * `getAheadOfBaseConfigEntries`), or the opaque key of a provider without them\n */\ntype BaseConfig = readonly (readonly [key: string, value: string | undefined])[] | string;\n\ninterface ReadResult {\n\treadonly summary: WorktreeSummary;\n\treadonly baseKeys: BaseKeys | undefined;\n\treadonly baseConfigKey: string | undefined;\n\t/** The default branch the base read resolved (see `AheadOfBase.defaultBranch`); `undefined` when unknown */\n\treadonly baseDefaultBranch: string | null | undefined;\n\t/** Why the base read failed when the rest of the summary was read; the summary is then stale */\n\treadonly baseError: unknown;\n\t/** When the dirty read started; `undefined` when dirty wasn't read but kept from `previous` */\n\treadonly dirtyReadAt: number | undefined;\n}\n\n/**\n * What an unwatched read was read from, taken just before it, so a change landing during the read leaves these looking\n * older than the disk and the next read re-reads what it moved. All of it comes from core's caches, which the `.git`\n * watch drops on a HEAD or ref change, or from one stat.\n */\ninterface UnwatchedInputs {\n\t/** HEAD as `getBranch` answered it: the branch (`undefined` when detached), detached, and HEAD's commit */\n\treadonly branch: string | undefined;\n\treadonly detached: boolean;\n\treadonly sha: string | undefined;\n\t/** The upstream's short name, which a `.git/config` write can change without moving any ref */\n\treadonly upstream: string | undefined;\n\t/**\n\t * The upstream's tips as a remote-tracking and as a local branch, since its short name doesn't say which; `undefined`\n\t * without an upstream\n\t */\n\treadonly upstreamTips: string | undefined;\n\t/**\n\t * The tip of each ref the base depends on, or for a remote's `HEAD` symref the ref it points at (see\n\t * {@link getBaseInput}); `undefined` for one that doesn't exist, which counts as unchanged only while it stays out,\n\t * and `null` for one that couldn't be read, which never does\n\t */\n\treadonly baseTips: ReadonlyMap<string, string | null | undefined>;\n\t/** The size and modification time of the worktree's index file; `undefined` when unknown (no stat, or it failed) */\n\treadonly index: string | undefined;\n}\n\n/** The last read of a worktree without a subscription, answered from until its inputs move */\ninterface UnwatchedRead {\n\treadonly result: ReadResult;\n\treadonly dirtyReadAt: number;\n\treadonly inputs: UnwatchedInputs;\n\t/** Read groups due on the next read whatever the inputs say: core's own write of the branch's merge target or base */\n\tdue: number;\n}\n\n/** What an unwatched read finds of its inputs before it reads */\ninterface UnwatchedProbe {\n\treadonly branch: string | undefined;\n\treadonly detached: boolean;\n\treadonly sha: string | undefined;\n\treadonly upstream: string | undefined;\n\t/** The ref tips' commits by full name (see {@link getTipsByName}) */\n\treadonly tips: ReadonlyMap<string, string>;\n\t/** Where the `HEAD` symref of each remote it read points, by remote name (see {@link readRemoteHeads}) */\n\treadonly remoteHeads: Map<string, string | null | undefined>;\n\treadonly index: string | undefined;\n}\n\n/** A read of a worktree without a subscription, which concurrent callers share */\nclass UnwatchedFlight {\n\treadonly abort = new AbortController();\n\t/** Callers waiting for it; the read is aborted once none is */\n\tcallers = 0;\n\t/** Read groups made due while it ran, carried into what it stores */\n\tdue = 0;\n\treadonly promise: Promise<UnwatchedRead>;\n\n\tconstructor(\n\t\treadonly key: string,\n\t\t/** Whether it re-reads the dirty flag whatever its inputs say; a caller asking for that joins only such a read */\n\t\treadonly probeDirty: boolean,\n\t\t/** Set once no caller waits any longer (or the service is disposed), which aborts the read */\n\t\tpublic disposed: boolean,\n\t\tread: (flight: UnwatchedFlight) => Promise<UnwatchedRead>,\n\t) {\n\t\t// A turn later, once the caller registered it: a read failing before its first await must still find itself the\n\t\t// registered one, to drop the last read\n\t\tthis.promise = Promise.resolve().then(() => read(this));\n\t\t// Callers race it against their own cancellation, so once every one gave up its rejection has no other handler\n\t\tthis.promise.catch(noop);\n\t}\n}\n\ninterface SummaryEntry {\n\treadonly key: string;\n\t/**\n\t * The repository's common git directory, which keys its read slots; set once the watch starts, and kept after the\n\t * last subscriber leaves, since a read still in flight then frees the slots it took under it\n\t */\n\tslotsKey: string | undefined;\n\t/** Aborted when the last subscriber leaves, cancelling any in-flight read */\n\treadonly abort: AbortController;\n\treadonly subscribers: Set<SummarySubscription>;\n\thandle: WatchHandle | undefined;\n\t/** The timing-neutral listening subscriptions and their event listeners */\n\tlisteners: UnifiedDisposable[] | undefined;\n\tvalue: WorktreeSummary | undefined;\n\t/** The stored base values as of the last base read; a `gk` config change re-reads the base only when they differ */\n\tbaseKeys: BaseKeys | undefined;\n\t/**\n\t * The config inputs the last base read consulted, as of just before it (see {@link getBaseConfigKey}); a config\n\t * change re-reads the base only when they differ\n\t */\n\tbaseConfigKey: string | undefined;\n\t/** The default branch the last base read resolved, for a re-measure to check; `undefined` when unknown */\n\tbaseDefaultBranch: string | null | undefined;\n\t/** Why the latest read failed while there was no value to keep */\n\terror: Error | undefined;\n\t/** Read groups due on the next run */\n\tpending: number;\n\t/** The refs the base depends on that moved, by name, for a pending {@link baseMoveField} */\n\tbaseMoves: Set<string> | undefined;\n\t/** The active run (or the start-up, which ends in one); cleared in the same turn the loop sees nothing pending */\n\trunning: Promise<void> | undefined;\n\tdisposed: boolean;\n}\n\nclass SummarySubscription implements WorktreeSummarySubscription {\n\tprivate readonly emitter = new Emitter<WorktreeSummary>();\n\t/** Listener-less session subscriptions that carry this subscriber's timing into the session */\n\tprivate timing: UnifiedDisposable[] | undefined;\n\tprivate disposed = false;\n\n\tconstructor(\n\t\tprivate readonly entry: SummaryEntry,\n\t\tprivate readonly options: WorktreeSummarySubscribeOptions | undefined,\n\t\tprivate readonly onDispose: (subscription: SummarySubscription) => void,\n\t) {}\n\n\tget current(): WorktreeSummary | undefined {\n\t\treturn this.disposed ? undefined : this.entry.value;\n\t}\n\n\tget onDidChange(): Event<WorktreeSummary> {\n\t\treturn this.emitter.event;\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\t\tif (this.timing != null) {\n\t\t\tfor (const d of this.timing) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tthis.timing = undefined;\n\t\t}\n\t\tthis.emitter.dispose();\n\t\tthis.onDispose(this);\n\t}\n\n\tattach(session: RepositoryWatchSession): void {\n\t\tif (this.disposed || this.timing != null) return;\n\n\t\tthis.timing = [\n\t\t\tsession.subscribe({ delayMs: this.options?.delayMs, maxWaitMs: this.options?.maxWaitMs }),\n\t\t\tsession.subscribeToWorkingTree({\n\t\t\t\tdelayMs: this.options?.workingTreeDelayMs,\n\t\t\t\tmaxWaitMs: this.options?.workingTreeMaxWaitMs,\n\t\t\t}),\n\t\t];\n\t}\n\n\tfire(summary: WorktreeSummary): void {\n\t\tthis.emitter.fire(summary);\n\t}\n}\n\n/**\n * A cached, change-notifying summary of each subscribed worktree — its branch, upstream and ahead/behind, whether it\n * has working changes, and how far it is past its base — kept current by the repository watch layer.\n *\n * **Lifecycle.** Entries are per repository key and ref-counted across {@link subscribe} calls. The first subscriber\n * starts watching the worktree and reads everything; the last one to leave aborts any in-flight read, releases the\n * watch and drops the value. Each subscriber's timing options join the watch session's cadence (the shortest wins);\n * the service's own listening subscriptions never constrain it.\n *\n * **Field-scoped recompute.** The summary is read in three groups — `dirty` (working changes), `branch` (branch,\n * HEAD's commit, upstream, ahead/behind) and `base` (ahead of base, the base's tip, and the refs it depends on) — and\n * an event recomputes only the groups it can affect: a working-tree edit, an index or ignore-rule change reads only\n * `dirty`; the upstream moving reads only `branch`; a ref the base depends on reads only `base`, and when the refs that\n * moved are named and include the base's own ref, as a fetch names them, the provider first tries re-measuring the\n * base from the previous one (one count per worktree when it only moved forward); a `gk` config change\n * (mostly bookkeeping writes to the shared `.git/gk/config`), or core's own write of the branch's merge target or\n * base, re-reads the branch's stored merge targets and recorded base, and reads `base` only when one of them changed;\n * a `.git/config` change reads `branch` and the provider's config inputs to the base (one cached read shared by the\n * repository's checkouts, or one per checkout where config can differ by checkout), and reads `base` only when the\n * inputs its last base read consulted (every branch's tracking but those of branches it didn't depend on), the stored\n * values, HEAD's commit or the upstream changed, unless it was confined to the\n * `branch.<name>` sections of branches the summary neither is on, tracks nor bases on (its `configBranches`), which\n * reads nothing; the checked-out branch moving, or a HEAD change, reads all of them. Stash, unrelated tags and\n * branches, and the like read nothing. One run is in flight per worktree at a time, at most four per repository (the\n * worktrees sharing a common git directory), and at most eight across the service. Runs take turns first come, first\n * served, except that one whose repository is at its cap lets later runs of other repositories go first, so a burst in\n * one repository never delays another's reads: events while a run waits for its turn join it, and events during a run\n * are merged into a single follow-up run.\n *\n * **Value-change-only notification.** {@link WorktreeSummarySubscription.onDidChange} and {@link onDidChangeSummary}\n * fire only when some field's value differs from the previous summary — a recompute that reads the same answer is\n * silent, so a consumer can re-render on every event without diffing. A failed recompute keeps the last values and\n * sets `stale` (firing once); the next successful one clears it.\n *\n * **Worktrees without a subscription.** {@link getSummary} and {@link getSummarySnapshot} answer one from its last read\n * (kept for the {@link maxUnwatchedReads} most recently asked), re-reading only the groups whose inputs moved since:\n * HEAD's branch or commit reads all of them; the upstream's tip, or its name, reads `branch`; a ref the base depends on\n * (where a remote's `HEAD` symref points, for one) reads `base`, re-measured from the previous one where that can\n * answer; the config inputs its base read consulted read `branch` and the base when they changed; and the worktree's\n * index file reads `dirty`. The inputs come from core's caches, so nothing moved means no git at all; only a `.git`\n * watch of the worktree (or core's own writes) keeps them current, so a host must watch the `.git` of a worktree it\n * asks about this way for others' changes to show. Its working tree is unwatched, so an edit that leaves the index\n * alone goes unseen until `dirty` is read again: the snapshot says when that was, and `probeDirty` reads it anyway.\n * Concurrent reads of a worktree share one, and take no read slot: each is a caller's foreground read, which never\n * waits behind subscribed worktrees' runs.\n */\nexport class WorktreeSummaryService implements UnifiedDisposable {\n\tprivate readonly _onDidChangeSummary = new Emitter<WorktreeSummary>();\n\t/** Fires when the summary of any subscribed worktree changes value */\n\tget onDidChangeSummary(): Event<WorktreeSummary> {\n\t\treturn this._onDidChangeSummary.event;\n\t}\n\n\tprivate readonly entries = new Map<string, SummaryEntry>();\n\t/** Reads running for each repository, by its slots key; a repository with none has no entry */\n\tprivate readonly activeReadsByRepository = new Map<string, number>();\n\tprivate activeReads = 0;\n\t/** Entries waiting for a read slot, oldest first */\n\tprivate readonly readWaiters = new Map<SummaryEntry, (acquired: boolean) => void>();\n\t/** The last read of each worktree without a subscription, by repository key */\n\tprivate readonly unwatchedReads = new LruMap<string, UnwatchedRead>(maxUnwatchedReads);\n\t/** The read in flight for each worktree without a subscription, by repository key */\n\tprivate readonly unwatchedFlights = new Map<string, UnwatchedFlight>();\n\tprivate readonly watchService: RepositoryWatchService;\n\tprivate readonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\tprivate readonly getCommonPath: ((repoPath: string) => string | undefined) | undefined;\n\tprivate readonly stat: WorktreeSummaryServiceOptions['stat'];\n\tprivate readonly verifyBaseRemeasure: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];\n\tprivate readonly mergeTargetsListener: UnifiedDisposable | undefined;\n\tprivate _disposed = false;\n\n\tconstructor(options: WorktreeSummaryServiceOptions) {\n\t\tthis.watchService = options.watchService;\n\t\tthis.getRepository = options.getRepository;\n\t\tthis.getCommonPath = options.getCommonPath;\n\t\tthis.stat = options.stat;\n\t\tthis.verifyBaseRemeasure = options.verifyBaseRemeasure;\n\t\tthis.mergeTargetsListener = options.onDidChangeMergeTargets?.(e => this.onMergeTargetsChanged(e));\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\t\tfor (const resolve of this.readWaiters.values()) {\n\t\t\tresolve(false);\n\t\t}\n\t\tthis.readWaiters.clear();\n\t\tfor (const flight of this.unwatchedFlights.values()) {\n\t\t\tflight.disposed = true;\n\t\t\tflight.abort.abort();\n\t\t}\n\t\tthis.unwatchedFlights.clear();\n\t\tthis.unwatchedReads.clear();\n\t\tthis.mergeTargetsListener?.dispose();\n\t\tfor (const entry of [...this.entries.values()]) {\n\t\t\tfor (const subscription of [...entry.subscribers]) {\n\t\t\t\tsubscription.dispose();\n\t\t\t}\n\t\t}\n\t\tthis.entries.clear();\n\t\tthis._onDidChangeSummary.dispose();\n\t}\n\n\t/** Subscribes to a worktree's summary, starting to watch and read it if this is its first subscriber */\n\tsubscribe(repoPath: string, options?: WorktreeSummarySubscribeOptions): WorktreeSummarySubscription {\n\t\tconst key = getRepositoryKey(repoPath);\n\n\t\tlet entry = this._disposed ? undefined : this.entries.get(key);\n\t\tconst isNew = entry == null;\n\t\tif (entry == null) {\n\t\t\tentry = createEntry(key);\n\t\t\tif (this._disposed) {\n\t\t\t\tentry.disposed = true;\n\t\t\t} else {\n\t\t\t\tthis.entries.set(key, entry);\n\t\t\t}\n\t\t}\n\n\t\tconst target = entry;\n\t\tconst subscription = new SummarySubscription(target, options, s => this.release(target, s));\n\t\ttarget.subscribers.add(subscription);\n\n\t\tif (isNew) {\n\t\t\tif (!target.disposed) {\n\t\t\t\t// The subscribed value supersedes the last unwatched read, and a read still in flight no longer stores one\n\t\t\t\tthis.unwatchedReads.delete(key);\n\t\t\t\tthis.unwatchedFlights.delete(key);\n\t\t\t\ttarget.pending = allFields | baseConfigField;\n\t\t\t\ttarget.running = this.start(target);\n\t\t\t}\n\t\t} else if (target.handle != null) {\n\t\t\tsubscription.attach(target.handle.session);\n\t\t} else if (target.running == null && !target.disposed) {\n\t\t\t// The start failed (no repository or git directory yet); a new subscriber retries it\n\t\t\ttarget.pending = allFields | baseConfigField;\n\t\t\ttarget.running = this.start(target);\n\t\t}\n\n\t\treturn subscription;\n\t}\n\n\t/**\n\t * Returns a worktree's summary. For a subscribed worktree, the cached value — after waiting for any run in flight\n\t * or pending; it rejects with the read's error when there is still no value. Otherwise the last read of it,\n\t * re-reading what moved since (see {@link getSummarySnapshot}).\n\t */\n\tasync getSummary(repoPath: string, cancellation?: AbortSignal): Promise<WorktreeSummary> {\n\t\tconst key = getRepositoryKey(repoPath);\n\t\tconst entry = this.entries.get(key);\n\t\tif (entry == null) return (await this.getUnwatchedSnapshot(key, false, cancellation)).summary;\n\n\t\treturn this.getSubscribedSummary(entry, cancellation);\n\t}\n\n\t/**\n\t * Returns a worktree's summary, with when its dirty flag was read. A subscribed worktree answers as\n\t * {@link getSummary} does, with `dirtyReadAt` `undefined`, since its summary follows its working tree. Any other\n\t * answers from its last read, re-reading only the groups whose inputs moved since (no git when none did), and in\n\t * full the first time, or after a failed or stale read. Its working tree is unwatched, so the dirty flag is re-read\n\t * only when the index file moved, or with `probeDirty`; a rejection drops the last read. Its inputs are core's\n\t * cached answers, which only a `.git` watch of the worktree (or core's own writes) keeps current.\n\t */\n\tasync getSummarySnapshot(\n\t\trepoPath: string,\n\t\toptions?: { probeDirty?: boolean; cancellation?: AbortSignal },\n\t): Promise<WorktreeSummarySnapshot> {\n\t\tconst key = getRepositoryKey(repoPath);\n\t\tconst entry = this.entries.get(key);\n\t\tif (entry == null) return this.getUnwatchedSnapshot(key, options?.probeDirty ?? false, options?.cancellation);\n\n\t\treturn { summary: await this.getSubscribedSummary(entry, options?.cancellation), dirtyReadAt: undefined };\n\t}\n\n\tprivate async getSubscribedSummary(\n\t\tentry: SummaryEntry,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<WorktreeSummary> {\n\t\tif (entry.running != null) {\n\t\t\tawait (cancellation != null ? raceWithSignal(entry.running, cancellation) : entry.running);\n\t\t}\n\t\tif (entry.disposed) throw new CancellationError();\n\t\tif (entry.value != null) return entry.value;\n\n\t\tthrow entry.error ?? new Error(`No summary available for '${entry.key}'`);\n\t}\n\n\t/** Joins the read in flight for `key`, or starts one; the read is aborted only once no caller waits for it */\n\tprivate async getUnwatchedSnapshot(\n\t\tkey: string,\n\t\tprobeDirty: boolean,\n\t\tcancellation: AbortSignal | undefined,\n\t): Promise<WorktreeSummarySnapshot> {\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\n\t\tlet flight = this.getJoinableFlight(key);\n\t\tif (probeDirty) {\n\t\t\t// A read that won't re-read the dirty flag can't answer a caller that asked for it; wait it out, then read\n\t\t\t// again\n\t\t\twhile (flight != null && !flight.probeDirty) {\n\t\t\t\tconst waited = flight;\n\t\t\t\ttry {\n\t\t\t\t\tawait (cancellation != null ? raceWithSignal(waited.promise, cancellation) : waited.promise);\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tif (cancellation?.aborted) throw ex;\n\t\t\t\t}\n\t\t\t\tflight = this.getJoinableFlight(key);\n\t\t\t\t// Settled, though its callers haven't all let go of it yet\n\t\t\t\tif (flight === waited) {\n\t\t\t\t\tflight = undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// A subscription that started meanwhile answers instead, its summary following the working tree\n\t\t\tconst entry = this.entries.get(key);\n\t\t\tif (entry != null) {\n\t\t\t\treturn { summary: await this.getSubscribedSummary(entry, cancellation), dirtyReadAt: undefined };\n\t\t\t}\n\t\t}\n\n\t\tif (flight == null) {\n\t\t\tflight = new UnwatchedFlight(key, probeDirty, this._disposed, f => this.readUnwatched(f));\n\t\t\tif (!this._disposed) {\n\t\t\t\tthis.unwatchedFlights.set(key, flight);\n\t\t\t}\n\t\t}\n\n\t\tconst current = flight;\n\t\tcurrent.callers++;\n\t\ttry {\n\t\t\tconst read = await (cancellation != null ? raceWithSignal(current.promise, cancellation) : current.promise);\n\t\t\treturn { summary: read.result.summary, dirtyReadAt: read.dirtyReadAt };\n\t\t} finally {\n\t\t\tcurrent.callers--;\n\t\t\t// Settled, or every caller gave up; either way a later caller starts a read of its own. Aborted even once\n\t\t\t// another read or a subscription took its place, which no caller waits on any longer\n\t\t\tif (current.callers === 0) {\n\t\t\t\tif (this.unwatchedFlights.get(key) === current) {\n\t\t\t\t\tthis.unwatchedFlights.delete(key);\n\t\t\t\t}\n\t\t\t\tcurrent.disposed = true;\n\t\t\t\tcurrent.abort.abort();\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * The read in flight for `key`, unless core wrote a merge target or base while it ran: it may have read the stored\n\t * values before the write, and a caller after an awaited write sees them, as one of a subscribed worktree does\n\t */\n\tprivate getJoinableFlight(key: string): UnwatchedFlight | undefined {\n\t\tconst flight = this.unwatchedFlights.get(key);\n\t\treturn flight?.due === 0 ? flight : undefined;\n\t}\n\n\tprivate async readUnwatched(flight: UnwatchedFlight): Promise<UnwatchedRead> {\n\t\tconst { key } = flight;\n\t\tconst signal = flight.abort.signal;\n\t\tif (flight.disposed) throw new CancellationError();\n\n\t\t// A stale read's fields can't be trusted (see `queue`), so it is read again in full\n\t\tlet last = this.unwatchedReads.get(key);\n\t\tif (last?.result.summary.stale) {\n\t\t\tlast = undefined;\n\t\t}\n\n\t\ttry {\n\t\t\tconst repo = this.getRepository(key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${key}'`);\n\n\t\t\t// Core drops every remote's cached `HEAD` symref on any ref change, so after the first read only those the\n\t\t\t// base depends on are read, unless the base is read again\n\t\t\tconst headRemotes = last != null ? getHeadRemotes(last.result.summary.baseDependsOn) : undefined;\n\t\t\tconst probe = await this.probeUnwatched(repo, headRemotes, signal);\n\t\t\tif (flight.disposed) throw new CancellationError();\n\n\t\t\tlet fields: number;\n\t\t\tlet baseMoves: Set<string> | undefined;\n\t\t\tif (last == null) {\n\t\t\t\t// The config inputs too, so a later base move can be re-measured against the key they had\n\t\t\t\tfields = allFields | baseConfigField;\n\t\t\t} else {\n\t\t\t\tfields = last.due;\n\t\t\t\tconst { inputs } = last;\n\t\t\t\tconst previous = last.result.summary;\n\t\t\t\tif (probe.branch !== inputs.branch || probe.detached !== inputs.detached || probe.sha !== inputs.sha) {\n\t\t\t\t\t// The branch read only widens to the rest for a different branch, but a commit moves the base count\n\t\t\t\t\t// and can leave the working tree clean\n\t\t\t\t\tfields |= allFields;\n\t\t\t\t} else {\n\t\t\t\t\tif (probe.upstream !== inputs.upstream) {\n\t\t\t\t\t\t// The config inputs too, which read the base again when the upstream's identity changed\n\t\t\t\t\t\tfields |= branchField | baseConfigField;\n\t\t\t\t\t} else if (getUpstreamTips(probe.tips, probe.upstream) !== inputs.upstreamTips) {\n\t\t\t\t\t\tfields |= branchField;\n\t\t\t\t\t}\n\t\t\t\t\tfor (const ref of previous.baseDependsOn) {\n\t\t\t\t\t\t// An unreadable value is unknown, so it moved\n\t\t\t\t\t\tconst value = getBaseInput(probe, ref);\n\t\t\t\t\t\tif (value !== null && value === inputs.baseTips.get(ref)) continue;\n\n\t\t\t\t\t\tbaseMoves ??= new Set();\n\t\t\t\t\t\tbaseMoves.add(ref);\n\t\t\t\t\t}\n\t\t\t\t\tif (baseMoves != null) {\n\t\t\t\t\t\tfields |= baseMoveField;\n\t\t\t\t\t}\n\t\t\t\t\t// A config write the base read consulted (another branch's tracking, a remote, the default branch)\n\t\t\t\t\t// moves no ref; one cached read, as a subscribed worktree's config change reads\n\t\t\t\t\tif (\n\t\t\t\t\t\t(fields & baseConfigField) === 0 &&\n\t\t\t\t\t\tlast.result.baseConfigKey != null &&\n\t\t\t\t\t\trepo.branches.getAheadOfBase != null &&\n\t\t\t\t\t\t(probe.detached || probe.branch != null)\n\t\t\t\t\t) {\n\t\t\t\t\t\tconst configKey = getBaseConfigKey(await readBaseConfig(repo, signal), previous);\n\t\t\t\t\t\tif (flight.disposed) throw new CancellationError();\n\n\t\t\t\t\t\t// An unreadable config is unknown, not changed\n\t\t\t\t\t\tif (configKey != null && configKey !== last.result.baseConfigKey) {\n\t\t\t\t\t\t\tfields |= branchField | baseConfigField;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (flight.probeDirty || probe.index == null || probe.index !== inputs.index) {\n\t\t\t\t\tfields |= dirtyField;\n\t\t\t\t}\n\t\t\t\tif (fields === 0) {\n\t\t\t\t\tthis.unwatchedReads.touch(key);\n\t\t\t\t\treturn last;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// A base read can come to depend on any remote's `HEAD`, so the rest are read too, before it like the other\n\t\t\t// inputs\n\t\t\tif (headRemotes != null && (fields & (baseField | baseKeysField | baseConfigField | baseMoveField)) !== 0) {\n\t\t\t\tawait readRemoteHeads(repo, probe.remoteHeads, undefined, signal);\n\t\t\t\tif (flight.disposed) throw new CancellationError();\n\t\t\t}\n\n\t\t\t// No read slot: like the one-off read it replaces, this is a caller's foreground read, which never waits\n\t\t\t// behind subscribed worktrees' runs\n\t\t\tlet result = await readSummary(\n\t\t\t\trepo,\n\t\t\t\tkey,\n\t\t\t\tfields,\n\t\t\t\tlast?.result.summary,\n\t\t\t\tlast?.result.baseKeys,\n\t\t\t\tlast?.result.baseConfigKey,\n\t\t\t\tlast?.result.baseDefaultBranch,\n\t\t\t\tbaseMoves,\n\t\t\t\tsignal,\n\t\t\t\tthis.verifyBaseRemeasure,\n\t\t\t);\n\n\t\t\t// Keep the summary the caller already has when nothing in it changed\n\t\t\tconst kept = last?.result.summary;\n\t\t\tif (kept != null && summariesEqual(kept, result.summary)) {\n\t\t\t\tresult = { ...result, summary: kept };\n\t\t\t}\n\t\t\tconst read: UnwatchedRead = {\n\t\t\t\tresult: result,\n\t\t\t\t// Without a last read every group was read, dirty included\n\t\t\t\tdirtyReadAt: result.dirtyReadAt ?? last?.dirtyReadAt ?? Date.now(),\n\t\t\t\tinputs: toUnwatchedInputs(probe, result.summary),\n\t\t\t\tdue: flight.due,\n\t\t\t};\n\t\t\tif (this.unwatchedFlights.get(key) === flight && !this.entries.has(key)) {\n\t\t\t\tthis.unwatchedReads.set(key, read);\n\t\t\t}\n\t\t\treturn read;\n\t\t} catch (ex) {\n\t\t\tif (!isCancellationError(ex) && this.unwatchedFlights.get(key) === flight) {\n\t\t\t\tthis.unwatchedReads.delete(key);\n\t\t\t}\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/**\n\t * Reads an unwatched read's inputs: cached answers but for the index file's stat (see {@link UnwatchedInputs}), with\n\t * the `HEAD` symrefs of `remotes` (every remote's when `undefined`)\n\t */\n\tprivate async probeUnwatched(\n\t\trepo: RepositoryService,\n\t\tremotes: ReadonlySet<string> | undefined,\n\t\tsignal: AbortSignal,\n\t): Promise<UnwatchedProbe> {\n\t\tconst remoteHeads = new Map<string, string | null | undefined>();\n\t\tconst [current, tips, gitDir] = await Promise.all([\n\t\t\trepo.branches.getBranch(undefined, undefined, signal),\n\t\t\trepo.refs.getRefTips(undefined, signal),\n\t\t\tgetGitDir(repo),\n\t\t\treadRemoteHeads(repo, remoteHeads, remotes, signal),\n\t\t]);\n\n\t\tlet index: string | undefined;\n\t\tconst stat = this.stat;\n\t\tif (stat != null && gitDir != null) {\n\t\t\ttry {\n\t\t\t\tconst stats = await stat(joinPaths(gitDir.uri.fsPath, 'index'));\n\t\t\t\tindex = stats != null ? `${stats.size}:${stats.mtime}` : '';\n\t\t\t} catch {\n\t\t\t\tindex = undefined;\n\t\t\t}\n\t\t}\n\n\t\tconst detached = current?.detached ?? false;\n\t\treturn {\n\t\t\tbranch: detached ? undefined : current?.name || undefined,\n\t\t\tdetached: detached,\n\t\t\tsha: current?.sha,\n\t\t\tupstream: current?.upstream?.name,\n\t\t\ttips: getTipsByName(tips),\n\t\t\tremoteHeads: remoteHeads,\n\t\t\tindex: index,\n\t\t};\n\t}\n\n\tgetResourceUsage(): ResourceUsage {\n\t\treturn { 'worktreeSummary.entries.count': this.entries.size };\n\t}\n\n\tprivate async start(entry: SummaryEntry): Promise<void> {\n\t\tlet gitDir: GitDir | undefined;\n\t\ttry {\n\t\t\tgitDir = await this.getRepository(entry.key)?.config.getGitDir?.();\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed) return;\n\n\t\t\tentry.running = undefined;\n\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\tthis.fail(entry, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tconst handle = gitDir != null ? this.watchService.watch(entry.key, gitDir) : undefined;\n\t\tif (gitDir == null || handle == null) {\n\t\t\tentry.running = undefined;\n\t\t\tthis.fail(entry, new Error(`Unable to watch '${entry.key}': no repository or git directory`));\n\t\t\treturn;\n\t\t}\n\n\t\tentry.handle = handle;\n\t\tentry.slotsKey = getRepositoryKey(gitDir.commonUri ?? gitDir.uri);\n\t\tconst session = handle.session;\n\t\t// Timing-neutral: the cadence comes from the subscribers' own session subscriptions (see `attach`)\n\t\tconst repoSub = session.subscribe({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tconst workingTreeSub = session.subscribeToWorkingTree({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tentry.listeners = [\n\t\t\trepoSub.onDidChange(e => this.onRepoChanged(entry, e)),\n\t\t\tworkingTreeSub.onDidChangeWorkingTree(() => this.queue(entry, dirtyField)),\n\t\t\trepoSub,\n\t\t\tworkingTreeSub,\n\t\t];\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.attach(session);\n\t\t}\n\n\t\tawait this.drain(entry);\n\t}\n\n\tprivate onRepoChanged(entry: SummaryEntry, e: WatcherRepoChangeEvent): void {\n\t\tconst value = entry.value;\n\t\tif (value == null || e.changed('unknown', 'head')) {\n\t\t\tthis.queue(entry, allFields | (e.changed('config') ? baseConfigField : 0));\n\t\t\treturn;\n\t\t}\n\n\t\tlet fields = 0;\n\t\tif (e.changed('index', 'ignores')) {\n\t\t\tfields |= dirtyField;\n\t\t}\n\t\tif (e.changed('config') && e.mayHaveChangedConfig(getConfigRelevantRefs(value))) {\n\t\t\tfields |= branchField | baseKeysField | baseConfigField;\n\t\t}\n\t\tif (e.changed('gkConfig')) {\n\t\t\tfields |= baseKeysField;\n\t\t}\n\t\tif (value.branch != null && e.mayHaveChangedRef(`refs/heads/${value.branch}`)) {\n\t\t\tfields |= allFields;\n\t\t}\n\t\t// The upstream's short name doesn't say whether it's remote or local (`--track main`), so test both forms\n\t\tif (\n\t\t\tvalue.upstream != null &&\n\t\t\t(e.mayHaveChangedRef(`refs/remotes/${value.upstream.name}`) ||\n\t\t\t\te.mayHaveChangedRef(`refs/heads/${value.upstream.name}`))\n\t\t) {\n\t\t\tfields |= branchField;\n\t\t}\n\t\tlet baseMoves: string[] | undefined;\n\t\tif ((fields & baseField) === 0) {\n\t\t\tfor (const ref of value.baseDependsOn) {\n\t\t\t\tif (!e.mayHaveChangedRef(ref)) continue;\n\n\t\t\t\t// Only a ref named exactly is known to be the one that moved: a ref directory or a ref kind that changed\n\t\t\t\t// without names (a packed-refs rewrite, an overflow, a poll) may hide any move\n\t\t\t\tconst kind = getRefChangeKind(ref);\n\t\t\t\tif (!e.refs.has(ref) || kind == null || e.unnamedRefKinds.has(kind)) {\n\t\t\t\t\tfields |= baseField;\n\t\t\t\t\tbaseMoves = undefined;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbaseMoves ??= [];\n\t\t\t\tbaseMoves.push(ref);\n\t\t\t}\n\t\t\tif (baseMoves != null) {\n\t\t\t\tfields |= baseMoveField;\n\t\t\t}\n\t\t}\n\n\t\tthis.queue(entry, fields, baseMoves);\n\t}\n\n\t/**\n\t * Queued synchronously, so a store awaited before `getSummary` finds the run in flight. The watcher's later\n\t * `gkConfig` event for the same write then finds the keys unchanged and leaves the base alone.\n\t */\n\tprivate onMergeTargetsChanged(e: MergeTargetsChangeEvent): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\t// Before the watch starts there's nothing to refresh: the start's own read comes after the write\n\t\t\tif (entry.handle == null) continue;\n\n\t\t\t// With no value yet, the first read may have read the stored values before the write, so `queue` reads\n\t\t\t// everything again\n\t\t\tconst value = entry.value;\n\t\t\tif (value != null && (value.branch == null || (e.refs != null && !e.refs.has(value.branch)))) continue;\n\n\t\t\tif (!this.isInRepository(entry.key, e.repoPath)) continue;\n\n\t\t\tthis.queue(entry, baseKeysField);\n\t\t}\n\n\t\t// No ref moves for such a write, so a worktree without a subscription re-reads the stored values on its next read\n\t\tfor (const read of this.unwatchedReads.values()) {\n\t\t\tconst value = read.result.summary;\n\t\t\tif (value.branch == null || (e.refs != null && !e.refs.has(value.branch))) continue;\n\t\t\tif (!this.isInRepository(value.repoPath, e.repoPath)) continue;\n\n\t\t\tread.due |= baseKeysField;\n\t\t}\n\t\t// One in flight may have read them before the write, so it takes no more callers; its branch isn't known yet,\n\t\t// so any of the repository's\n\t\tfor (const flight of this.unwatchedFlights.values()) {\n\t\t\tif (!this.isInRepository(flight.key, e.repoPath)) continue;\n\n\t\t\tflight.due |= baseKeysField;\n\t\t}\n\t}\n\n\tprivate isInRepository(key: string, commonPath: string): boolean {\n\t\tconst path = this.getCommonPath?.(key);\n\t\treturn path == null || path === commonPath;\n\t}\n\n\tprivate queue(entry: SummaryEntry, fields: number, baseMoves?: readonly string[]): void {\n\t\tif (entry.disposed || fields === 0) return;\n\n\t\t// Without a value a partial read has nothing to merge into, and a stale value's fields can't be trusted: a\n\t\t// partial read would clear `stale` while vouching for groups it never re-read\n\t\tentry.pending |= entry.value == null || entry.value.stale ? fields | allFields | baseConfigField : fields;\n\t\tif (baseMoves != null) {\n\t\t\tentry.baseMoves ??= new Set();\n\t\t\tfor (const ref of baseMoves) {\n\t\t\t\tentry.baseMoves.add(ref);\n\t\t\t}\n\t\t}\n\t\tentry.running ??= this.drain(entry);\n\t}\n\n\t/** Runs until nothing is pending. Never rejects — every failure becomes entry state. */\n\tprivate async drain(entry: SummaryEntry): Promise<void> {\n\t\twhile (entry.pending !== 0 && !entry.disposed) {\n\t\t\tif (!(await this.acquireRead(entry))) break;\n\n\t\t\ttry {\n\t\t\t\tif (entry.disposed) break;\n\n\t\t\t\t// Consume after acquiring: changes that arrived while queued belong to this same read.\n\t\t\t\tconst fields = entry.pending;\n\t\t\t\tconst baseMoves = entry.baseMoves;\n\t\t\t\tentry.pending = 0;\n\t\t\t\tentry.baseMoves = undefined;\n\t\t\t\tawait this.run(entry, fields, baseMoves);\n\t\t\t} finally {\n\t\t\t\tthis.releaseRead(entry);\n\t\t\t}\n\t\t}\n\t\t// Same turn as the loop's last check, so an event can't queue fields that no run will pick up\n\t\tentry.running = undefined;\n\t}\n\n\t/**\n\t * A read takes one of its repository's slots and one of the service's. Each release hands what it frees to the\n\t * waiters first, so a new read takes a slot only when no waiter can use it.\n\t */\n\tprivate acquireRead(entry: SummaryEntry): Promise<boolean> {\n\t\tif (entry.disposed || this._disposed) return Promise.resolve(false);\n\n\t\tif (this.tryTakeReadSlots(entry)) return Promise.resolve(true);\n\n\t\treturn new Promise(resolve => this.readWaiters.set(entry, resolve));\n\t}\n\n\tprivate releaseRead(entry: SummaryEntry): void {\n\t\tconst key = entry.slotsKey ?? entry.key;\n\t\tconst active = this.activeReadsByRepository.get(key);\n\t\tif (active == null) return;\n\n\t\tif (active > 1) {\n\t\t\tthis.activeReadsByRepository.set(key, active - 1);\n\t\t} else {\n\t\t\tthis.activeReadsByRepository.delete(key);\n\t\t}\n\t\tthis.activeReads--;\n\n\t\t// The oldest waiter whose repository has a free slot goes next, so a repository at its own cap never holds up\n\t\t// another's waiters, and a busy checkout rejoins the tail for a follow-up rather than starving its siblings\n\t\tfor (const [waiter, resolve] of this.readWaiters) {\n\t\t\tif (this.activeReads >= maxConcurrentReads) break;\n\n\t\t\tif (waiter.disposed || this._disposed) {\n\t\t\t\tthis.readWaiters.delete(waiter);\n\t\t\t\tresolve(false);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!this.tryTakeReadSlots(waiter)) continue;\n\n\t\t\tthis.readWaiters.delete(waiter);\n\t\t\tresolve(true);\n\t\t}\n\t}\n\n\tprivate tryTakeReadSlots(entry: SummaryEntry): boolean {\n\t\tif (this.activeReads >= maxConcurrentReads) return false;\n\n\t\tconst key = entry.slotsKey ?? entry.key;\n\t\tconst active = this.activeReadsByRepository.get(key) ?? 0;\n\t\tif (active >= maxConcurrentReadsPerRepository) return false;\n\n\t\tthis.activeReadsByRepository.set(key, active + 1);\n\t\tthis.activeReads++;\n\t\treturn true;\n\t}\n\n\tprivate async run(entry: SummaryEntry, fields: number, baseMoves: ReadonlySet<string> | undefined): Promise<void> {\n\t\tconst previous = entry.value;\n\n\t\tlet result: ReadResult;\n\t\ttry {\n\t\t\tconst repo = this.getRepository(entry.key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${entry.key}'`);\n\n\t\t\tresult = await readSummary(\n\t\t\t\trepo,\n\t\t\t\tentry.key,\n\t\t\t\tfields,\n\t\t\t\tprevious,\n\t\t\t\tentry.baseKeys,\n\t\t\t\tentry.baseConfigKey,\n\t\t\t\tentry.baseDefaultBranch,\n\t\t\t\tbaseMoves,\n\t\t\t\tentry.abort.signal,\n\t\t\t\tthis.verifyBaseRemeasure,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed || isCancellationError(ex)) return;\n\n\t\t\tthis.fail(entry, ex);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tif (result.summary.stale && (previous == null || !previous.stale)) {\n\t\t\tLogger.warn(\n\t\t\t\t`WorktreeSummaryService: reading the base of '${entry.key}' failed: ${String(result.baseError)}`,\n\t\t\t);\n\t\t}\n\n\t\tentry.error = undefined;\n\t\tentry.baseKeys = result.baseKeys;\n\t\tentry.baseConfigKey = entry.pending & baseConfigField ? undefined : result.baseConfigKey;\n\t\tentry.baseDefaultBranch = result.baseDefaultBranch;\n\t\tif (previous != null && summariesEqual(previous, result.summary)) return;\n\n\t\tentry.value = result.summary;\n\t\tthis.fireChange(entry, result.summary);\n\t}\n\n\tprivate fail(entry: SummaryEntry, ex: unknown): void {\n\t\tconst previous = entry.value;\n\t\tif (previous == null) {\n\t\t\tif (entry.error == null) {\n\t\t\t\tLogger.warn(`WorktreeSummaryService: reading '${entry.key}' failed: ${String(ex)}`);\n\t\t\t}\n\t\t\tentry.error = ex instanceof Error ? ex : new Error(String(ex));\n\t\t\treturn;\n\t\t}\n\t\tif (previous.stale) return;\n\n\t\tLogger.warn(`WorktreeSummaryService: refreshing '${entry.key}' failed: ${String(ex)}`);\n\t\tconst summary: WorktreeSummary = { ...previous, stale: true };\n\t\tentry.value = summary;\n\t\tthis.fireChange(entry, summary);\n\t}\n\n\tprivate fireChange(entry: SummaryEntry, summary: WorktreeSummary): void {\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.fire(summary);\n\t\t}\n\t\tthis._onDidChangeSummary.fire(summary);\n\t}\n\n\tprivate release(entry: SummaryEntry, subscription: SummarySubscription): void {\n\t\tentry.subscribers.delete(subscription);\n\t\tif (entry.subscribers.size !== 0) return;\n\n\t\tentry.disposed = true;\n\t\t// A waiter holds no slot, so dropping it frees none\n\t\tconst cancelRead = this.readWaiters.get(entry);\n\t\tif (cancelRead != null) {\n\t\t\tthis.readWaiters.delete(entry);\n\t\t\tcancelRead(false);\n\t\t}\n\t\tentry.abort.abort();\n\t\tif (entry.listeners != null) {\n\t\t\tfor (const d of entry.listeners) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tentry.listeners = undefined;\n\t\t}\n\t\tentry.handle?.dispose();\n\t\tentry.handle = undefined;\n\t\tentry.value = undefined;\n\t\tentry.baseKeys = undefined;\n\t\tentry.baseConfigKey = undefined;\n\t\tentry.baseDefaultBranch = undefined;\n\t\tentry.pending = 0;\n\t\tentry.baseMoves = undefined;\n\t\tentry.running = undefined;\n\n\t\tif (this.entries.get(entry.key) === entry) {\n\t\t\tthis.entries.delete(entry.key);\n\t\t}\n\t}\n}\n\n/**\n * The local branches whose config sections a summary reads: its branch's (upstream and base keys), and those of the\n * branches it tracks or bases on. The upstream's short name doesn't say whether it's local, so it counts as local too.\n */\nfunction* getConfigRelevantRefs(value: WorktreeSummary): Iterable<string> {\n\tif (value.branch != null) {\n\t\tyield `refs/heads/${value.branch}`;\n\t}\n\tif (value.upstream != null) {\n\t\tyield `refs/heads/${value.upstream.name}`;\n\t}\n\tyield* value.baseDependsOn;\n}\n\nfunction createEntry(key: string): SummaryEntry {\n\treturn {\n\t\tkey: key,\n\t\tslotsKey: undefined,\n\t\tabort: new AbortController(),\n\t\tsubscribers: new Set(),\n\t\thandle: undefined,\n\t\tlisteners: undefined,\n\t\tvalue: undefined,\n\t\tbaseKeys: undefined,\n\t\tbaseConfigKey: undefined,\n\t\tbaseDefaultBranch: undefined,\n\t\terror: undefined,\n\t\tpending: 0,\n\t\tbaseMoves: undefined,\n\t\trunning: undefined,\n\t\tdisposed: false,\n\t};\n}\n\n/**\n * Reads the due groups and fills the rest from `previous` (every group when there is none). The branch read comes\n * before the base read, which needs its name; the dirty read runs alongside both. A branch read that finds a\n * different branch (or detached state) also reads the base and dirty groups, and stored base values that differ\n * from `previousBaseKeys` read the base group. A base due only because `baseMoves` moved, with HEAD, the stored\n * values and the config inputs as before, is first re-measured from `previous` and the default branch its read\n * resolved, `previousBaseDefaultBranch` (see `getAheadOfBaseAfterBaseMove`), and read in full only when that can't\n * answer. A failed base read doesn't fail the rest: the summary comes back stale, keeping the previous\n * base and `baseDependsOn` only while the branch is the same.\n */\nasync function readSummary(\n\trepo: RepositoryService,\n\tkey: string,\n\tfields: number,\n\tprevious: WorktreeSummary | undefined,\n\tpreviousBaseKeys: BaseKeys | undefined,\n\tpreviousBaseConfigKey: string | undefined,\n\tpreviousBaseDefaultBranch: string | null | undefined,\n\tbaseMoves: ReadonlySet<string> | undefined,\n\tsignal: AbortSignal | undefined,\n\tverifyBaseRemeasure: WorktreeSummaryServiceOptions['verifyBaseRemeasure'],\n): Promise<ReadResult> {\n\tif (previous == null) {\n\t\tfields |= allFields;\n\t}\n\n\tlet dirtyReadAt: number | undefined;\n\tconst startDirtyRead = (): Promise<boolean> => {\n\t\tdirtyReadAt = Date.now();\n\t\treturn readDirty(repo, signal);\n\t};\n\tlet dirtyRead = fields & dirtyField ? startDirtyRead() : undefined;\n\n\tlet branch = previous?.branch;\n\tlet detached = previous?.detached ?? false;\n\tlet sha = previous?.sha;\n\tlet upstream = previous?.upstream;\n\tlet ahead = previous?.ahead ?? 0;\n\tlet behind = previous?.behind ?? 0;\n\tif (fields & branchField) {\n\t\tconst current = await repo.branches.getBranch(undefined, undefined, signal);\n\t\tdetached = current?.detached ?? false;\n\t\tbranch = detached ? undefined : current?.name || undefined;\n\t\tsha = current?.sha;\n\t\tupstream = current?.upstream != null ? await toSummaryUpstream(repo, current.upstream, signal) : undefined;\n\t\tahead = current?.upstream?.state.ahead ?? 0;\n\t\tbehind = current?.upstream?.state.behind ?? 0;\n\n\t\tif (previous != null && (branch !== previous.branch || detached !== previous.detached)) {\n\t\t\tfields |= baseField;\n\t\t\tdirtyRead ??= startDirtyRead();\n\t\t}\n\t}\n\n\tlet base = previous?.base;\n\tlet dependsOn = previous?.baseDependsOn ?? emptyRefs;\n\tlet baseKeys = previousBaseKeys;\n\tlet baseKeysRead = false;\n\tlet baseConfigKey = previousBaseConfigKey;\n\tlet baseConfig: BaseConfig | undefined;\n\tlet baseConfigRead = false;\n\tlet baseDefaultBranch = previousBaseDefaultBranch;\n\tlet baseFailed = false;\n\tlet baseError: unknown;\n\t// A detached HEAD is measured as `undefined`; an unborn HEAD with no name has nothing to measure\n\tconst baseReadable = repo.branches.getAheadOfBase != null && (detached || branch != null);\n\tif (fields & baseConfigField && !(fields & baseField) && baseReadable) {\n\t\tbaseConfig = await readBaseConfig(repo, signal);\n\t\tbaseConfigRead = true;\n\t\t// Without a base read due, the branch and upstream are as before, so the inputs to compare are the ones the last\n\t\t// base read consulted\n\t\tconst key = previous != null ? getBaseConfigKey(baseConfig, previous) : undefined;\n\t\tif (\n\t\t\tkey == null ||\n\t\t\tkey !== previousBaseConfigKey ||\n\t\t\tsha !== previous?.sha ||\n\t\t\t!upstreamsEqual(upstream, previous?.upstream)\n\t\t) {\n\t\t\tfields |= baseField;\n\t\t}\n\t}\n\tif (fields & baseKeysField && !(fields & baseField) && baseReadable) {\n\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\tbaseKeysRead = true;\n\t\tif (baseKeys == null || previousBaseKeys == null || !baseKeysEqual(baseKeys, previousBaseKeys)) {\n\t\t\tfields |= baseField;\n\t\t}\n\t}\n\tif (fields & (baseField | baseMoveField)) {\n\t\tif (!baseReadable) {\n\t\t\tbase = undefined;\n\t\t\tdependsOn = emptyRefs;\n\t\t\tbaseKeys = undefined;\n\t\t\tbaseConfigKey = undefined;\n\t\t\tbaseDefaultBranch = undefined;\n\t\t} else {\n\t\t\t// Snapshot before the base read, so a write it misses is one the next `gk` config event sees as a change\n\t\t\tif (!baseKeysRead) {\n\t\t\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\t\t}\n\t\t\tif (!baseConfigRead && (previous != null || (fields & baseConfigField) !== 0)) {\n\t\t\t\tbaseConfig = await readBaseConfig(repo, signal);\n\t\t\t\tbaseConfigRead = true;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tlet aheadOfBase: AheadOfBase | undefined;\n\t\t\t\t// The config inputs too, though no config change is due: a write can land between the last read and its\n\t\t\t\t// event (core evicts its own write's caches before the event fires), and the re-measure reads no config,\n\t\t\t\t// so the key kept with it would vouch for config the base never saw\n\t\t\t\tif (\n\t\t\t\t\t!(fields & baseField) &&\n\t\t\t\t\tbaseMoves != null &&\n\t\t\t\t\tbaseMoves.size !== 0 &&\n\t\t\t\t\tprevious?.base != null &&\n\t\t\t\t\tsha != null &&\n\t\t\t\t\tsha === previous.sha &&\n\t\t\t\t\tbaseKeys != null &&\n\t\t\t\t\tpreviousBaseKeys != null &&\n\t\t\t\t\tbaseKeysEqual(baseKeys, previousBaseKeys) &&\n\t\t\t\t\tgetBaseConfigKey(baseConfig, previous) === previousBaseConfigKey\n\t\t\t\t) {\n\t\t\t\t\taheadOfBase = await repo.branches.getAheadOfBaseAfterBaseMove?.(\n\t\t\t\t\t\tbranch,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\thead: sha,\n\t\t\t\t\t\t\tbase: previous.base,\n\t\t\t\t\t\t\tdependsOn: previous.baseDependsOn,\n\t\t\t\t\t\t\t...(previousBaseDefaultBranch !== undefined\n\t\t\t\t\t\t\t\t? { defaultBranch: previousBaseDefaultBranch }\n\t\t\t\t\t\t\t\t: undefined),\n\t\t\t\t\t\t},\n\t\t\t\t\t\t[...baseMoves],\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t);\n\t\t\t\t\tif (aheadOfBase != null && verifyBaseRemeasure != null) {\n\t\t\t\t\t\tlet full: AheadOfBase | undefined;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tfull = await repo.branches.getAheadOfBase?.(branch, undefined, signal);\n\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\tif (isCancellationError(ex)) throw ex;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tverifyBaseRemeasure(aheadOfBase, full);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\taheadOfBase ??= await repo.branches.getAheadOfBase?.(branch, undefined, signal);\n\t\t\t\tbase = aheadOfBase?.base;\n\t\t\t\tdependsOn = aheadOfBase?.dependsOn ?? emptyRefs;\n\t\t\t\tbaseDefaultBranch = aheadOfBase?.defaultBranch;\n\t\t\t} catch (ex) {\n\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\tbaseFailed = true;\n\t\t\t\tbaseError = ex;\n\t\t\t\t// Keep `baseDependsOn` for the same branch: a fetch moving one of its refs is what retries the base\n\t\t\t\tconst sameBranch = previous != null && branch === previous.branch && detached === previous.detached;\n\t\t\t\tbase = sameBranch ? previous.base : undefined;\n\t\t\t\tdependsOn = sameBranch ? previous.baseDependsOn : emptyRefs;\n\t\t\t\tbaseKeys = undefined;\n\t\t\t\tbaseConfigRead = false;\n\t\t\t\tbaseConfigKey = undefined;\n\t\t\t\tbaseDefaultBranch = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst dirty = dirtyRead != null ? await dirtyRead : (previous?.dirty ?? false);\n\n\tconst summary: WorktreeSummary = {\n\t\trepoPath: key,\n\t\tbranch: branch,\n\t\tdetached: detached,\n\t\tsha: sha,\n\t\tupstream: upstream,\n\t\tahead: ahead,\n\t\tbehind: behind,\n\t\tdirty: dirty,\n\t\tbase: base,\n\t\tbaseDependsOn: dependsOn,\n\t\tstale: baseFailed,\n\t};\n\t// Keyed on what this run's base read (or the one it kept) consulted, from the inputs read before it\n\tif (baseConfigRead) {\n\t\tbaseConfigKey = getBaseConfigKey(baseConfig, summary);\n\t}\n\n\treturn {\n\t\tsummary: summary,\n\t\tbaseKeys: baseKeys,\n\t\tbaseConfigKey: baseConfigKey,\n\t\tbaseDefaultBranch: baseDefaultBranch,\n\t\tbaseError: baseError,\n\t\tdirtyReadAt: dirtyReadAt,\n\t};\n}\n\nasync function getGitDir(repo: RepositoryService): Promise<GitDir | undefined> {\n\ttry {\n\t\treturn await repo.config.getGitDir?.();\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\n/** Each ref list's tips by full name, built once per list, which core's cache shares across a repository's worktrees */\nconst tipsByNameCache = new WeakMap<readonly GitRefTip[], ReadonlyMap<string, string>>();\n\nfunction getTipsByName(tips: readonly GitRefTip[]): ReadonlyMap<string, string> {\n\tlet byName = tipsByNameCache.get(tips);\n\tif (byName == null) {\n\t\tconst map = new Map<string, string>();\n\t\tfor (const tip of tips) {\n\t\t\tmap.set(tip.fullName, tip.sha);\n\t\t}\n\t\tbyName = map;\n\t\ttipsByNameCache.set(tips, byName);\n\t}\n\treturn byName;\n}\n\n/** The upstream's tips as a remote-tracking and as a local branch, since its short name doesn't say which */\nfunction getUpstreamTips(tips: ReadonlyMap<string, string>, upstream: string | undefined): string | undefined {\n\tif (upstream == null) return undefined;\n\n\treturn `${tips.get(`refs/remotes/${upstream}`) ?? ''}\\0${tips.get(`refs/heads/${upstream}`) ?? ''}`;\n}\n\n/**\n * Reads where the `HEAD` symref of each of `remotes` (every remote's when `undefined`) points (`origin/main`) into\n * `heads`, as core's cached local reads answer it, skipping those already there: `undefined` when it has none (a\n * remote since removed included), `null` when the read failed\n */\nasync function readRemoteHeads(\n\trepo: RepositoryService,\n\theads: Map<string, string | null | undefined>,\n\tremotes: ReadonlySet<string> | undefined,\n\tsignal: AbortSignal,\n): Promise<void> {\n\tif (remotes?.size === 0) return;\n\n\tconst names = remotes ?? (await repo.remotes.getRemotes(undefined, signal)).map(r => r.name);\n\tawait Promise.all(\n\t\tArray.from(names, async name => {\n\t\t\tif (heads.has(name)) return;\n\n\t\t\tlet target: string | null | undefined;\n\t\t\ttry {\n\t\t\t\ttarget = await repo.branches.getDefaultBranchName(name, { local: true }, signal);\n\t\t\t} catch (ex) {\n\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\ttarget = null;\n\t\t\t}\n\t\t\theads.set(name, target);\n\t\t}),\n\t);\n}\n\n/** The remote whose `HEAD` symref `ref` is; `undefined` for any other ref */\nfunction getHeadRemote(ref: string): string | undefined {\n\tif (!ref.startsWith('refs/remotes/') || !ref.endsWith('/HEAD')) return undefined;\n\n\t// A remote name can hold a slash, so the remote is everything between\n\treturn ref.slice('refs/remotes/'.length, -'/HEAD'.length);\n}\n\n/** The remotes whose `HEAD` symref is among `refs` */\nfunction getHeadRemotes(refs: readonly string[]): Set<string> {\n\tconst remotes = new Set<string>();\n\tfor (const ref of refs) {\n\t\tconst remote = getHeadRemote(ref);\n\t\tif (remote != null) {\n\t\t\tremotes.add(remote);\n\t\t}\n\t}\n\treturn remotes;\n}\n\n/**\n * What `probe` found of `ref`, a ref the base depends on: for a remote's `HEAD` symref, which the ref tips leave out,\n * the ref it points at, since its tip follows that ref's, itself a dependency; else its tip. `null` for a symref of a\n * remote it didn't read: only when the read found HEAD elsewhere than the probe did and read the base for that, which\n * the next probe sees as HEAD moving anyway\n */\nfunction getBaseInput(probe: UnwatchedProbe, ref: string): string | null | undefined {\n\tconst remote = getHeadRemote(ref);\n\tif (remote == null) return probe.tips.get(ref);\n\n\treturn probe.remoteHeads.has(remote) ? probe.remoteHeads.get(remote) : null;\n}\n\n/** The inputs `summary` was read from, as the probe before its read found them */\nfunction toUnwatchedInputs(probe: UnwatchedProbe, summary: WorktreeSummary): UnwatchedInputs {\n\tconst baseTips = new Map<string, string | null | undefined>();\n\tfor (const ref of summary.baseDependsOn) {\n\t\tbaseTips.set(ref, getBaseInput(probe, ref));\n\t}\n\treturn {\n\t\tbranch: probe.branch,\n\t\tdetached: probe.detached,\n\t\tsha: probe.sha,\n\t\tupstream: probe.upstream,\n\t\tupstreamTips: getUpstreamTips(probe.tips, probe.upstream),\n\t\tbaseTips: baseTips,\n\t\tindex: probe.index,\n\t};\n}\n\nasync function readBaseConfig(\n\trepo: RepositoryService,\n\tsignal: AbortSignal | undefined,\n): Promise<BaseConfig | undefined> {\n\ttry {\n\t\tif (repo.branches.getAheadOfBaseConfigEntries != null) {\n\t\t\treturn await repo.branches.getAheadOfBaseConfigEntries(signal);\n\t\t}\n\t\treturn await repo.branches.getAheadOfBaseConfigKey?.(signal);\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\n/**\n * The key of the config inputs a base read for `value` consulted: every entry except the `branch.<name>` ones of local\n * branches outside its {@link getConfigRelevantRefs}, which the read never resolved. So a write to another branch's\n * section, confined (which wakes no summary but changes the inputs) or not, leaves the base alone. An opaque key is\n * taken whole.\n */\nfunction getBaseConfigKey(config: BaseConfig | undefined, value: WorktreeSummary): string | undefined {\n\tif (config == null || typeof config === 'string') return config;\n\n\tconst branches = new Set<string>();\n\tfor (const ref of getConfigRelevantRefs(value)) {\n\t\tif (ref.startsWith('refs/heads/')) {\n\t\t\tbranches.add(ref.substring('refs/heads/'.length));\n\t\t}\n\t}\n\n\tlet key = '';\n\tfor (const [name, entry] of config) {\n\t\t// The subsection is everything between the first and last dot, so a branch name can hold dots\n\t\tconst last = name.lastIndexOf('.');\n\t\tif (\n\t\t\tname.startsWith('branch.') &&\n\t\t\tlast > 'branch.'.length &&\n\t\t\t!branches.has(name.substring('branch.'.length, last))\n\t\t) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tkey += entry != null ? `${name}\\n${entry}\\0` : `${name}\\0`;\n\t}\n\treturn key;\n}\n\n/**\n * Reads {@link BaseKeys} for `branch`, normalized the way `getAheadOfBase` reads them. `undefined` when any read\n * failed — an unknown snapshot, so the next `gk` config change reads the base rather than trusting it. A detached\n * HEAD (`branch` undefined) has no stored values to read.\n */\nasync function readBaseKeys(repo: RepositoryService, branch: string | undefined): Promise<BaseKeys | undefined> {\n\tif (branch == null) return noBaseKeys;\n\n\ttry {\n\t\tconst [user, detected, recorded] = await Promise.all([\n\t\t\trepo.branches.getStoredUserMergeTargetBranchName?.(branch),\n\t\t\trepo.branches.getStoredDetectedMergeTargetBranchName?.(branch),\n\t\t\trepo.config.getGkConfig?.(`branch.${branch}.gk-merge-base`),\n\t\t]);\n\t\treturn [user?.trim() || undefined, detected?.trim() || undefined, recorded?.trim() || undefined];\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\nfunction baseKeysEqual(a: BaseKeys, b: BaseKeys): boolean {\n\treturn a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n}\n\nfunction readDirty(repo: RepositoryService, signal: AbortSignal | undefined): Promise<boolean> {\n\tconst read = repo.status.hasWorkingChanges({ untracked: true }, signal);\n\t// Awaited only after the branch and base reads; if one of those throws first, this rejection has no other handler\n\tread.catch(noop);\n\treturn read;\n}\n\nfunction noop(): void {}\n\n/**\n * Uses the remote and branch git reported for the upstream; only when it reported neither does this split the name\n * at the longest remote name it starts with (no split when none matches or nothing follows it)\n */\nasync function toSummaryUpstream(\n\trepo: RepositoryService,\n\tupstream: GitTrackingUpstream,\n\tsignal: AbortSignal | undefined,\n): Promise<WorktreeSummaryUpstream> {\n\tconst { name, missing } = upstream;\n\n\tif (upstream.local) return { name: name, remote: undefined, branch: undefined, missing: missing };\n\n\tif (upstream.remote) {\n\t\tconst { remote } = upstream;\n\t\tconst branch =\n\t\t\tupstream.remoteBranch || (name.startsWith(`${remote}/`) ? name.substring(remote.length + 1) : undefined);\n\t\treturn { name: name, remote: branch ? remote : undefined, branch: branch || undefined, missing: missing };\n\t}\n\n\tlet remote: string | undefined;\n\tfor (const r of await repo.remotes.getRemotes(undefined, signal)) {\n\t\tif (r.name && name.startsWith(`${r.name}/`) && (remote == null || r.name.length > remote.length)) {\n\t\t\tremote = r.name;\n\t\t}\n\t}\n\n\tconst branch = remote != null ? name.substring(remote.length + 1) : undefined;\n\treturn {\n\t\tname: name,\n\t\tremote: branch ? remote : undefined,\n\t\tbranch: branch || undefined,\n\t\tmissing: missing,\n\t};\n}\n\nfunction upstreamsEqual(a: WorktreeSummaryUpstream | undefined, b: WorktreeSummaryUpstream | undefined): boolean {\n\treturn (\n\t\ta === b ||\n\t\t(a != null &&\n\t\t\tb != null &&\n\t\t\ta.name === b.name &&\n\t\t\ta.remote === b.remote &&\n\t\t\ta.branch === b.branch &&\n\t\t\ta.missing === b.missing)\n\t);\n}\n\nfunction summariesEqual(a: WorktreeSummary, b: WorktreeSummary): boolean {\n\treturn (\n\t\ta.repoPath === b.repoPath &&\n\t\ta.branch === b.branch &&\n\t\ta.detached === b.detached &&\n\t\ta.sha === b.sha &&\n\t\ta.ahead === b.ahead &&\n\t\ta.behind === b.behind &&\n\t\ta.dirty === b.dirty &&\n\t\ta.stale === b.stale &&\n\t\tareEqual(a.baseDependsOn, b.baseDependsOn) &&\n\t\tupstreamsEqual(a.upstream, b.upstream) &&\n\t\t(a.base === b.base ||\n\t\t\t(a.base != null &&\n\t\t\t\tb.base != null &&\n\t\t\t\ta.base.ref === b.base.ref &&\n\t\t\t\ta.base.ahead === b.base.ahead &&\n\t\t\t\ta.base.sha === b.base.sha &&\n\t\t\t\ta.base.forkPoint === b.base.forkPoint &&\n\t\t\t\ta.base.tier === b.base.tier))\n\t);\n}\n"],"mappings":"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"}
@@ -49,6 +49,15 @@ export interface CliGitProviderOptions {
49
49
  * failures rather than an honest "unsupported here". Defaults to `true`.
50
50
  */
51
51
  localRepositories?: boolean;
52
+ /**
53
+ * Folders, relative to a repository's common git dir, whose worktrees core acts as if didn't exist: they are left out
54
+ * of worktree lists, and their add, remove, lock, unlock or move run through `notify: 'infer'`, and the watcher's
55
+ * reports of their admin entries, raise no `worktrees` change. The typed worktree writes still announce one. A host
56
+ * registers one for a tool's throwaway worktrees, which no host shows (sync-tools' `sync-tools-worktrees`, for one).
57
+ * Nothing else is ignored, since a user may keep real worktrees anywhere, the git dir included. A folder that isn't
58
+ * a literal path strictly inside the common git dir, or that lies under `worktrees`, is dropped.
59
+ */
60
+ ignoredWorktreeFolders?: readonly string[];
52
61
  }
53
62
  /**
54
63
  * Exposes `isTrackedWithDetails` as a callable method for sub-providers.
@@ -85,10 +94,27 @@ export declare class CliGitProvider implements GitProvider {
85
94
  /** The host's own hooks, before the provider wraps `cache.onReset` with its own clearing */
86
95
  private readonly _hostHooks;
87
96
  private readonly _localRepositories;
97
+ private readonly _ignoredWorktreeFolders;
98
+ /** By common git dir, the folders whose worktrees are ignored there: a common git dir never moves while it is open */
99
+ private readonly _ignoredWorktreeRoots;
88
100
  constructor(options: CliGitProviderOptions);
89
101
  /** The underlying cache instance (for repo path registration, etc.) */
90
102
  get cache(): Cache;
91
103
  get onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent>;
104
+ getIgnoredWorktreeFolders(_repoPath: string): readonly string[];
105
+ /**
106
+ * `[]`, reading nothing, when no folder is registered. Read once per common git dir, since a worktree list rebuild
107
+ * and every inferred worktree write ask for them; a read that fails or doesn't settle in time (a hung mount) rejects,
108
+ * and isn't kept, so the next call reads again
109
+ */
110
+ getIgnoredWorktreeRoots(repoPath: string): Promise<readonly string[]>;
111
+ /**
112
+ * From the cached git dir, or the repository's own files, without running git; a repository whose files don't
113
+ * show it (a bare one) runs git once, and the git dir it finds is cached with the repository
114
+ */
115
+ private findCommonGitDir;
116
+ /** `resolved` is false when the common git dir's resolved spelling couldn't be read */
117
+ private readIgnoredWorktreeRoots;
92
118
  getCommonPath(repoPath: string): string | undefined;
93
119
  private readonly _onDidAnnounceChanges;
94
120
  get onDidAnnounceChanges(): Event<AnnouncedRepositoryChanges>;
@@ -1,18 +1,20 @@
1
1
  import { isCancellationError } from "../utils/cancellation.js";
2
2
  import { iterateByDelimiter } from "../utils/string.js";
3
3
  import { getScopedLogger } from "../utils/logger.scoped.js";
4
+ import { cancellable } from "../utils/promise.js";
4
5
  import { Emitter } from "../utils/event.js";
5
6
  import { fileUri, joinUriPath, parseUri, toFsPath } from "../utils/uri.js";
6
7
  import { joinPaths, maybeUri, normalizePath, splitPath } from "../utils/path.js";
7
8
  import "../git/models/revision.js";
8
9
  import { isUncommitted } from "../git/utils/revision.utils.js";
9
- import { Cache } from "../git/cache.js";
10
+ import { Cache, resetDropsPendingRuns } from "../git/cache.js";
11
+ import { sanitizeIgnoredWorktreeFolders } from "../git/watching/watcherPatterns.js";
10
12
  import { getRefChangeKind } from "../git/watching/changeEvent.js";
11
13
  import { parseGitRemoteUrl } from "../git/utils/remote.utils.js";
12
14
  import { GitIgnoreFilter } from "../git/watching/gitIgnoreFilter.js";
13
15
  import { worktreeSharedChanges } from "./exec/commandClassifier.js";
14
16
  import { fsExists } from "../utils/env/node/exec.js";
15
- import { readHeadState } from "./exec/headState.js";
17
+ import { readCommonGitDir, readHeadState } from "./exec/headState.js";
16
18
  import { Git, batchLiteralPathspecs, gitConfigsStatus } from "./exec/git.js";
17
19
  import { foldMatchingIgnored } from "./parsers/statusParser.js";
18
20
  import { BlameGitSubProvider } from "./providers/blame.js";
@@ -39,6 +41,8 @@ import { homedir } from "node:os";
39
41
  import * as path from "node:path";
40
42
  import { env } from "node:process";
41
43
  //#region ../git-cli/src/cliGitProvider.ts
44
+ /** How long reading the folders whose worktrees are ignored may take before it counts as failed (a hung mount) */
45
+ const ignoredWorktreeRootsTimeoutMs = 5e3;
42
46
  /**
43
47
  * A fully-wired CLI-based git provider that executes git commands via `child_process`.
44
48
  * Implements both `CliGitProviderInternal` (for internal sub-provider wiring)
@@ -69,6 +73,9 @@ var CliGitProvider = class {
69
73
  /** The host's own hooks, before the provider wraps `cache.onReset` with its own clearing */
70
74
  _hostHooks;
71
75
  _localRepositories;
76
+ _ignoredWorktreeFolders;
77
+ /** By common git dir, the folders whose worktrees are ignored there: a common git dir never moves while it is open */
78
+ _ignoredWorktreeRoots = /* @__PURE__ */ new Map();
72
79
  constructor(options) {
73
80
  const hostHooks = options.context.hooks;
74
81
  this._hostHooks = hostHooks;
@@ -80,7 +87,7 @@ var CliGitProvider = class {
80
87
  ...hostHooks?.cache,
81
88
  onReset: (repoPath, ...types) => {
82
89
  this._cache.evictCaches(repoPath, ...types);
83
- this._git.clearPendingCommands(this.getRepositoryPaths(repoPath));
90
+ if (resetDropsPendingRuns(types)) this._git.clearPendingCommands(this.getRepositoryPaths(repoPath));
84
91
  hostHooks?.cache?.onReset?.(repoPath, ...types);
85
92
  }
86
93
  },
@@ -99,6 +106,7 @@ var CliGitProvider = class {
99
106
  }
100
107
  };
101
108
  this._localRepositories = options.localRepositories ?? true;
109
+ this._ignoredWorktreeFolders = sanitizeIgnoredWorktreeFolders(options.ignoredWorktreeFolders);
102
110
  this._git = options.git ?? new Git(options.locator, {
103
111
  ...options.gitOptions,
104
112
  isTrusted: options.context.workspace?.isTrusted != null ? () => options.context.workspace.isTrusted : void 0
@@ -107,6 +115,7 @@ var CliGitProvider = class {
107
115
  this._cache = options.cache instanceof Cache ? options.cache : new Cache();
108
116
  this._git.bindChangeNotifier((repoPaths, changes, sharedPaths, named) => this.notifyChangedCore(repoPaths, changes, void 0, sharedPaths, named));
109
117
  this._git.bindHeadReader((path) => readHeadState(path, this.context.fs, this._cache));
118
+ this._git.bindIgnoredWorktreeRootsReader((path) => this.getIgnoredWorktreeRoots(path));
110
119
  }
111
120
  /** The underlying cache instance (for repo path registration, etc.) */
112
121
  get cache() {
@@ -115,6 +124,58 @@ var CliGitProvider = class {
115
124
  get onDidChangeMergeTargets() {
116
125
  return this._cache.onDidChangeMergeTargets;
117
126
  }
127
+ getIgnoredWorktreeFolders(_repoPath) {
128
+ return this._ignoredWorktreeFolders;
129
+ }
130
+ /**
131
+ * `[]`, reading nothing, when no folder is registered. Read once per common git dir, since a worktree list rebuild
132
+ * and every inferred worktree write ask for them; a read that fails or doesn't settle in time (a hung mount) rejects,
133
+ * and isn't kept, so the next call reads again
134
+ */
135
+ async getIgnoredWorktreeRoots(repoPath) {
136
+ if (!this._ignoredWorktreeFolders.length) return [];
137
+ const commonGitDir = await cancellable(this.findCommonGitDir(repoPath), ignoredWorktreeRootsTimeoutMs);
138
+ let roots = this._ignoredWorktreeRoots.get(commonGitDir);
139
+ if (roots == null) {
140
+ const read = cancellable(this.readIgnoredWorktreeRoots(commonGitDir), ignoredWorktreeRootsTimeoutMs);
141
+ const entry = read.then((r) => r.roots);
142
+ roots = entry;
143
+ this._ignoredWorktreeRoots.set(commonGitDir, entry);
144
+ const forget = () => {
145
+ if (this._ignoredWorktreeRoots.get(commonGitDir) === entry) this._ignoredWorktreeRoots.delete(commonGitDir);
146
+ };
147
+ read.then((r) => r.resolved ? void 0 : forget(), forget);
148
+ }
149
+ return roots;
150
+ }
151
+ /**
152
+ * From the cached git dir, or the repository's own files, without running git; a repository whose files don't
153
+ * show it (a bare one) runs git once, and the git dir it finds is cached with the repository
154
+ */
155
+ async findCommonGitDir(repoPath) {
156
+ const commonGitDir = await readCommonGitDir(repoPath, this.context.fs, this._cache);
157
+ if (commonGitDir != null) return commonGitDir;
158
+ const gitDir = await this.config.getGitDir(repoPath);
159
+ return (gitDir.commonUri ?? gitDir.uri).fsPath;
160
+ }
161
+ /** `resolved` is false when the common git dir's resolved spelling couldn't be read */
162
+ async readIgnoredWorktreeRoots(commonGitDir) {
163
+ const roots = new Set(this._ignoredWorktreeFolders.map((f) => normalizePath(joinPaths(commonGitDir, f))));
164
+ if (!this._localRepositories) return {
165
+ roots: [...roots],
166
+ resolved: true
167
+ };
168
+ const realCommonGitDir = await promises.realpath(commonGitDir).catch(() => void 0);
169
+ if (realCommonGitDir == null) return {
170
+ roots: [...roots],
171
+ resolved: false
172
+ };
173
+ for (const folder of this._ignoredWorktreeFolders) roots.add(normalizePath(joinPaths(realCommonGitDir, folder)));
174
+ return {
175
+ roots: [...roots],
176
+ resolved: true
177
+ };
178
+ }
118
179
  getCommonPath(repoPath) {
119
180
  return this._cache.isRegistered(repoPath) ? this._cache.getCommonPath(repoPath) : void 0;
120
181
  }
@@ -219,7 +280,7 @@ var CliGitProvider = class {
219
280
  repoPath,
220
281
  types
221
282
  });
222
- for (const path of this.getRepositoryPaths(repoPath)) pendingPaths.add(path);
283
+ if (resetDropsPendingRuns(types)) for (const path of this.getRepositoryPaths(repoPath)) pendingPaths.add(path);
223
284
  }
224
285
  const named = [...announcements.keys()];
225
286
  for (const repoPath of named) for (const [path, siblingChanges] of this.getSiblingChanges(repoPath, changes)) {
@@ -396,7 +457,7 @@ var CliGitProvider = class {
396
457
  }
397
458
  _worktrees;
398
459
  get worktrees() {
399
- return this._worktrees ??= new WorktreesGitSubProvider(this.context, this._git, this._cache, this);
460
+ return this._worktrees ??= new WorktreesGitSubProvider(this.context, this._git, this._cache, this, this._ignoredWorktreeFolders.length ? (path) => this.getIgnoredWorktreeRoots(path) : void 0);
400
461
  }
401
462
  getAbsoluteUri(pathOrUri, base) {
402
463
  const baseUri = typeof base !== "string" ? base : fileUri(base);