@gitkraken/core-gitlens 0.16.4 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +39 -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/issue.d.ts +20 -1
  6. package/dist/git/models/issue.js +3 -1
  7. package/dist/git/models/issue.js.map +1 -1
  8. package/dist/git/models/repository.d.ts +4 -2
  9. package/dist/git/models/repository.js +1 -0
  10. package/dist/git/models/repository.js.map +1 -1
  11. package/dist/git/models/worktree.d.ts +5 -1
  12. package/dist/git/models/worktree.js +5 -3
  13. package/dist/git/models/worktree.js.map +1 -1
  14. package/dist/git/providers/provider.d.ts +17 -0
  15. package/dist/git/providers/worktrees.d.ts +17 -2
  16. package/dist/git/run.types.d.ts +7 -0
  17. package/dist/git/service.d.ts +5 -2
  18. package/dist/git/service.js +11 -5
  19. package/dist/git/service.js.map +1 -1
  20. package/dist/git/utils/issue.utils.js +5 -1
  21. package/dist/git/utils/issue.utils.js.map +1 -1
  22. package/dist/git/watching/classifyChange.d.ts +4 -3
  23. package/dist/git/watching/classifyChange.js +7 -4
  24. package/dist/git/watching/classifyChange.js.map +1 -1
  25. package/dist/git/watching/provider.d.ts +6 -0
  26. package/dist/git/watching/watchGroup.d.ts +142 -3
  27. package/dist/git/watching/watchGroup.js +424 -19
  28. package/dist/git/watching/watchGroup.js.map +1 -1
  29. package/dist/git/watching/watchService.d.ts +25 -0
  30. package/dist/git/watching/watchService.js +27 -7
  31. package/dist/git/watching/watchService.js.map +1 -1
  32. package/dist/git/watching/watchSession.d.ts +13 -8
  33. package/dist/git/watching/watchSession.js +50 -10
  34. package/dist/git/watching/watchSession.js.map +1 -1
  35. package/dist/git/watching/watcherPatterns.d.ts +14 -0
  36. package/dist/git/watching/watcherPatterns.js +38 -1
  37. package/dist/git/watching/watcherPatterns.js.map +1 -1
  38. package/dist/git/watching/worktreeSummary.d.ts +64 -2
  39. package/dist/git/watching/worktreeSummary.js +326 -13
  40. package/dist/git/watching/worktreeSummary.js.map +1 -1
  41. package/dist/git-cli/cliGitProvider.d.ts +26 -0
  42. package/dist/git-cli/cliGitProvider.js +66 -5
  43. package/dist/git-cli/cliGitProvider.js.map +1 -1
  44. package/dist/git-cli/exec/commandClassifier.d.ts +9 -3
  45. package/dist/git-cli/exec/commandClassifier.js +98 -17
  46. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  47. package/dist/git-cli/exec/git.d.ts +20 -0
  48. package/dist/git-cli/exec/git.js +69 -7
  49. package/dist/git-cli/exec/git.js.map +1 -1
  50. package/dist/git-cli/exec/headState.d.ts +5 -0
  51. package/dist/git-cli/exec/headState.js +14 -1
  52. package/dist/git-cli/exec/headState.js.map +1 -1
  53. package/dist/git-cli/parsers/worktreeParser.js +2 -2
  54. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  55. package/dist/git-cli/providers/branches.js +8 -4
  56. package/dist/git-cli/providers/branches.js.map +1 -1
  57. package/dist/git-cli/providers/graph.js +1 -1
  58. package/dist/git-cli/providers/graph.js.map +1 -1
  59. package/dist/git-cli/providers/operations.d.ts +3 -0
  60. package/dist/git-cli/providers/operations.js +14 -5
  61. package/dist/git-cli/providers/operations.js.map +1 -1
  62. package/dist/git-cli/providers/worktrees.d.ts +25 -3
  63. package/dist/git-cli/providers/worktrees.js +125 -32
  64. package/dist/git-cli/providers/worktrees.js.map +1 -1
  65. package/dist/git-cli/service.d.ts +2 -0
  66. package/dist/git-cli/service.js +4 -2
  67. package/dist/git-cli/service.js.map +1 -1
  68. package/dist/plus/integrations/models/gitHostIntegration.d.ts +13 -0
  69. package/dist/plus/integrations/models/gitHostIntegration.js +20 -3
  70. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  71. package/dist/plus/integrations/models/integration.d.ts +12 -0
  72. package/dist/plus/integrations/models/integration.js +7 -1
  73. package/dist/plus/integrations/models/integration.js.map +1 -1
  74. package/dist/plus/integrations/providers/azure/azure.d.ts +13 -0
  75. package/dist/plus/integrations/providers/azure/azure.js +11 -3
  76. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  77. package/dist/plus/integrations/providers/azure/models.d.ts +3 -1
  78. package/dist/plus/integrations/providers/azure/models.js +11 -5
  79. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  80. package/dist/plus/integrations/providers/azureDevOps.d.ts +33 -5
  81. package/dist/plus/integrations/providers/azureDevOps.js +75 -11
  82. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  83. package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -0
  84. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  85. package/dist/plus/integrations/providers/models.d.ts +19 -0
  86. package/dist/plus/integrations/providers/models.js +12 -3
  87. package/dist/plus/integrations/providers/models.js.map +1 -1
  88. package/dist/plus/integrations/reads/etag.js +6 -1
  89. package/dist/plus/integrations/reads/etag.js.map +1 -1
  90. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../../git/src/service.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from './cache.js';\nimport type { GitProvider } from './providers/provider.js';\nimport type { RepositoryService, SubProviderForRepo } from './repositoryService.js';\nimport {\n\tclearGlobalRepositoryServiceResolver,\n\tcreateSubProviderProxyForRepo,\n\tsetGlobalRepositoryServiceResolver,\n} from './repositoryService.js';\nimport type { FileWatchingProvider } from './watching/provider.js';\nimport type { WatchPollingOptions } from './watching/watchService.js';\nimport { RepositoryWatchService } from './watching/watchService.js';\nimport type { WorktreeSummaryServiceOptions } from './watching/worktreeSummary.js';\nimport { WorktreeSummaryService } from './watching/worktreeSummary.js';\n\nexport interface ProvidersChangeEvent {\n\treadonly added: readonly GitProvider[];\n\treadonly removed: readonly GitProvider[];\n\treadonly etag: number;\n}\n\n/**\n * Result of {@link GitService.validateRepo}.\n *\n * - `valid: true, safe: true` — a usable git repository at `repoPath`; `gitDir` / `commonGitDir` / `superprojectPath` are populated when available\n * - `valid: true, safe: false` — git detected unsafe (dubious) ownership at the given path; no `repoPath` is surfaced (the raw call doesn't carry one back)\n * - `valid: false` — not a git repository, no registered provider can handle the path, or the routed provider doesn't implement `getRepositoryInfo`\n */\nexport type ValidateRepoResult =\n\t| {\n\t\t\tvalid: true;\n\t\t\tsafe: true;\n\t\t\trepoPath: string;\n\t\t\tgitDir?: string;\n\t\t\tcommonGitDir?: string;\n\t\t\tsuperprojectPath?: string;\n\t }\n\t| { valid: true; safe: false }\n\t| { valid: false };\n\ninterface RegisteredProvider {\n\tprovider: GitProvider;\n\tcanHandle: (repoPath: string) => boolean;\n\t/** Forwards the provider's write announcements to {@link GitService.watchService}, when there is one */\n\tannouncements?: UnifiedDisposable;\n}\n\ninterface MergeTargetsSource {\n\treadonly subscription: UnifiedDisposable;\n\trefCount: number;\n}\n\n/**\n * Entry point — provider router, repo-scoped proxy cache, and global configuration.\n *\n * Creating an instance wires up the module-level repository\n * resolver. Calling {@link dispose} tears it down.\n *\n * Consumers register providers with routing predicates, then call\n * {@link forRepo} to get a {@link RepositoryService} with sub-provider\n * proxies that auto-inject the repository path.\n *\n * The service has no discovery or repository tracking state — it trusts\n * the caller to pass valid repository paths. Discovery and lifecycle\n * (open/close, file watching, etc.) are application-level concerns.\n *\n * @example\n * ```typescript\n * const service = GitService.createSingleton();\n * service.register(cliProvider, (path) => true);\n *\n * const repo = service.forRepo('/home/user/repo');\n * const branches = await repo.branches.getBranches();\n *\n * service.dispose(); // cleans up module-level hooks\n * ```\n */\nexport class GitService implements UnifiedDisposable {\n\tprivate static _instance: GitService | undefined;\n\n\t/**\n\t * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch, and\n\t * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`).\n\t * `options.verifyBaseRemeasure` is for tests only (see `WorktreeSummaryServiceOptions.verifyBaseRemeasure`)\n\t */\n\tstatic createSingleton(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\toptions?: {\n\t\t\twatchPolling?: WatchPollingOptions;\n\t\t\treadFile?: (path: string) => Promise<Uint8Array>;\n\t\t\tverifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];\n\t\t},\n\t): GitService {\n\t\tif (GitService._instance != null) {\n\t\t\tthrow new Error('GitService already exists — only one instance is allowed');\n\t\t}\n\n\t\tconst instance = new GitService(\n\t\t\twatchingProvider,\n\t\t\toptions?.watchPolling,\n\t\t\toptions?.readFile,\n\t\t\toptions?.verifyBaseRemeasure,\n\t\t);\n\t\tGitService._instance = instance;\n\t\treturn instance;\n\t}\n\n\tprivate readonly _onDidChangeProviders = new Emitter<ProvidersChangeEvent>();\n\tget onDidChangeProviders(): Event<ProvidersChangeEvent> {\n\t\treturn this._onDidChangeProviders.event;\n\t}\n\n\tprivate readonly _onDidChangeMergeTargets = new Emitter<MergeTargetsChangeEvent>();\n\t/**\n\t * Fires when a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that\n\t * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming\n\t * the branch. Once per change per repository (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.\n\t */\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._onDidChangeMergeTargets.event;\n\t}\n\n\t/** Keyed by the provider's event, so providers sharing one cache are forwarded once */\n\tprivate readonly _mergeTargetsSources = new Map<Event<MergeTargetsChangeEvent>, MergeTargetsSource>();\n\n\tprivate _etag = 0;\n\tprivate readonly _providers: RegisteredProvider[] = [];\n\tprivate readonly _serviceCache = new Map<string, RepositoryService>();\n\tprivate readonly _watchService: RepositoryWatchService | undefined;\n\tprivate readonly _summaries: WorktreeSummaryService | undefined;\n\n\tprivate constructor(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\twatchPolling?: WatchPollingOptions,\n\t\treadFile?: (path: string) => Promise<Uint8Array>,\n\t\tverifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'],\n\t) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tpolling: watchPolling,\n\t\t\t\treadFile: readFile,\n\t\t\t\tgetIgnoreFilter: (repoPath, gitDirPath) => {\n\t\t\t\t\treturn this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);\n\t\t\t\t},\n\t\t\t\tgetIgnoredDirectories: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([]),\n\t\t\t});\n\t\t\tthis._summaries = new WorktreeSummaryService({\n\t\t\t\twatchService: this._watchService,\n\t\t\t\tgetRepository: repoPath => this.forRepo(repoPath),\n\t\t\t\tonDidChangeMergeTargets: this.onDidChangeMergeTargets,\n\t\t\t\tgetCommonPath: repoPath => this.getProvider(repoPath)?.provider.getCommonPath?.(repoPath),\n\t\t\t\tverifyBaseRemeasure: verifyBaseRemeasure,\n\t\t\t});\n\t\t}\n\n\t\tsetGlobalRepositoryServiceResolver(repoPath => this.forRepo(repoPath));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tclearGlobalRepositoryServiceResolver();\n\t\tGitService._instance = undefined;\n\t\tthis._serviceCache.clear();\n\t\tthis._summaries?.dispose();\n\t\tthis._watchService?.dispose();\n\n\t\t// Don't dispose providers since they're owned by the caller, but do clear the array to release references\n\t\tfor (const entry of this._providers) {\n\t\t\tentry.announcements?.dispose();\n\t\t}\n\t\tthis._providers.length = 0;\n\n\t\tfor (const source of this._mergeTargetsSources.values()) {\n\t\t\tsource.subscription.dispose();\n\t\t}\n\t\tthis._mergeTargetsSources.clear();\n\n\t\tthis._onDidChangeProviders.dispose();\n\t\tthis._onDidChangeMergeTargets.dispose();\n\t}\n\n\t/** Monotonically increasing counter incremented on structural changes. */\n\tget etag(): number {\n\t\treturn this._etag;\n\t}\n\n\t/** Whether any providers are registered. */\n\tget hasProviders(): boolean {\n\t\treturn this._providers.length > 0;\n\t}\n\n\tget watchService(): RepositoryWatchService | undefined {\n\t\treturn this._watchService;\n\t}\n\n\t/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */\n\tget summaries(): WorktreeSummaryService | undefined {\n\t\treturn this._summaries;\n\t}\n\n\t/**\n\t * Registers a provider with a routing predicate.\n\t *\n\t * @returns A disposable that unregisters the provider.\n\t */\n\tregister(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable {\n\t\tconst watchService = this._watchService;\n\t\tconst entry: RegisteredProvider = {\n\t\t\tprovider: provider,\n\t\t\tcanHandle: canHandle,\n\t\t\tannouncements:\n\t\t\t\twatchService != null\n\t\t\t\t\t? provider.onDidAnnounceChanges?.(e =>\n\t\t\t\t\t\t\twatchService.noteSelfWrite(e.repoPath, e.changes, e.refs, {\n\t\t\t\t\t\t\t\tconfigConfinedToRefs: e.configConfinedToRefs,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t: undefined,\n\t\t};\n\t\tthis._providers.push(entry);\n\t\tthis._etag++;\n\t\tthis.addMergeTargetsSource(provider);\n\n\t\tthis._onDidChangeProviders.fire({\n\t\t\tadded: [provider],\n\t\t\tremoved: [],\n\t\t\tetag: this._etag,\n\t\t});\n\n\t\t// Invalidate cached proxies since provider routing may have changed\n\t\tthis._serviceCache.clear();\n\n\t\treturn createDisposable(() => {\n\t\t\tconst idx = this._providers.indexOf(entry);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis._providers.splice(idx, 1);\n\t\t\t\tentry.announcements?.dispose();\n\t\t\t\tthis._etag++;\n\t\t\t\tthis.removeMergeTargetsSource(provider);\n\t\t\t\tthis._serviceCache.clear();\n\n\t\t\t\tthis._onDidChangeProviders.fire({\n\t\t\t\t\tadded: [],\n\t\t\t\t\tremoved: [provider],\n\t\t\t\t\tetag: this._etag,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate addMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source != null) {\n\t\t\tsource.refCount++;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._mergeTargetsSources.set(event, {\n\t\t\tsubscription: event(e => this._onDidChangeMergeTargets.fire(e)),\n\t\t\trefCount: 1,\n\t\t});\n\t}\n\n\tprivate removeMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source == null || --source.refCount > 0) return;\n\n\t\tsource.subscription.dispose();\n\t\tthis._mergeTargetsSources.delete(event);\n\t}\n\n\t/** Returns the provider that can handle the given path, or undefined if none matches. */\n\tgetProvider(repoPath: string): { provider: GitProvider; path: string } | undefined {\n\t\trepoPath = normalizePath(repoPath);\n\t\tfor (const entry of this._providers) {\n\t\t\tif (entry.canHandle(repoPath)) {\n\t\t\t\treturn { provider: entry.provider, path: repoPath };\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Returns all registered providers. */\n\t*getProviders(): Iterable<GitProvider> {\n\t\tfor (const entry of this._providers) {\n\t\t\tyield entry.provider;\n\t\t}\n\t}\n\n\t/**\n\t * Routes a repo URI/path to a provider and returns a cached\n\t * {@link RepositoryService} with repo-scoped sub-provider proxies.\n\t * Returns undefined if no provider can handle the path.\n\t */\n\tforRepo(repoUri: Uri | string): RepositoryService | undefined {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\n\t\tlet repo = this._serviceCache.get(repoPath);\n\t\tif (repo != null) return repo;\n\n\t\tconst result = this.getProvider(repoPath);\n\t\tif (result == null) return undefined;\n\n\t\trepo = createRepositoryService(result.provider, repoPath, this._watchService);\n\t\tthis._serviceCache.set(repoPath, repo);\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Evicts the cached {@link RepositoryService} for a closed or removed repository.\n\t * No-op if the path isn't cached.\n\t */\n\tcloseRepo(repoUri: Uri | string): void {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\t\tthis._serviceCache.delete(repoPath);\n\t}\n\n\t/**\n\t * Validates whether `pathOrUri` points to (or resides inside) a git repository,\n\t * and returns information about it when it does.\n\t *\n\t * Routes the path to a registered provider and delegates to its config sub-provider's\n\t * `getRepositoryInfo` implementation, then normalizes the raw result into a clean\n\t * discriminated union. For a simple yes/no check, use `(await validateRepo(path)).valid`.\n\t *\n\t * Returns `{ valid: false }` if no provider can handle the path or if the routed\n\t * provider does not implement `getRepositoryInfo`.\n\t */\n\tasync validateRepo(pathOrUri: Uri | string): Promise<ValidateRepoResult> {\n\t\tconst key = getRepositoryKey(pathOrUri);\n\t\tconst routed = this.getProvider(key);\n\t\tif (routed == null) return { valid: false };\n\n\t\tconst info = await routed.provider.config.getRepositoryInfo?.(routed.path);\n\t\tif (info == null) return { valid: false };\n\n\t\tif (Array.isArray(info)) {\n\t\t\tif (info.length === 0) return { valid: false };\n\t\t\tif (info[0] === false) return { valid: true, safe: false };\n\t\t\treturn { valid: true, safe: true, repoPath: info[1] };\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsafe: true,\n\t\t\trepoPath: info.repoPath,\n\t\t\tgitDir: info.gitDir,\n\t\t\tcommonGitDir: info.commonGitDir,\n\t\t\tsuperprojectPath: info.superprojectPath,\n\t\t};\n\t}\n}\n\nfunction createRepositoryService(\n\tprovider: GitProvider,\n\trepoPath: string,\n\twatchService?: RepositoryWatchService,\n): RepositoryService {\n\tconst proxies = new Map<string, SubProviderForRepo<unknown>>();\n\n\tfunction getProxy<T extends object>(prop: string, subProvider: T | undefined): SubProviderForRepo<T> | undefined {\n\t\tif (subProvider == null) return undefined;\n\n\t\tlet proxy = proxies.get(prop);\n\t\tif (proxy == null) {\n\t\t\tproxy = createSubProviderProxyForRepo(subProvider, repoPath);\n\t\t\tproxies.set(prop, proxy);\n\t\t}\n\t\treturn proxy as SubProviderForRepo<T>;\n\t}\n\n\treturn {\n\t\tpath: repoPath,\n\t\tprovider: provider.descriptor,\n\n\t\tget etagWorkingTree(): number | undefined {\n\t\t\treturn watchService?.getSession(repoPath)?.etagWorkingTree;\n\t\t},\n\n\t\tgetAbsoluteUri: (relativePath: string): Uri => {\n\t\t\treturn provider.getAbsoluteUri(relativePath, repoPath);\n\t\t},\n\n\t\t// Required sub-providers\n\t\tget branches() {\n\t\t\tconst proxy = getProxy('branches', provider.branches);\n\t\t\tif (proxy == null) throw new Error('GitService: branches sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget commits() {\n\t\t\tconst proxy = getProxy('commits', provider.commits);\n\t\t\tif (proxy == null) throw new Error('GitService: commits sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget config() {\n\t\t\tconst proxy = getProxy('config', provider.config);\n\t\t\tif (proxy == null) throw new Error('GitService: config sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget contributors() {\n\t\t\tconst proxy = getProxy('contributors', provider.contributors);\n\t\t\tif (proxy == null) throw new Error('GitService: contributors sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget diff() {\n\t\t\tconst proxy = getProxy('diff', provider.diff);\n\t\t\tif (proxy == null) throw new Error('GitService: diff sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget graph() {\n\t\t\tconst proxy = getProxy('graph', provider.graph);\n\t\t\tif (proxy == null) throw new Error('GitService: graph sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget refs() {\n\t\t\tconst proxy = getProxy('refs', provider.refs);\n\t\t\tif (proxy == null) throw new Error('GitService: refs sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget remotes() {\n\t\t\tconst proxy = getProxy('remotes', provider.remotes);\n\t\t\tif (proxy == null) throw new Error('GitService: remotes sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget revision() {\n\t\t\tconst proxy = getProxy('revision', provider.revision);\n\t\t\tif (proxy == null) throw new Error('GitService: revision sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget status() {\n\t\t\tconst proxy = getProxy('status', provider.status);\n\t\t\tif (proxy == null) throw new Error('GitService: status sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget tags() {\n\t\t\tconst proxy = getProxy('tags', provider.tags);\n\t\t\tif (proxy == null) throw new Error('GitService: tags sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\n\t\t// Optional sub-providers\n\t\tget blame() {\n\t\t\treturn getProxy('blame', provider.blame);\n\t\t},\n\t\tget maintenance() {\n\t\t\treturn getProxy('maintenance', provider.maintenance);\n\t\t},\n\t\tget ops() {\n\t\t\treturn getProxy('ops', provider.ops);\n\t\t},\n\t\tget patch() {\n\t\t\treturn getProxy('patch', provider.patch);\n\t\t},\n\t\tget pausedOps() {\n\t\t\treturn getProxy('pausedOps', provider.pausedOps);\n\t\t},\n\t\tget staging() {\n\t\t\treturn getProxy('staging', provider.staging);\n\t\t},\n\t\tget stash() {\n\t\t\treturn getProxy('stash', provider.stash);\n\t\t},\n\t\tget worktrees() {\n\t\t\treturn getProxy('worktrees', provider.worktrees);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../git/src/service.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from './cache.js';\nimport type { GitProvider } from './providers/provider.js';\nimport type { RepositoryService, SubProviderForRepo } from './repositoryService.js';\nimport {\n\tclearGlobalRepositoryServiceResolver,\n\tcreateSubProviderProxyForRepo,\n\tsetGlobalRepositoryServiceResolver,\n} from './repositoryService.js';\nimport type { FileWatchingProvider } from './watching/provider.js';\nimport type { WatchPollingOptions } from './watching/watchService.js';\nimport { RepositoryWatchService } from './watching/watchService.js';\nimport type { WorktreeSummaryServiceOptions } from './watching/worktreeSummary.js';\nimport { WorktreeSummaryService } from './watching/worktreeSummary.js';\n\nexport interface ProvidersChangeEvent {\n\treadonly added: readonly GitProvider[];\n\treadonly removed: readonly GitProvider[];\n\treadonly etag: number;\n}\n\n/**\n * Result of {@link GitService.validateRepo}.\n *\n * - `valid: true, safe: true` — a usable git repository at `repoPath`; `gitDir` / `commonGitDir` / `superprojectPath` are populated when available\n * - `valid: true, safe: false` — git detected unsafe (dubious) ownership at the given path; no `repoPath` is surfaced (the raw call doesn't carry one back)\n * - `valid: false` — not a git repository, no registered provider can handle the path, or the routed provider doesn't implement `getRepositoryInfo`\n */\nexport type ValidateRepoResult =\n\t| {\n\t\t\tvalid: true;\n\t\t\tsafe: true;\n\t\t\trepoPath: string;\n\t\t\tgitDir?: string;\n\t\t\tcommonGitDir?: string;\n\t\t\tsuperprojectPath?: string;\n\t }\n\t| { valid: true; safe: false }\n\t| { valid: false };\n\ninterface RegisteredProvider {\n\tprovider: GitProvider;\n\tcanHandle: (repoPath: string) => boolean;\n\t/** Forwards the provider's write announcements to {@link GitService.watchService}, when there is one */\n\tannouncements?: UnifiedDisposable;\n}\n\ninterface MergeTargetsSource {\n\treadonly subscription: UnifiedDisposable;\n\trefCount: number;\n}\n\n/**\n * Entry point — provider router, repo-scoped proxy cache, and global configuration.\n *\n * Creating an instance wires up the module-level repository\n * resolver. Calling {@link dispose} tears it down.\n *\n * Consumers register providers with routing predicates, then call\n * {@link forRepo} to get a {@link RepositoryService} with sub-provider\n * proxies that auto-inject the repository path.\n *\n * The service has no discovery or repository tracking state — it trusts\n * the caller to pass valid repository paths. Discovery and lifecycle\n * (open/close, file watching, etc.) are application-level concerns.\n *\n * @example\n * ```typescript\n * const service = GitService.createSingleton();\n * service.register(cliProvider, (path) => true);\n *\n * const repo = service.forRepo('/home/user/repo');\n * const branches = await repo.branches.getBranches();\n *\n * service.dispose(); // cleans up module-level hooks\n * ```\n */\nexport class GitService implements UnifiedDisposable {\n\tprivate static _instance: GitService | undefined;\n\n\t/**\n\t * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch,\n\t * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`),\n\t * and with it `options.readDirectory` lets it drop the reports of ignored worktrees (see\n\t * `WatchServiceOptions.readDirectory`).\n\t * `options.verifyBaseRemeasure` is for tests only (see `WorktreeSummaryServiceOptions.verifyBaseRemeasure`)\n\t */\n\tstatic createSingleton(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\toptions?: {\n\t\t\twatchPolling?: WatchPollingOptions;\n\t\t\treadFile?: (path: string) => Promise<Uint8Array>;\n\t\t\treadDirectory?: (path: string) => Promise<readonly (readonly [string, number])[]>;\n\t\t\tverifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];\n\t\t},\n\t): GitService {\n\t\tif (GitService._instance != null) {\n\t\t\tthrow new Error('GitService already exists — only one instance is allowed');\n\t\t}\n\n\t\tconst instance = new GitService(\n\t\t\twatchingProvider,\n\t\t\toptions?.watchPolling,\n\t\t\toptions?.readFile,\n\t\t\toptions?.readDirectory,\n\t\t\toptions?.verifyBaseRemeasure,\n\t\t);\n\t\tGitService._instance = instance;\n\t\treturn instance;\n\t}\n\n\tprivate readonly _onDidChangeProviders = new Emitter<ProvidersChangeEvent>();\n\tget onDidChangeProviders(): Event<ProvidersChangeEvent> {\n\t\treturn this._onDidChangeProviders.event;\n\t}\n\n\tprivate readonly _onDidChangeMergeTargets = new Emitter<MergeTargetsChangeEvent>();\n\t/**\n\t * Fires when a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that\n\t * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming\n\t * the branch. Once per change per repository (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.\n\t */\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._onDidChangeMergeTargets.event;\n\t}\n\n\t/** Keyed by the provider's event, so providers sharing one cache are forwarded once */\n\tprivate readonly _mergeTargetsSources = new Map<Event<MergeTargetsChangeEvent>, MergeTargetsSource>();\n\n\tprivate _etag = 0;\n\tprivate readonly _providers: RegisteredProvider[] = [];\n\tprivate readonly _serviceCache = new Map<string, RepositoryService>();\n\tprivate readonly _watchService: RepositoryWatchService | undefined;\n\tprivate readonly _summaries: WorktreeSummaryService | undefined;\n\n\tprivate constructor(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\twatchPolling?: WatchPollingOptions,\n\t\treadFile?: (path: string) => Promise<Uint8Array>,\n\t\treadDirectory?: (path: string) => Promise<readonly (readonly [string, number])[]>,\n\t\tverifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'],\n\t) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tpolling: watchPolling,\n\t\t\t\treadFile: readFile,\n\t\t\t\treadDirectory: readDirectory,\n\t\t\t\tgetIgnoreFilter: (repoPath, gitDirPath) => {\n\t\t\t\t\treturn this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);\n\t\t\t\t},\n\t\t\t\tgetIgnoredDirectories: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([]),\n\t\t\t\tgetIgnoredWorktreeRoots: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredWorktreeRoots?.(repoPath) ?? Promise.resolve([]),\n\t\t\t\tgetIgnoredWorktreeFolders: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredWorktreeFolders?.(repoPath) ?? [],\n\t\t\t});\n\t\t\tthis._summaries = new WorktreeSummaryService({\n\t\t\t\twatchService: this._watchService,\n\t\t\t\tgetRepository: repoPath => this.forRepo(repoPath),\n\t\t\t\tonDidChangeMergeTargets: this.onDidChangeMergeTargets,\n\t\t\t\tgetCommonPath: repoPath => this.getProvider(repoPath)?.provider.getCommonPath?.(repoPath),\n\t\t\t\t// The host's polling stat, when it configured polling, answers whether an unwatched index file moved\n\t\t\t\tstat: watchPolling?.stat,\n\t\t\t\tverifyBaseRemeasure: verifyBaseRemeasure,\n\t\t\t});\n\t\t}\n\n\t\tsetGlobalRepositoryServiceResolver(repoPath => this.forRepo(repoPath));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tclearGlobalRepositoryServiceResolver();\n\t\tGitService._instance = undefined;\n\t\tthis._serviceCache.clear();\n\t\tthis._summaries?.dispose();\n\t\tthis._watchService?.dispose();\n\n\t\t// Don't dispose providers since they're owned by the caller, but do clear the array to release references\n\t\tfor (const entry of this._providers) {\n\t\t\tentry.announcements?.dispose();\n\t\t}\n\t\tthis._providers.length = 0;\n\n\t\tfor (const source of this._mergeTargetsSources.values()) {\n\t\t\tsource.subscription.dispose();\n\t\t}\n\t\tthis._mergeTargetsSources.clear();\n\n\t\tthis._onDidChangeProviders.dispose();\n\t\tthis._onDidChangeMergeTargets.dispose();\n\t}\n\n\t/** Monotonically increasing counter incremented on structural changes. */\n\tget etag(): number {\n\t\treturn this._etag;\n\t}\n\n\t/** Whether any providers are registered. */\n\tget hasProviders(): boolean {\n\t\treturn this._providers.length > 0;\n\t}\n\n\tget watchService(): RepositoryWatchService | undefined {\n\t\treturn this._watchService;\n\t}\n\n\t/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */\n\tget summaries(): WorktreeSummaryService | undefined {\n\t\treturn this._summaries;\n\t}\n\n\t/**\n\t * Registers a provider with a routing predicate.\n\t *\n\t * @returns A disposable that unregisters the provider.\n\t */\n\tregister(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable {\n\t\tconst watchService = this._watchService;\n\t\tconst entry: RegisteredProvider = {\n\t\t\tprovider: provider,\n\t\t\tcanHandle: canHandle,\n\t\t\tannouncements:\n\t\t\t\twatchService != null\n\t\t\t\t\t? provider.onDidAnnounceChanges?.(e =>\n\t\t\t\t\t\t\twatchService.noteSelfWrite(e.repoPath, e.changes, e.refs, {\n\t\t\t\t\t\t\t\tconfigConfinedToRefs: e.configConfinedToRefs,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t: undefined,\n\t\t};\n\t\tthis._providers.push(entry);\n\t\tthis._etag++;\n\t\tthis.addMergeTargetsSource(provider);\n\n\t\tthis._onDidChangeProviders.fire({\n\t\t\tadded: [provider],\n\t\t\tremoved: [],\n\t\t\tetag: this._etag,\n\t\t});\n\n\t\t// Invalidate cached proxies since provider routing may have changed\n\t\tthis._serviceCache.clear();\n\n\t\treturn createDisposable(() => {\n\t\t\tconst idx = this._providers.indexOf(entry);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis._providers.splice(idx, 1);\n\t\t\t\tentry.announcements?.dispose();\n\t\t\t\tthis._etag++;\n\t\t\t\tthis.removeMergeTargetsSource(provider);\n\t\t\t\tthis._serviceCache.clear();\n\n\t\t\t\tthis._onDidChangeProviders.fire({\n\t\t\t\t\tadded: [],\n\t\t\t\t\tremoved: [provider],\n\t\t\t\t\tetag: this._etag,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate addMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source != null) {\n\t\t\tsource.refCount++;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._mergeTargetsSources.set(event, {\n\t\t\tsubscription: event(e => this._onDidChangeMergeTargets.fire(e)),\n\t\t\trefCount: 1,\n\t\t});\n\t}\n\n\tprivate removeMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source == null || --source.refCount > 0) return;\n\n\t\tsource.subscription.dispose();\n\t\tthis._mergeTargetsSources.delete(event);\n\t}\n\n\t/** Returns the provider that can handle the given path, or undefined if none matches. */\n\tgetProvider(repoPath: string): { provider: GitProvider; path: string } | undefined {\n\t\trepoPath = normalizePath(repoPath);\n\t\tfor (const entry of this._providers) {\n\t\t\tif (entry.canHandle(repoPath)) {\n\t\t\t\treturn { provider: entry.provider, path: repoPath };\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Returns all registered providers. */\n\t*getProviders(): Iterable<GitProvider> {\n\t\tfor (const entry of this._providers) {\n\t\t\tyield entry.provider;\n\t\t}\n\t}\n\n\t/**\n\t * Routes a repo URI/path to a provider and returns a cached\n\t * {@link RepositoryService} with repo-scoped sub-provider proxies.\n\t * Returns undefined if no provider can handle the path.\n\t */\n\tforRepo(repoUri: Uri | string): RepositoryService | undefined {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\n\t\tlet repo = this._serviceCache.get(repoPath);\n\t\tif (repo != null) return repo;\n\n\t\tconst result = this.getProvider(repoPath);\n\t\tif (result == null) return undefined;\n\n\t\trepo = createRepositoryService(result.provider, repoPath, this._watchService);\n\t\tthis._serviceCache.set(repoPath, repo);\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Evicts the cached {@link RepositoryService} for a closed or removed repository.\n\t * No-op if the path isn't cached.\n\t */\n\tcloseRepo(repoUri: Uri | string): void {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\t\tthis._serviceCache.delete(repoPath);\n\t}\n\n\t/**\n\t * Validates whether `pathOrUri` points to (or resides inside) a git repository,\n\t * and returns information about it when it does.\n\t *\n\t * Routes the path to a registered provider and delegates to its config sub-provider's\n\t * `getRepositoryInfo` implementation, then normalizes the raw result into a clean\n\t * discriminated union. For a simple yes/no check, use `(await validateRepo(path)).valid`.\n\t *\n\t * Returns `{ valid: false }` if no provider can handle the path or if the routed\n\t * provider does not implement `getRepositoryInfo`.\n\t */\n\tasync validateRepo(pathOrUri: Uri | string): Promise<ValidateRepoResult> {\n\t\tconst key = getRepositoryKey(pathOrUri);\n\t\tconst routed = this.getProvider(key);\n\t\tif (routed == null) return { valid: false };\n\n\t\tconst info = await routed.provider.config.getRepositoryInfo?.(routed.path);\n\t\tif (info == null) return { valid: false };\n\n\t\tif (Array.isArray(info)) {\n\t\t\tif (info.length === 0) return { valid: false };\n\t\t\tif (info[0] === false) return { valid: true, safe: false };\n\t\t\treturn { valid: true, safe: true, repoPath: info[1] };\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsafe: true,\n\t\t\trepoPath: info.repoPath,\n\t\t\tgitDir: info.gitDir,\n\t\t\tcommonGitDir: info.commonGitDir,\n\t\t\tsuperprojectPath: info.superprojectPath,\n\t\t};\n\t}\n}\n\nfunction createRepositoryService(\n\tprovider: GitProvider,\n\trepoPath: string,\n\twatchService?: RepositoryWatchService,\n): RepositoryService {\n\tconst proxies = new Map<string, SubProviderForRepo<unknown>>();\n\n\tfunction getProxy<T extends object>(prop: string, subProvider: T | undefined): SubProviderForRepo<T> | undefined {\n\t\tif (subProvider == null) return undefined;\n\n\t\tlet proxy = proxies.get(prop);\n\t\tif (proxy == null) {\n\t\t\tproxy = createSubProviderProxyForRepo(subProvider, repoPath);\n\t\t\tproxies.set(prop, proxy);\n\t\t}\n\t\treturn proxy as SubProviderForRepo<T>;\n\t}\n\n\treturn {\n\t\tpath: repoPath,\n\t\tprovider: provider.descriptor,\n\n\t\tget etagWorkingTree(): number | undefined {\n\t\t\treturn watchService?.getSession(repoPath)?.etagWorkingTree;\n\t\t},\n\n\t\tgetAbsoluteUri: (relativePath: string): Uri => {\n\t\t\treturn provider.getAbsoluteUri(relativePath, repoPath);\n\t\t},\n\n\t\t// Required sub-providers\n\t\tget branches() {\n\t\t\tconst proxy = getProxy('branches', provider.branches);\n\t\t\tif (proxy == null) throw new Error('GitService: branches sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget commits() {\n\t\t\tconst proxy = getProxy('commits', provider.commits);\n\t\t\tif (proxy == null) throw new Error('GitService: commits sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget config() {\n\t\t\tconst proxy = getProxy('config', provider.config);\n\t\t\tif (proxy == null) throw new Error('GitService: config sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget contributors() {\n\t\t\tconst proxy = getProxy('contributors', provider.contributors);\n\t\t\tif (proxy == null) throw new Error('GitService: contributors sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget diff() {\n\t\t\tconst proxy = getProxy('diff', provider.diff);\n\t\t\tif (proxy == null) throw new Error('GitService: diff sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget graph() {\n\t\t\tconst proxy = getProxy('graph', provider.graph);\n\t\t\tif (proxy == null) throw new Error('GitService: graph sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget refs() {\n\t\t\tconst proxy = getProxy('refs', provider.refs);\n\t\t\tif (proxy == null) throw new Error('GitService: refs sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget remotes() {\n\t\t\tconst proxy = getProxy('remotes', provider.remotes);\n\t\t\tif (proxy == null) throw new Error('GitService: remotes sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget revision() {\n\t\t\tconst proxy = getProxy('revision', provider.revision);\n\t\t\tif (proxy == null) throw new Error('GitService: revision sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget status() {\n\t\t\tconst proxy = getProxy('status', provider.status);\n\t\t\tif (proxy == null) throw new Error('GitService: status sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget tags() {\n\t\t\tconst proxy = getProxy('tags', provider.tags);\n\t\t\tif (proxy == null) throw new Error('GitService: tags sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\n\t\t// Optional sub-providers\n\t\tget blame() {\n\t\t\treturn getProxy('blame', provider.blame);\n\t\t},\n\t\tget maintenance() {\n\t\t\treturn getProxy('maintenance', provider.maintenance);\n\t\t},\n\t\tget ops() {\n\t\t\treturn getProxy('ops', provider.ops);\n\t\t},\n\t\tget patch() {\n\t\t\treturn getProxy('patch', provider.patch);\n\t\t},\n\t\tget pausedOps() {\n\t\t\treturn getProxy('pausedOps', provider.pausedOps);\n\t\t},\n\t\tget staging() {\n\t\t\treturn getProxy('staging', provider.staging);\n\t\t},\n\t\tget stash() {\n\t\t\treturn getProxy('stash', provider.stash);\n\t\t},\n\t\tget worktrees() {\n\t\t\treturn getProxy('worktrees', provider.worktrees);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -165,7 +165,11 @@ function serializeIssue(value) {
165
165
  isActive: iteration.isActive,
166
166
  startDate: iteration.startDate,
167
167
  endDate: iteration.endDate
168
- }))
168
+ })),
169
+ viewer: value.viewer != null ? {
170
+ id: value.viewer.id,
171
+ username: value.viewer.username
172
+ } : void 0
169
173
  };
170
174
  }
171
175
  /** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */
@@ -1 +1 @@
1
- {"version":3,"file":"issue.utils.js","names":[],"sources":["../../../../git/src/utils/issue.utils.ts"],"sourcesContent":["import type {\n\tIssue,\n\tIssueProviderState,\n\tIssueRepositoryIdentityDescriptor,\n\tIssueShape,\n\tIssueSortField,\n\tIssueSorting,\n} from '../models/issue.js';\nimport { defaultIssueSort } from '../models/issue.js';\n\n/**\n * How each sort field is read off an {@link IssueShape}, for the reads that MERGE several provider queries and so\n * have to order the union themselves instead of letting the provider do it.\n *\n * Only the fields the shape actually carries appear. `priority`, `dueDate` and `resolved` are absent because\n * `IssueOrPullRequest`/`IssueShape` doesn't model them: a provider may well order by them server-side on a\n * single-query read, but a merged one cannot — which is exactly what {@link getIssueComparator} returning\n * `undefined` lets its caller refuse, instead of concatenating unordered runs and calling them sorted.\n */\nconst issueSortValues: Partial<Record<IssueSortField, (issue: IssueShape) => number | Date | string | undefined>> = {\n\tcreated: i => i.createdDate,\n\tupdated: i => i.updatedDate,\n\tclosed: i => i.closedDate,\n\tcomments: i => i.commentsCount,\n\treactions: i => i.thumbsUpCount,\n\ttitle: i => i.title,\n};\n\n/**\n * The order a read that ALWAYS orders is being made under: the caller's key, or the facade's default.\n *\n * The one place `?? defaultIssueSort` may be written. Several things downstream must agree on the answer — the\n * emitted provider qualifier, the fingerprint sealed into a cursor, the comparator that re-sorts a merged page and\n * the ceiling warning that names the order — and re-deriving the fallback at each is how they come to disagree,\n * which is not cosmetic: a cursor sealed with a key its query wasn't built with makes the fingerprint check refuse\n * valid resumptions.\n *\n * Note this is for a surface that CAN always order. A read whose provider may not express even the default resolves\n * through `resolveIssueSort` instead, which can answer \"leave the provider's own order\".\n */\nexport function effectiveIssueSort(sort: IssueSorting | undefined): IssueSorting {\n\treturn sort ?? defaultIssueSort;\n}\n\n/**\n * A comparator for one sort key over normalized issues, or `undefined` when the field isn't derivable from an\n * {@link IssueShape} — the signal that the key is only honorable on a SINGLE-origin read, where the provider\n * already ordered the page.\n *\n * `@gitkraken/provider-apis` carries a twin of this over its own `Issue` shape (`src/issueSort.ts`), which it uses\n * for the merges it performs itself. It is not importable — the SDK does not export it, and it takes a different\n * shape — so the two are separate implementations of one rule, and the rule below is the part that must not drift.\n *\n * Missing values sort LAST in both directions. That is deliberately not the same as negating the descending\n * comparator (which would float them to the front when ascending), and it is why they're partitioned before any\n * arithmetic rather than mapped to an infinite sentinel: two missing values have to compare equal, and\n * `-Infinity - -Infinity` is `NaN`, which makes the comparator inconsistent and the resulting order\n * implementation-defined.\n *\n * Note it orders whatever it is handed. Sorting a page that was already capped does not make it the top N.\n *\n * KNOWN DIVERGENCE, `title` only: strings are compared by code unit, so `'B' < 'a'`. A single-scope read is\n * ordered by the provider's own database instead — case-insensitive and accent-aware — so the same `title:asc`\n * yields \"Banana, apple, ábaco\" from a merged page and \"ábaco, apple, Banana\" from one repository. Deliberately\n * NOT fixed with `localeCompare`: the SDK's twin comparator (which performs the merges core doesn't, notably\n * GitLab's repo-ids aggregate) compares by code unit, so switching here would make two paths of the SAME\n * multi-repo read disagree — a worse divergence than the one it closes, and it would break the parity test in\n * `issueSortCapabilities.test.ts`. Closing this properly means changing the rule on BOTH sides at once; until\n * then it is a cosmetic ordering difference on one key, not wrong data.\n */\nexport function getIssueComparator(sort: IssueSorting): ((a: IssueShape, b: IssueShape) => number) | undefined {\n\t// The union's shape guarantees both halves, so this needs no validation — only the cast tsc can't infer.\n\tconst [field, direction] = sort.split(':') as [IssueSortField, 'asc' | 'desc'];\n\tconst getValue = issueSortValues[field];\n\tif (getValue == null) return undefined;\n\n\treturn (a, b) => {\n\t\tconst left = toComparable(getValue(a));\n\t\tconst right = toComparable(getValue(b));\n\t\tif (left == null || right == null) {\n\t\t\tif (left == null && right == null) return 0;\n\t\t\treturn left == null ? 1 : -1;\n\t\t}\n\n\t\tconst ordered = left < right ? -1 : left > right ? 1 : 0;\n\t\treturn direction === 'asc' ? ordered : -ordered;\n\t};\n}\n\nfunction toComparable(value: number | Date | string | undefined): number | string | undefined {\n\treturn value instanceof Date ? value.getTime() : value;\n}\n\nexport function getRepositoryIdentityForIssue(issue: IssueShape | Issue): IssueRepositoryIdentityDescriptor {\n\tif (issue.repository == null) throw new Error('Missing repository');\n\n\treturn {\n\t\tremote: {\n\t\t\turl: issue.repository.url,\n\t\t\tdomain: issue.provider.domain,\n\t\t},\n\t\tname: `${issue.repository.owner}/${issue.repository.repo}`,\n\t\tprovider: {\n\t\t\tid: issue.provider.id,\n\t\t\tdomain: issue.provider.domain,\n\t\t\trepoDomain: issue.repository.owner,\n\t\t\trepoName: issue.repository.repo,\n\t\t\trepoOwnerDomain: issue.repository.owner,\n\t\t},\n\t};\n}\n\n/**\n * Deliberately leaves `projection` out: this copy drops `repository.accessLevel`, so a tag would have field presence\n * report the viewer's access as fetched when the copy no longer has it.\n */\nexport function serializeIssue(value: IssueShape): IssueShape {\n\tconst serialized: IssueShape = {\n\t\ttype: value.type,\n\t\tprovider: {\n\t\t\tid: value.provider.id,\n\t\t\tname: value.provider.name,\n\t\t\tdomain: value.provider.domain,\n\t\t\ticon: value.provider.icon,\n\t\t},\n\t\tid: value.id,\n\t\tnodeId: value.nodeId,\n\t\tissueType: value.issueType,\n\t\ttitle: value.title,\n\t\turl: value.url,\n\t\tcreatedDate: value.createdDate,\n\t\tupdatedDate: value.updatedDate,\n\t\tclosedDate: value.closedDate,\n\t\tclosed: value.closed,\n\t\tstate: value.state,\n\t\tproviderState:\n\t\t\tvalue.providerState == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.providerState.id,\n\t\t\t\t\t\tname: value.providerState.name,\n\t\t\t\t\t\tcolor: value.providerState.color,\n\t\t\t\t\t\tcategory: value.providerState.category,\n\t\t\t\t\t},\n\t\tauthor:\n\t\t\tvalue.author == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.author.id,\n\t\t\t\t\t\tname: value.author.name,\n\t\t\t\t\t\tusername: value.author.username,\n\t\t\t\t\t\tavatarUrl: value.author.avatarUrl,\n\t\t\t\t\t\turl: value.author.url,\n\t\t\t\t\t},\n\t\trepository:\n\t\t\tvalue.repository == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\towner: value.repository.owner,\n\t\t\t\t\t\trepo: value.repository.repo,\n\t\t\t\t\t\turl: value.repository.url,\n\t\t\t\t\t},\n\t\tproject:\n\t\t\tvalue.project == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.project.id,\n\t\t\t\t\t\tname: value.project.name,\n\t\t\t\t\t\tresourceId: value.project.resourceId,\n\t\t\t\t\t\tresourceName: value.project.resourceName,\n\t\t\t\t\t},\n\t\tassignees: value.assignees.map(assignee => ({\n\t\t\tid: assignee.id,\n\t\t\tname: assignee.name,\n\t\t\tusername: assignee.username,\n\t\t\tavatarUrl: assignee.avatarUrl,\n\t\t\turl: assignee.url,\n\t\t})),\n\t\tlabels:\n\t\t\tvalue.labels == null\n\t\t\t\t? undefined\n\t\t\t\t: value.labels.map(label => ({\n\t\t\t\t\t\tcolor: label.color,\n\t\t\t\t\t\tname: label.name,\n\t\t\t\t\t})),\n\t\tcommentsCount: value.commentsCount,\n\t\tthumbsUpCount: value.thumbsUpCount,\n\t\tbody: value.body,\n\t\tbodyFormat: value.bodyFormat,\n\t\tbodyField: value.bodyField == null ? undefined : { id: value.bodyField.id, name: value.bodyField.name },\n\t\titerations:\n\t\t\tvalue.iterations == null\n\t\t\t\t? undefined\n\t\t\t\t: value.iterations.map(iteration => ({\n\t\t\t\t\t\tid: iteration.id,\n\t\t\t\t\t\tname: iteration.name,\n\t\t\t\t\t\tisActive: iteration.isActive,\n\t\t\t\t\t\tstartDate: iteration.startDate,\n\t\t\t\t\t\tendDate: iteration.endDate,\n\t\t\t\t\t})),\n\t};\n\treturn serialized;\n}\n\n/** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */\nexport function isTerminalIssueStateCategory(category: IssueProviderState['category']): boolean {\n\treturn category === 'DONE' || category === 'CANCELED';\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"issue.utils.js","names":[],"sources":["../../../../git/src/utils/issue.utils.ts"],"sourcesContent":["import type {\n\tIssue,\n\tIssueProviderState,\n\tIssueRepositoryIdentityDescriptor,\n\tIssueShape,\n\tIssueSortField,\n\tIssueSorting,\n} from '../models/issue.js';\nimport { defaultIssueSort } from '../models/issue.js';\n\n/**\n * How each sort field is read off an {@link IssueShape}, for the reads that MERGE several provider queries and so\n * have to order the union themselves instead of letting the provider do it.\n *\n * Only the fields the shape actually carries appear. `priority`, `dueDate` and `resolved` are absent because\n * `IssueOrPullRequest`/`IssueShape` doesn't model them: a provider may well order by them server-side on a\n * single-query read, but a merged one cannot — which is exactly what {@link getIssueComparator} returning\n * `undefined` lets its caller refuse, instead of concatenating unordered runs and calling them sorted.\n */\nconst issueSortValues: Partial<Record<IssueSortField, (issue: IssueShape) => number | Date | string | undefined>> = {\n\tcreated: i => i.createdDate,\n\tupdated: i => i.updatedDate,\n\tclosed: i => i.closedDate,\n\tcomments: i => i.commentsCount,\n\treactions: i => i.thumbsUpCount,\n\ttitle: i => i.title,\n};\n\n/**\n * The order a read that ALWAYS orders is being made under: the caller's key, or the facade's default.\n *\n * The one place `?? defaultIssueSort` may be written. Several things downstream must agree on the answer — the\n * emitted provider qualifier, the fingerprint sealed into a cursor, the comparator that re-sorts a merged page and\n * the ceiling warning that names the order — and re-deriving the fallback at each is how they come to disagree,\n * which is not cosmetic: a cursor sealed with a key its query wasn't built with makes the fingerprint check refuse\n * valid resumptions.\n *\n * Note this is for a surface that CAN always order. A read whose provider may not express even the default resolves\n * through `resolveIssueSort` instead, which can answer \"leave the provider's own order\".\n */\nexport function effectiveIssueSort(sort: IssueSorting | undefined): IssueSorting {\n\treturn sort ?? defaultIssueSort;\n}\n\n/**\n * A comparator for one sort key over normalized issues, or `undefined` when the field isn't derivable from an\n * {@link IssueShape} — the signal that the key is only honorable on a SINGLE-origin read, where the provider\n * already ordered the page.\n *\n * `@gitkraken/provider-apis` carries a twin of this over its own `Issue` shape (`src/issueSort.ts`), which it uses\n * for the merges it performs itself. It is not importable — the SDK does not export it, and it takes a different\n * shape — so the two are separate implementations of one rule, and the rule below is the part that must not drift.\n *\n * Missing values sort LAST in both directions. That is deliberately not the same as negating the descending\n * comparator (which would float them to the front when ascending), and it is why they're partitioned before any\n * arithmetic rather than mapped to an infinite sentinel: two missing values have to compare equal, and\n * `-Infinity - -Infinity` is `NaN`, which makes the comparator inconsistent and the resulting order\n * implementation-defined.\n *\n * Note it orders whatever it is handed. Sorting a page that was already capped does not make it the top N.\n *\n * KNOWN DIVERGENCE, `title` only: strings are compared by code unit, so `'B' < 'a'`. A single-scope read is\n * ordered by the provider's own database instead — case-insensitive and accent-aware — so the same `title:asc`\n * yields \"Banana, apple, ábaco\" from a merged page and \"ábaco, apple, Banana\" from one repository. Deliberately\n * NOT fixed with `localeCompare`: the SDK's twin comparator (which performs the merges core doesn't, notably\n * GitLab's repo-ids aggregate) compares by code unit, so switching here would make two paths of the SAME\n * multi-repo read disagree — a worse divergence than the one it closes, and it would break the parity test in\n * `issueSortCapabilities.test.ts`. Closing this properly means changing the rule on BOTH sides at once; until\n * then it is a cosmetic ordering difference on one key, not wrong data.\n */\nexport function getIssueComparator(sort: IssueSorting): ((a: IssueShape, b: IssueShape) => number) | undefined {\n\t// The union's shape guarantees both halves, so this needs no validation — only the cast tsc can't infer.\n\tconst [field, direction] = sort.split(':') as [IssueSortField, 'asc' | 'desc'];\n\tconst getValue = issueSortValues[field];\n\tif (getValue == null) return undefined;\n\n\treturn (a, b) => {\n\t\tconst left = toComparable(getValue(a));\n\t\tconst right = toComparable(getValue(b));\n\t\tif (left == null || right == null) {\n\t\t\tif (left == null && right == null) return 0;\n\t\t\treturn left == null ? 1 : -1;\n\t\t}\n\n\t\tconst ordered = left < right ? -1 : left > right ? 1 : 0;\n\t\treturn direction === 'asc' ? ordered : -ordered;\n\t};\n}\n\nfunction toComparable(value: number | Date | string | undefined): number | string | undefined {\n\treturn value instanceof Date ? value.getTime() : value;\n}\n\nexport function getRepositoryIdentityForIssue(issue: IssueShape | Issue): IssueRepositoryIdentityDescriptor {\n\tif (issue.repository == null) throw new Error('Missing repository');\n\n\treturn {\n\t\tremote: {\n\t\t\turl: issue.repository.url,\n\t\t\tdomain: issue.provider.domain,\n\t\t},\n\t\tname: `${issue.repository.owner}/${issue.repository.repo}`,\n\t\tprovider: {\n\t\t\tid: issue.provider.id,\n\t\t\tdomain: issue.provider.domain,\n\t\t\trepoDomain: issue.repository.owner,\n\t\t\trepoName: issue.repository.repo,\n\t\t\trepoOwnerDomain: issue.repository.owner,\n\t\t},\n\t};\n}\n\n/**\n * Deliberately leaves `projection` out: this copy drops `repository.accessLevel`, so a tag would have field presence\n * report the viewer's access as fetched when the copy no longer has it.\n */\nexport function serializeIssue(value: IssueShape): IssueShape {\n\tconst serialized: IssueShape = {\n\t\ttype: value.type,\n\t\tprovider: {\n\t\t\tid: value.provider.id,\n\t\t\tname: value.provider.name,\n\t\t\tdomain: value.provider.domain,\n\t\t\ticon: value.provider.icon,\n\t\t},\n\t\tid: value.id,\n\t\tnodeId: value.nodeId,\n\t\tissueType: value.issueType,\n\t\ttitle: value.title,\n\t\turl: value.url,\n\t\tcreatedDate: value.createdDate,\n\t\tupdatedDate: value.updatedDate,\n\t\tclosedDate: value.closedDate,\n\t\tclosed: value.closed,\n\t\tstate: value.state,\n\t\tproviderState:\n\t\t\tvalue.providerState == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.providerState.id,\n\t\t\t\t\t\tname: value.providerState.name,\n\t\t\t\t\t\tcolor: value.providerState.color,\n\t\t\t\t\t\tcategory: value.providerState.category,\n\t\t\t\t\t},\n\t\tauthor:\n\t\t\tvalue.author == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.author.id,\n\t\t\t\t\t\tname: value.author.name,\n\t\t\t\t\t\tusername: value.author.username,\n\t\t\t\t\t\tavatarUrl: value.author.avatarUrl,\n\t\t\t\t\t\turl: value.author.url,\n\t\t\t\t\t},\n\t\trepository:\n\t\t\tvalue.repository == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\towner: value.repository.owner,\n\t\t\t\t\t\trepo: value.repository.repo,\n\t\t\t\t\t\turl: value.repository.url,\n\t\t\t\t\t},\n\t\tproject:\n\t\t\tvalue.project == null\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\tid: value.project.id,\n\t\t\t\t\t\tname: value.project.name,\n\t\t\t\t\t\tresourceId: value.project.resourceId,\n\t\t\t\t\t\tresourceName: value.project.resourceName,\n\t\t\t\t\t},\n\t\tassignees: value.assignees.map(assignee => ({\n\t\t\tid: assignee.id,\n\t\t\tname: assignee.name,\n\t\t\tusername: assignee.username,\n\t\t\tavatarUrl: assignee.avatarUrl,\n\t\t\turl: assignee.url,\n\t\t})),\n\t\tlabels:\n\t\t\tvalue.labels == null\n\t\t\t\t? undefined\n\t\t\t\t: value.labels.map(label => ({\n\t\t\t\t\t\tcolor: label.color,\n\t\t\t\t\t\tname: label.name,\n\t\t\t\t\t})),\n\t\tcommentsCount: value.commentsCount,\n\t\tthumbsUpCount: value.thumbsUpCount,\n\t\tbody: value.body,\n\t\tbodyFormat: value.bodyFormat,\n\t\tbodyField: value.bodyField == null ? undefined : { id: value.bodyField.id, name: value.bodyField.name },\n\t\titerations:\n\t\t\tvalue.iterations == null\n\t\t\t\t? undefined\n\t\t\t\t: value.iterations.map(iteration => ({\n\t\t\t\t\t\tid: iteration.id,\n\t\t\t\t\t\tname: iteration.name,\n\t\t\t\t\t\tisActive: iteration.isActive,\n\t\t\t\t\t\tstartDate: iteration.startDate,\n\t\t\t\t\t\tendDate: iteration.endDate,\n\t\t\t\t\t})),\n\t\tviewer: value.viewer != null ? { id: value.viewer.id, username: value.viewer.username } : undefined,\n\t};\n\treturn serialized;\n}\n\n/** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */\nexport function isTerminalIssueStateCategory(category: IssueProviderState['category']): boolean {\n\treturn category === 'DONE' || category === 'CANCELED';\n}\n"],"mappings":"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"}
@@ -13,9 +13,10 @@ export declare const allWatchedRepositoryChanges: readonly RepositoryChange[];
13
13
  * to which logical repository changes.
14
14
  *
15
15
  * `worktrees` means the worktree list, or what `git worktree list` reports per worktree, changed:
16
- * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. Any other
17
- * path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state, logs, …) is that worktree's
18
- * own state, which its own gitdir watcher classifies, so it yields `undefined` here.
16
+ * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. `worktrees/<n>/HEAD`
17
+ * is also `worktreeHead`: that worktree's HEAD moved, which can change what any worktree's git reads answer, where
18
+ * the rest changes only the list. Any other path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state,
19
+ * logs, …) is that worktree's own state, which its own gitdir watcher classifies, so it yields `undefined` here.
19
20
  *
20
21
  * @param relativePath - Path relative to the common `.git` directory or a worktree's gitdir
21
22
  * (e.g., `refs/heads/main`, `HEAD`, `config`, `worktrees/foo/HEAD`)
@@ -12,6 +12,7 @@ const allWatchedRepositoryChanges = [
12
12
  "stash",
13
13
  "remotes",
14
14
  "worktrees",
15
+ "worktreeHead",
15
16
  "config",
16
17
  "cherryPick",
17
18
  "merge",
@@ -31,9 +32,10 @@ const watchedRefRegex = /^refs\/(?:heads|remotes|stash|tags|replace)(?:\/|\.lock
31
32
  * to which logical repository changes.
32
33
  *
33
34
  * `worktrees` means the worktree list, or what `git worktree list` reports per worktree, changed:
34
- * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. Any other
35
- * path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state, logs, …) is that worktree's
36
- * own state, which its own gitdir watcher classifies, so it yields `undefined` here.
35
+ * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. `worktrees/<n>/HEAD`
36
+ * is also `worktreeHead`: that worktree's HEAD moved, which can change what any worktree's git reads answer, where
37
+ * the rest changes only the list. Any other path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state,
38
+ * logs, …) is that worktree's own state, which its own gitdir watcher classifies, so it yields `undefined` here.
37
39
  *
38
40
  * @param relativePath - Path relative to the common `.git` directory or a worktree's gitdir
39
41
  * (e.g., `refs/heads/main`, `HEAD`, `config`, `worktrees/foo/HEAD`)
@@ -50,7 +52,8 @@ function classifyGitDirChange(relativePath) {
50
52
  const idEnd = relativePath.indexOf("/", 10);
51
53
  if (idEnd === -1) return relativePath.length > 10 ? ["worktrees"] : void 0;
52
54
  const file = relativePath.substring(idEnd + 1);
53
- return file === "HEAD" || file === "locked" || file === "gitdir" ? ["worktrees"] : void 0;
55
+ if (file === "HEAD") return ["worktrees", "worktreeHead"];
56
+ return file === "locked" || file === "gitdir" ? ["worktrees"] : void 0;
54
57
  }
55
58
  }
56
59
  if (relativePath.startsWith("refs/")) {
@@ -1 +1 @@
1
- {"version":3,"file":"classifyChange.js","names":[],"sources":["../../../../git/src/watching/classifyChange.ts"],"sourcesContent":["import type { RepositoryChange } from '../models/repository.js';\n\n/**\n * Everything a `.git` watcher can report. Used when events were lost (`overflow`), so every cache type those\n * kinds clear is refreshed. Deliberately not `'unknown'`: `Cache.onRepositoryChanged` treats that as a close and\n * unregisters the path, after which shared evictions stop reaching it.\n */\nexport const allWatchedRepositoryChanges: readonly RepositoryChange[] = [\n\t'index',\n\t'head',\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'config',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'pausedOp',\n\t'ignores',\n\t'gkConfig',\n];\n\nconst classifyRegex =\n\t/(index|HEAD|FETCH_HEAD|ORIG_HEAD|CHERRY_PICK_HEAD|MERGE_HEAD|REBASE_HEAD|rebase-merge|rebase-apply|sequencer|REVERT_HEAD|config|gk\\/config|info\\/exclude|refs\\/(?:heads|remotes|stash|tags|replace)|packed-refs|shallow)/;\n/** A ref, ref directory or ref lock in a namespace {@link classifyRegex} maps; of the locks, only the stash's counts */\nconst watchedRefRegex = /^refs\\/(?:heads|remotes|stash|tags|replace)(?:\\/|\\.lock$|$)/;\n\n/**\n * Maps a path relative to a `.git` directory to the corresponding\n * {@link RepositoryChange} types. This is pure Git knowledge — the\n * regex and switch table encode which files in `.git/` correspond\n * to which logical repository changes.\n *\n * `worktrees` means the worktree list, or what `git worktree list` reports per worktree, changed:\n * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. Any other\n * path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state, logs, …) is that worktree's\n * own state, which its own gitdir watcher classifies, so it yields `undefined` here.\n *\n * @param relativePath - Path relative to the common `.git` directory or a worktree's gitdir\n * (e.g., `refs/heads/main`, `HEAD`, `config`, `worktrees/foo/HEAD`)\n * @returns An array of change types, or `undefined` if the path\n * is noise or unrecognized. `undefined` for `FETCH_HEAD` which\n * is intentionally not mapped to a change type (the extension\n * handles it as a last-fetched timestamp).\n */\nexport function classifyGitDirChange(relativePath: string): RepositoryChange[] | undefined {\n\t// User-edited todo file (and its backup) — handled by VS Code's TextDocument\n\t// events in the rebase webview (see `rebaseWebviewProvider.ts`). Suppress here\n\t// to avoid thrashing the repo update path on every save during an interactive\n\t// rebase; rebase lifecycle (start/stop/step) still fires from sibling files\n\t// in `rebase-merge/` and the directory itself.\n\tif (relativePath.endsWith('/git-rebase-todo') || relativePath.endsWith('/git-rebase-todo.backup')) {\n\t\treturn undefined;\n\t}\n\n\tif (relativePath.startsWith('worktrees')) {\n\t\tif (relativePath.length === 9) return ['worktrees'];\n\n\t\tif (relativePath.charCodeAt(9) === 47 /* / */) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd === -1) return relativePath.length > 10 ? ['worktrees'] : undefined;\n\n\t\t\tconst file = relativePath.substring(idEnd + 1);\n\t\t\treturn file === 'HEAD' || file === 'locked' || file === 'gitdir' ? ['worktrees'] : undefined;\n\t\t}\n\t}\n\n\t// A ref outside the namespaces core reads (a tool's own, such as `refs/undo/*`) is no change, whatever words its\n\t// name holds: `refs/kepler/x/fix-index` must not read as the index\n\tif (relativePath.startsWith('refs/')) {\n\t\tif (!watchedRefRegex.test(relativePath)) return undefined;\n\n\t\t// A ref's lock file is no change of the ref (no ref name ends in `.lock`): git renames it over the ref, which\n\t\t// reports the ref itself, or removes it with the ref unchanged, as every fetch does with\n\t\t// `refs/remotes/<remote>/HEAD` to check the remote's default branch. Except the stash's: its entries are its\n\t\t// reflog (`logs/`, unwatched), which `git reflog expire` (as `gc` runs it) and `git reflog delete` rewrite under\n\t\t// that lock alone\n\t\tif (relativePath.endsWith('.lock') && relativePath !== 'refs/stash.lock') return undefined;\n\t}\n\n\tconst match = classifyRegex.exec(relativePath);\n\tif (match == null) return undefined;\n\n\tswitch (match[1]) {\n\t\tcase 'config':\n\t\t\treturn ['config', 'remotes'];\n\n\t\tcase 'gk/config':\n\t\t\treturn ['gkConfig'];\n\n\t\tcase 'info/exclude':\n\t\t\treturn ['ignores'];\n\n\t\tcase 'index':\n\t\t\treturn ['index'];\n\n\t\tcase 'FETCH_HEAD':\n\t\t\t// No RepositoryChange — extension handles this for last-fetched timestamps\n\t\t\treturn undefined;\n\n\t\tcase 'HEAD':\n\t\t\treturn ['head', 'heads'];\n\n\t\tcase 'ORIG_HEAD':\n\t\t\treturn ['heads'];\n\n\t\tcase 'shallow':\n\t\tcase 'refs/replace':\n\t\t\t// Ancestry changes affect graph, blame/history, health, and ahead/behind consumers even\n\t\t\t// when tips stay put. Reuse their shared history invalidation rather than only refreshing Graph.\n\t\t\treturn ['heads'];\n\n\t\tcase 'CHERRY_PICK_HEAD':\n\t\t\treturn ['cherryPick', 'pausedOp'];\n\n\t\tcase 'MERGE_HEAD':\n\t\t\treturn ['merge', 'pausedOp'];\n\n\t\tcase 'REBASE_HEAD':\n\t\tcase 'rebase-merge':\n\t\tcase 'rebase-apply':\n\t\t\treturn ['rebase', 'pausedOp'];\n\n\t\tcase 'REVERT_HEAD':\n\t\t\treturn ['revert', 'pausedOp'];\n\n\t\tcase 'sequencer':\n\t\t\treturn ['pausedOp'];\n\n\t\t// `'heads'` is deliberately the ONLY thing a `refs/heads/*` event yields, including a create. It is\n\t\t// tempting to read a create as \"a new branch exists under this name\" and use it to drop anything\n\t\t// derived from the old branch's identity (its base, its merge target). That signal does not mean\n\t\t// what it looks like: git rewrites an EXISTING loose ref by writing `<name>.lock` and renaming it\n\t\t// over the file, which watchers report as a create of the destination — so every ordinary commit\n\t\t// would look like a new branch and discard a live branch's metadata. Nothing here can distinguish\n\t\t// the two; identity changes are handled where GitLens performs the operation itself.\n\t\tcase 'refs/heads':\n\t\t\treturn ['heads'];\n\n\t\tcase 'refs/remotes':\n\t\t\treturn ['remotes'];\n\n\t\tcase 'refs/stash':\n\t\t\treturn ['stash'];\n\n\t\tcase 'refs/tags':\n\t\t\treturn ['tags'];\n\n\t\t// A `packed-refs` rewrite (after `git pack-refs` or `git gc`) changes ref state\n\t\t// WITHOUT any `refs/**` file event — deleting a packed branch, for example, only\n\t\t// rewrites this file. The file content is not inspected here, so classification\n\t\t// is deliberately broad: it can add, remove, or move refs of any of these types.\n\t\tcase 'packed-refs':\n\t\t\treturn ['heads', 'remotes', 'tags'];\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n"],"mappings":";;;;;;AAOA,MAAa,8BAA2D;CACvE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AAEA,MAAM,gBACL;;AAED,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;AAoBxB,SAAgB,qBAAqB,cAAsD;CAM1F,IAAI,aAAa,SAAS,kBAAkB,KAAK,aAAa,SAAS,yBAAyB,GAC/F;CAGD,IAAI,aAAa,WAAW,WAAW,GAAG;EACzC,IAAI,aAAa,WAAW,GAAG,OAAO,CAAC,WAAW;EAElD,IAAI,aAAa,WAAW,CAAC,MAAM,IAAY;GAC9C,MAAM,QAAQ,aAAa,QAAQ,KAAK,EAAE;GAC1C,IAAI,UAAU,IAAI,OAAO,aAAa,SAAS,KAAK,CAAC,WAAW,IAAI,KAAA;GAEpE,MAAM,OAAO,aAAa,UAAU,QAAQ,CAAC;GAC7C,OAAO,SAAS,UAAU,SAAS,YAAY,SAAS,WAAW,CAAC,WAAW,IAAI,KAAA;EACpF;CACD;CAIA,IAAI,aAAa,WAAW,OAAO,GAAG;EACrC,IAAI,CAAC,gBAAgB,KAAK,YAAY,GAAG,OAAO,KAAA;EAOhD,IAAI,aAAa,SAAS,OAAO,KAAK,iBAAiB,mBAAmB,OAAO,KAAA;CAClF;CAEA,MAAM,QAAQ,cAAc,KAAK,YAAY;CAC7C,IAAI,SAAS,MAAM,OAAO,KAAA;CAE1B,QAAQ,MAAM,IAAd;EACC,KAAK,UACJ,OAAO,CAAC,UAAU,SAAS;EAE5B,KAAK,aACJ,OAAO,CAAC,UAAU;EAEnB,KAAK,gBACJ,OAAO,CAAC,SAAS;EAElB,KAAK,SACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,cAEJ;EAED,KAAK,QACJ,OAAO,CAAC,QAAQ,OAAO;EAExB,KAAK,aACJ,OAAO,CAAC,OAAO;EAEhB,KAAK;EACL,KAAK,gBAGJ,OAAO,CAAC,OAAO;EAEhB,KAAK,oBACJ,OAAO,CAAC,cAAc,UAAU;EAEjC,KAAK,cACJ,OAAO,CAAC,SAAS,UAAU;EAE5B,KAAK;EACL,KAAK;EACL,KAAK,gBACJ,OAAO,CAAC,UAAU,UAAU;EAE7B,KAAK,eACJ,OAAO,CAAC,UAAU,UAAU;EAE7B,KAAK,aACJ,OAAO,CAAC,UAAU;EASnB,KAAK,cACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,gBACJ,OAAO,CAAC,SAAS;EAElB,KAAK,cACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,aACJ,OAAO,CAAC,MAAM;EAMf,KAAK,eACJ,OAAO;GAAC;GAAS;GAAW;EAAM;EAEnC,SACC;CACF;AACD"}
1
+ {"version":3,"file":"classifyChange.js","names":[],"sources":["../../../../git/src/watching/classifyChange.ts"],"sourcesContent":["import type { RepositoryChange } from '../models/repository.js';\n\n/**\n * Everything a `.git` watcher can report. Used when events were lost (`overflow`), so every cache type those\n * kinds clear is refreshed. Deliberately not `'unknown'`: `Cache.onRepositoryChanged` treats that as a close and\n * unregisters the path, after which shared evictions stop reaching it.\n */\nexport const allWatchedRepositoryChanges: readonly RepositoryChange[] = [\n\t'index',\n\t'head',\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'worktreeHead',\n\t'config',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'pausedOp',\n\t'ignores',\n\t'gkConfig',\n];\n\nconst classifyRegex =\n\t/(index|HEAD|FETCH_HEAD|ORIG_HEAD|CHERRY_PICK_HEAD|MERGE_HEAD|REBASE_HEAD|rebase-merge|rebase-apply|sequencer|REVERT_HEAD|config|gk\\/config|info\\/exclude|refs\\/(?:heads|remotes|stash|tags|replace)|packed-refs|shallow)/;\n/** A ref, ref directory or ref lock in a namespace {@link classifyRegex} maps; of the locks, only the stash's counts */\nconst watchedRefRegex = /^refs\\/(?:heads|remotes|stash|tags|replace)(?:\\/|\\.lock$|$)/;\n\n/**\n * Maps a path relative to a `.git` directory to the corresponding\n * {@link RepositoryChange} types. This is pure Git knowledge — the\n * regex and switch table encode which files in `.git/` correspond\n * to which logical repository changes.\n *\n * `worktrees` means the worktree list, or what `git worktree list` reports per worktree, changed:\n * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. `worktrees/<n>/HEAD`\n * is also `worktreeHead`: that worktree's HEAD moved, which can change what any worktree's git reads answer, where\n * the rest changes only the list. Any other path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state,\n * logs, …) is that worktree's own state, which its own gitdir watcher classifies, so it yields `undefined` here.\n *\n * @param relativePath - Path relative to the common `.git` directory or a worktree's gitdir\n * (e.g., `refs/heads/main`, `HEAD`, `config`, `worktrees/foo/HEAD`)\n * @returns An array of change types, or `undefined` if the path\n * is noise or unrecognized. `undefined` for `FETCH_HEAD` which\n * is intentionally not mapped to a change type (the extension\n * handles it as a last-fetched timestamp).\n */\nexport function classifyGitDirChange(relativePath: string): RepositoryChange[] | undefined {\n\t// User-edited todo file (and its backup) — handled by VS Code's TextDocument\n\t// events in the rebase webview (see `rebaseWebviewProvider.ts`). Suppress here\n\t// to avoid thrashing the repo update path on every save during an interactive\n\t// rebase; rebase lifecycle (start/stop/step) still fires from sibling files\n\t// in `rebase-merge/` and the directory itself.\n\tif (relativePath.endsWith('/git-rebase-todo') || relativePath.endsWith('/git-rebase-todo.backup')) {\n\t\treturn undefined;\n\t}\n\n\tif (relativePath.startsWith('worktrees')) {\n\t\tif (relativePath.length === 9) return ['worktrees'];\n\n\t\tif (relativePath.charCodeAt(9) === 47 /* / */) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd === -1) return relativePath.length > 10 ? ['worktrees'] : undefined;\n\n\t\t\tconst file = relativePath.substring(idEnd + 1);\n\t\t\tif (file === 'HEAD') return ['worktrees', 'worktreeHead'];\n\n\t\t\treturn file === 'locked' || file === 'gitdir' ? ['worktrees'] : undefined;\n\t\t}\n\t}\n\n\t// A ref outside the namespaces core reads (a tool's own, such as `refs/undo/*`) is no change, whatever words its\n\t// name holds: `refs/kepler/x/fix-index` must not read as the index\n\tif (relativePath.startsWith('refs/')) {\n\t\tif (!watchedRefRegex.test(relativePath)) return undefined;\n\n\t\t// A ref's lock file is no change of the ref (no ref name ends in `.lock`): git renames it over the ref, which\n\t\t// reports the ref itself, or removes it with the ref unchanged, as every fetch does with\n\t\t// `refs/remotes/<remote>/HEAD` to check the remote's default branch. Except the stash's: its entries are its\n\t\t// reflog (`logs/`, unwatched), which `git reflog expire` (as `gc` runs it) and `git reflog delete` rewrite under\n\t\t// that lock alone\n\t\tif (relativePath.endsWith('.lock') && relativePath !== 'refs/stash.lock') return undefined;\n\t}\n\n\tconst match = classifyRegex.exec(relativePath);\n\tif (match == null) return undefined;\n\n\tswitch (match[1]) {\n\t\tcase 'config':\n\t\t\treturn ['config', 'remotes'];\n\n\t\tcase 'gk/config':\n\t\t\treturn ['gkConfig'];\n\n\t\tcase 'info/exclude':\n\t\t\treturn ['ignores'];\n\n\t\tcase 'index':\n\t\t\treturn ['index'];\n\n\t\tcase 'FETCH_HEAD':\n\t\t\t// No RepositoryChange — extension handles this for last-fetched timestamps\n\t\t\treturn undefined;\n\n\t\tcase 'HEAD':\n\t\t\treturn ['head', 'heads'];\n\n\t\tcase 'ORIG_HEAD':\n\t\t\treturn ['heads'];\n\n\t\tcase 'shallow':\n\t\tcase 'refs/replace':\n\t\t\t// Ancestry changes affect graph, blame/history, health, and ahead/behind consumers even\n\t\t\t// when tips stay put. Reuse their shared history invalidation rather than only refreshing Graph.\n\t\t\treturn ['heads'];\n\n\t\tcase 'CHERRY_PICK_HEAD':\n\t\t\treturn ['cherryPick', 'pausedOp'];\n\n\t\tcase 'MERGE_HEAD':\n\t\t\treturn ['merge', 'pausedOp'];\n\n\t\tcase 'REBASE_HEAD':\n\t\tcase 'rebase-merge':\n\t\tcase 'rebase-apply':\n\t\t\treturn ['rebase', 'pausedOp'];\n\n\t\tcase 'REVERT_HEAD':\n\t\t\treturn ['revert', 'pausedOp'];\n\n\t\tcase 'sequencer':\n\t\t\treturn ['pausedOp'];\n\n\t\t// `'heads'` is deliberately the ONLY thing a `refs/heads/*` event yields, including a create. It is\n\t\t// tempting to read a create as \"a new branch exists under this name\" and use it to drop anything\n\t\t// derived from the old branch's identity (its base, its merge target). That signal does not mean\n\t\t// what it looks like: git rewrites an EXISTING loose ref by writing `<name>.lock` and renaming it\n\t\t// over the file, which watchers report as a create of the destination — so every ordinary commit\n\t\t// would look like a new branch and discard a live branch's metadata. Nothing here can distinguish\n\t\t// the two; identity changes are handled where GitLens performs the operation itself.\n\t\tcase 'refs/heads':\n\t\t\treturn ['heads'];\n\n\t\tcase 'refs/remotes':\n\t\t\treturn ['remotes'];\n\n\t\tcase 'refs/stash':\n\t\t\treturn ['stash'];\n\n\t\tcase 'refs/tags':\n\t\t\treturn ['tags'];\n\n\t\t// A `packed-refs` rewrite (after `git pack-refs` or `git gc`) changes ref state\n\t\t// WITHOUT any `refs/**` file event — deleting a packed branch, for example, only\n\t\t// rewrites this file. The file content is not inspected here, so classification\n\t\t// is deliberately broad: it can add, remove, or move refs of any of these types.\n\t\tcase 'packed-refs':\n\t\t\treturn ['heads', 'remotes', 'tags'];\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n"],"mappings":";;;;;;AAOA,MAAa,8BAA2D;CACvE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AAEA,MAAM,gBACL;;AAED,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;AAqBxB,SAAgB,qBAAqB,cAAsD;CAM1F,IAAI,aAAa,SAAS,kBAAkB,KAAK,aAAa,SAAS,yBAAyB,GAC/F;CAGD,IAAI,aAAa,WAAW,WAAW,GAAG;EACzC,IAAI,aAAa,WAAW,GAAG,OAAO,CAAC,WAAW;EAElD,IAAI,aAAa,WAAW,CAAC,MAAM,IAAY;GAC9C,MAAM,QAAQ,aAAa,QAAQ,KAAK,EAAE;GAC1C,IAAI,UAAU,IAAI,OAAO,aAAa,SAAS,KAAK,CAAC,WAAW,IAAI,KAAA;GAEpE,MAAM,OAAO,aAAa,UAAU,QAAQ,CAAC;GAC7C,IAAI,SAAS,QAAQ,OAAO,CAAC,aAAa,cAAc;GAExD,OAAO,SAAS,YAAY,SAAS,WAAW,CAAC,WAAW,IAAI,KAAA;EACjE;CACD;CAIA,IAAI,aAAa,WAAW,OAAO,GAAG;EACrC,IAAI,CAAC,gBAAgB,KAAK,YAAY,GAAG,OAAO,KAAA;EAOhD,IAAI,aAAa,SAAS,OAAO,KAAK,iBAAiB,mBAAmB,OAAO,KAAA;CAClF;CAEA,MAAM,QAAQ,cAAc,KAAK,YAAY;CAC7C,IAAI,SAAS,MAAM,OAAO,KAAA;CAE1B,QAAQ,MAAM,IAAd;EACC,KAAK,UACJ,OAAO,CAAC,UAAU,SAAS;EAE5B,KAAK,aACJ,OAAO,CAAC,UAAU;EAEnB,KAAK,gBACJ,OAAO,CAAC,SAAS;EAElB,KAAK,SACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,cAEJ;EAED,KAAK,QACJ,OAAO,CAAC,QAAQ,OAAO;EAExB,KAAK,aACJ,OAAO,CAAC,OAAO;EAEhB,KAAK;EACL,KAAK,gBAGJ,OAAO,CAAC,OAAO;EAEhB,KAAK,oBACJ,OAAO,CAAC,cAAc,UAAU;EAEjC,KAAK,cACJ,OAAO,CAAC,SAAS,UAAU;EAE5B,KAAK;EACL,KAAK;EACL,KAAK,gBACJ,OAAO,CAAC,UAAU,UAAU;EAE7B,KAAK,eACJ,OAAO,CAAC,UAAU,UAAU;EAE7B,KAAK,aACJ,OAAO,CAAC,UAAU;EASnB,KAAK,cACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,gBACJ,OAAO,CAAC,SAAS;EAElB,KAAK,cACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,aACJ,OAAO,CAAC,MAAM;EAMf,KAAK,eACJ,OAAO;GAAC;GAAS;GAAW;EAAM;EAEnC,SACC;CACF;AACD"}
@@ -42,6 +42,12 @@ export interface FileWatcherOptions {
42
42
  * already down reports `unwatchable` itself. Going from `unwatchable` to anything else makes core rescan.
43
43
  */
44
44
  readonly onDidChangeReliability?: (state: FileWatcherReliability) => void;
45
+ /**
46
+ * Called once, when a watch the provider places asynchronously goes live: whatever changed under `basePath` between
47
+ * `createWatcher` and then went unseen, so core rescans. A provider whose watch is live when `createWatcher` returns
48
+ * never calls it. Passed only to working-tree watchers, whether or not the host opted into polling.
49
+ */
50
+ readonly onDidStart?: () => void;
45
51
  }
46
52
  /**
47
53
  * Factory for creating filesystem watchers. The library calls this
@@ -61,14 +61,51 @@ interface SessionEntry {
61
61
  * starts, is the baseline and delivers nothing; a report arriving before it completes may describe a change it
62
62
  * already holds, so it is delivered unconfined. A read that fails or doesn't settle within a few seconds, or a file
63
63
  * git couldn't parse, is unconfined too.
64
+ *
65
+ * Given a `readFile`, a `readDirectory` (a directory's entries with their types, only the directories counting as
66
+ * admin entries) and the folders whose worktrees the host has core ignore (a tool's throwaway worktrees, which no
67
+ * host shows), the group reports no change of an ignored worktree's admin entry
68
+ * (`<common>/worktrees/<id>`) to any session. Only registered folders count, since a user may keep real worktrees
69
+ * anywhere, `.git` included. It lists the entries and reads each one's `gitdir` once as it starts, and again after a
70
+ * watcher dropped events; a report arriving meanwhile is told at once, and decided once the listing settles, so what
71
+ * is known of its entry catches up. From then on it reads an entry only when its `gitdir` may be new (the entry was
72
+ * created, or its `gitdir` written, or it was removed before), so a real worktree's HEAD moves are told at once,
73
+ * unread. Of two reads of one entry, the later decides both reports. An entry it doesn't know whose `gitdir` can't be
74
+ * read (a watcher can report one before git writes it, and miss the write) is read once more a moment later, its
75
+ * reports held until then, and told if it still can't be. Only the entry's directory or its `gitdir` going is a
76
+ * removal; a deleted `HEAD` (a watcher may report git's lock file renamed over it as a delete) or `locked` is told as
77
+ * the entry is known. Removing an entry deletes the `gitdir` a read needs, so a removal is told from what the entry was
78
+ * known to be (before a read that couldn't read it, if one is waiting to read it again), and an entry it never knew is
79
+ * told as real. Any other report of a removed entry but a delete is read, and one that can't be read is the removal's
80
+ * own (a watcher may report the entry's directory changed beside each file it deletes), unless it creates the entry
81
+ * again. A report of the `worktrees` directory itself (a coalesced
82
+ * removal, a parent directory change, a poll, which sees nothing finer) lists it again: it is told to every session
83
+ * when a real entry was added or removed since the last listing, and to none when only ignored ones were. Git removes
84
+ * the directory with its last entry, so a directory that isn't there is an empty listing, as is a removal of it that
85
+ * can't be listed. Since a listing (or a worktree moved into or out of a folder) changes what every session shows, it
86
+ * is told to all of them; the other watcher's report of the same change then finds nothing left to tell. Where only a
87
+ * poll watches the common dir, it sees just the `worktrees` directory, so an entry removed and added again under the
88
+ * same id between two polls, or an entry's HEAD or lock changed in place, goes untold.
89
+ *
90
+ * Given the folders, the watchers over the common dir leave each one unwatched, since a tool's checkout there would
91
+ * otherwise have every directory of it watched; one that shares a top-level directory with what the `.git` watch reads
92
+ * stays watched.
64
93
  */
65
94
  export declare class WatchGroup implements UnifiedDisposable {
66
95
  readonly commonGitDir: string;
67
96
  private readonly provider;
68
97
  /** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */
69
98
  private readonly poller?;
70
- /** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */
99
+ /**
100
+ * When given, reports of the shared `.git/config` are classified by what changed in it, and admin entries are
101
+ * read to tell an ignored worktree's (see above)
102
+ */
71
103
  private readonly readFile?;
104
+ /**
105
+ * Returns a directory's entries with their types; with `readFile`, the admin entries are listed, only the
106
+ * directories counting (see above)
107
+ */
108
+ private readonly readDirectory?;
72
109
  private readonly _sessions;
73
110
  private _commonWatcher;
74
111
  private _commonReliability;
@@ -80,11 +117,52 @@ export declare class WatchGroup implements UnifiedDisposable {
80
117
  private _configBaselining;
81
118
  private _configReading;
82
119
  private _configRereadQueued;
120
+ /**
121
+ * Settles once the latest baseline of the admin entries was taken; `undefined` when the group can't tell an
122
+ * ignored worktree's, so every report is told at once
123
+ */
124
+ private _worktreesBaseline;
125
+ /** A baseline is pending, so a report waits for it */
126
+ private _worktreesBaselining;
127
+ private _worktreesRebaselineQueued;
128
+ /** Set by the first baseline; while empty, nothing is ignored and every report is told at once */
129
+ private _ignoredWorktreeRoots;
130
+ /** Listings run one at a time, so each compares with the one before it */
131
+ private _worktreesQueue;
132
+ /** The `worktrees` directory's reports waiting on the queued listing; `undefined` while none is queued */
133
+ private _worktreesRescanQueued;
134
+ /** The admin entries the last listing found */
135
+ private _listedWorktreeIds;
136
+ /** Admin entries known to exist, by id: whether each is an ignored worktree's */
137
+ private readonly _worktreeIds;
138
+ /** What removed admin entries were, by id, oldest first */
139
+ private readonly _removedWorktreeIds;
140
+ /**
141
+ * Each id's latest `gitdir` read, settling with what it found and what the id was known to be before it; only it
142
+ * may update what is known of the id. A removal of the entry drops it
143
+ */
144
+ private readonly _worktreeReads;
145
+ /** Admin entries whose `gitdir` couldn't be read, by id, waiting to be read once more */
146
+ private readonly _worktreeRetries;
147
+ /** The `.git` excludes of a watcher over the common git dir, with the ignored worktrees' folders it may leave out */
148
+ private readonly _commonGitDirExcludes;
83
149
  constructor(commonGitDir: string, provider: FileWatchingProvider,
84
150
  /** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */
85
151
  poller?: GitDirPoller | undefined,
86
- /** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */
87
- readFile?: ((path: string) => Promise<Uint8Array>) | undefined);
152
+ /**
153
+ * When given, reports of the shared `.git/config` are classified by what changed in it, and admin entries are
154
+ * read to tell an ignored worktree's (see above)
155
+ */
156
+ readFile?: ((path: string) => Promise<Uint8Array>) | undefined,
157
+ /**
158
+ * Returns a directory's entries with their types; with `readFile`, the admin entries are listed, only the
159
+ * directories counting (see above)
160
+ */
161
+ readDirectory?: ((path: string) => Promise<readonly (readonly [string, number])[]>) | undefined,
162
+ /** The absolute, normalized folders whose worktrees are ignored; none reads no admin entry */
163
+ ignoredWorktreeRoots?: Promise<readonly string[]>,
164
+ /** The folders whose worktrees are ignored, relative to the common git dir */
165
+ ignoredWorktreeFolders?: readonly string[]);
88
166
  [Symbol.dispose](): void;
89
167
  /** Dispose the entire group (common watcher + all root watchers). */
90
168
  dispose(): void;
@@ -100,6 +178,12 @@ export declare class WatchGroup implements UnifiedDisposable {
100
178
  private disposeCommonWatcher;
101
179
  /** Hosts that didn't opt into polling get exactly the options they always had */
102
180
  private getWatcherOptions;
181
+ /**
182
+ * The ignored worktrees' folders join the `.git` excludes of a watcher over the common git dir: a tool that keeps its
183
+ * throwaway checkouts inside the git dir would otherwise have each one's every directory watched, and every file it
184
+ * writes reported. Every other watcher sits on a linked worktree's admin entry, which holds none of them
185
+ */
186
+ private getExcludes;
103
187
  private setRootReliability;
104
188
  private setCommonReliability;
105
189
  private stopPolling;
@@ -119,12 +203,67 @@ export declare class WatchGroup implements UnifiedDisposable {
119
203
  private onCommonEvent;
120
204
  private deliverOverflow;
121
205
  private onRootEvent;
206
+ /** A report of the `worktrees` directory, an admin entry or a file in it, once the group may tell them apart */
207
+ private isWorktreesReport;
208
+ /** `deliver` tells the sessions the report reaches of it, should the report concern them (see above) */
209
+ private onWorktreesReported;
210
+ private decideWorktreesReport;
211
+ /**
212
+ * Reads the entry's `gitdir`: whether it names an ignored worktree (`undefined` when it can't be read), and what the
213
+ * id was known to be when this read replaced it. Reads of one id can settle out of order (a slow one from before the
214
+ * worktree moved out of the folder), so only the latest updates what is known of it; an earlier one settles with
215
+ * what the latest finds, without what the id was, since the latest tells a move itself, or once the entry was removed
216
+ * since, with what the removal found it was. One that can't read the entry forgets what it was known to be, since git
217
+ * may be writing its `gitdir` again, and reads it once more a moment later, unless `retry` is false (it then leaves
218
+ * what is known as it was); one that can settles that read, if pending.
219
+ */
220
+ private readWorktreeId;
221
+ /** Reads the entry once more a moment later, unless that is already pending; `previous` is what it was known to be */
222
+ private scheduleWorktreeRetry;
223
+ /**
224
+ * A report whose read couldn't read the entry waits on the entry's next read, and `deliver` is told then, unless that
225
+ * read finds an ignored worktree's; one still unreadable is told. Both watchers report each change of an entry, so
226
+ * the reports of one id wait on one read. Resolves with what it found, as {@link settleWorktreeRetry} does
227
+ */
228
+ private waitForWorktreeRetry;
229
+ /**
230
+ * Tells the reports waiting on the id's retry what the entry turned out to be: nothing when an ignored worktree's,
231
+ * every session when it moved into or out of an ignored folder, and the reports' own sessions otherwise, `ignored`
232
+ * being `undefined` when it still couldn't be read
233
+ */
234
+ private settleWorktreeRetry;
235
+ /**
236
+ * Takes `id` as removed, remembering what it was for the removal's other reports; returns that, or `undefined` when
237
+ * the id was never known
238
+ */
239
+ private forgetWorktreeId;
240
+ /** Whether the entry's `gitdir` names an ignored worktree, or `undefined` when it can't be read. Never rejects */
241
+ private readIgnoredWorktree;
242
+ private queueWorktreesBaseline;
243
+ /** A watcher of the `worktrees` directory dropped events: whatever changed meanwhile went unseen */
244
+ private rebaselineWorktrees;
245
+ /** The watchers' reports of the directory pile onto one queued listing, which tells them apart */
246
+ private queueWorktreesRescan;
247
+ /**
248
+ * Lists the admin entries and compares them with the last listing. A baseline (or a rebaseline, after a watcher
249
+ * dropped events) reads every entry; a rescan only those it doesn't know, reading one it can't read once more a
250
+ * moment later. Git removes the directory with its last entry, so a directory that isn't there is an empty listing,
251
+ * whatever the reports said of it (a poll sees its removal as a change). A listing that fails otherwise is a
252
+ * baseline's empty directory, and a rescan's too when one of its reports (`delivers`) removed the directory. For any
253
+ * other rescan, it is a change it can't rule out, told to the reports' own sessions
254
+ */
255
+ private listWorktrees;
256
+ private deliverWorktreesChange;
122
257
  private onConfigReported;
123
258
  private readConfigBaseline;
124
259
  /** Reads and compares until no report came in during the last read: it may describe a change the read missed */
125
260
  private readConfigChanges;
126
261
  /** `undefined` when the file couldn't be read or parsed, or the read didn't settle in time */
127
262
  private readConfig;
263
+ /** `undefined` when the file couldn't be read, or the read didn't settle in time */
264
+ private readWithTimeout;
265
+ /** `undefined` when `read` rejects, or doesn't settle in time */
266
+ private withTimeout;
128
267
  /**
129
268
  * Tells every session of a change to the shared `.git/config`: confined to some branches' sections, or (`undefined`,
130
269
  * or a change outside them) anywhere, which may have changed a remote's settings too. Either way no ref moved