@gitkraken/core-gitlens 0.12.0 → 0.13.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 (177) hide show
  1. package/CHANGELOG.md +57 -1
  2. package/dist/git/cache.d.ts +84 -29
  3. package/dist/git/cache.js +188 -54
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/features.d.ts +1 -1
  6. package/dist/git/features.js +4 -0
  7. package/dist/git/features.js.map +1 -1
  8. package/dist/git/models/branch.d.ts +15 -2
  9. package/dist/git/models/branch.js +27 -5
  10. package/dist/git/models/branch.js.map +1 -1
  11. package/dist/git/models/status.js +1 -1
  12. package/dist/git/models/worktree.d.ts +4 -1
  13. package/dist/git/models/worktree.js +4 -2
  14. package/dist/git/models/worktree.js.map +1 -1
  15. package/dist/git/providers/branches.d.ts +73 -8
  16. package/dist/git/providers/pausedOperations.d.ts +6 -0
  17. package/dist/git/providers/provider.d.ts +34 -2
  18. package/dist/git/providers/status.d.ts +8 -2
  19. package/dist/git/remotes/azure-devops.js +61 -53
  20. package/dist/git/remotes/azure-devops.js.map +1 -1
  21. package/dist/git/service.d.ts +8 -5
  22. package/dist/git/service.js +16 -8
  23. package/dist/git/service.js.map +1 -1
  24. package/dist/git/utils/branch.utils.d.ts +8 -1
  25. package/dist/git/utils/branch.utils.js +30 -1
  26. package/dist/git/utils/branch.utils.js.map +1 -1
  27. package/dist/git/watching/changeEvent.d.ts +13 -2
  28. package/dist/git/watching/changeEvent.js +17 -3
  29. package/dist/git/watching/changeEvent.js.map +1 -1
  30. package/dist/git/watching/gitDirPoller.d.ts +111 -0
  31. package/dist/git/watching/gitDirPoller.js +266 -0
  32. package/dist/git/watching/gitDirPoller.js.map +1 -0
  33. package/dist/git/watching/provider.d.ts +16 -0
  34. package/dist/git/watching/watchGroup.d.ts +40 -4
  35. package/dist/git/watching/watchGroup.js +143 -17
  36. package/dist/git/watching/watchGroup.js.map +1 -1
  37. package/dist/git/watching/watchService.d.ts +70 -1
  38. package/dist/git/watching/watchService.js +158 -13
  39. package/dist/git/watching/watchService.js.map +1 -1
  40. package/dist/git/watching/watchSession.d.ts +44 -0
  41. package/dist/git/watching/watchSession.js +91 -6
  42. package/dist/git/watching/watchSession.js.map +1 -1
  43. package/dist/git/watching/watcherPatterns.d.ts +21 -0
  44. package/dist/git/watching/watcherPatterns.js +67 -1
  45. package/dist/git/watching/watcherPatterns.js.map +1 -1
  46. package/dist/git/watching/worktreeSummary.d.ts +34 -7
  47. package/dist/git/watching/worktreeSummary.js +39 -12
  48. package/dist/git/watching/worktreeSummary.js.map +1 -1
  49. package/dist/git-cli/cliGitProvider.d.ts +34 -2
  50. package/dist/git-cli/cliGitProvider.js +119 -17
  51. package/dist/git-cli/cliGitProvider.js.map +1 -1
  52. package/dist/git-cli/exec/commandClassifier.d.ts +15 -0
  53. package/dist/git-cli/exec/commandClassifier.js +48 -2
  54. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  55. package/dist/git-cli/exec/git.d.ts +15 -3
  56. package/dist/git-cli/exec/git.js +38 -17
  57. package/dist/git-cli/exec/git.js.map +1 -1
  58. package/dist/git-cli/exec/gitQueue.d.ts +2 -0
  59. package/dist/git-cli/exec/gitQueue.js +2 -1
  60. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  61. package/dist/git-cli/exec/indexLock.d.ts +38 -0
  62. package/dist/git-cli/exec/indexLock.js +150 -0
  63. package/dist/git-cli/exec/indexLock.js.map +1 -0
  64. package/dist/git-cli/parsers/worktreeParser.js +42 -2
  65. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  66. package/dist/git-cli/providers/branches.d.ts +109 -10
  67. package/dist/git-cli/providers/branches.js +624 -131
  68. package/dist/git-cli/providers/branches.js.map +1 -1
  69. package/dist/git-cli/providers/config.d.ts +9 -5
  70. package/dist/git-cli/providers/config.js +60 -27
  71. package/dist/git-cli/providers/config.js.map +1 -1
  72. package/dist/git-cli/providers/operations.d.ts +17 -0
  73. package/dist/git-cli/providers/operations.js +153 -96
  74. package/dist/git-cli/providers/operations.js.map +1 -1
  75. package/dist/git-cli/providers/pausedOperations.d.ts +8 -0
  76. package/dist/git-cli/providers/pausedOperations.js +60 -40
  77. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  78. package/dist/git-cli/providers/refs.js +11 -8
  79. package/dist/git-cli/providers/refs.js.map +1 -1
  80. package/dist/git-cli/providers/remotes.js +19 -10
  81. package/dist/git-cli/providers/remotes.js.map +1 -1
  82. package/dist/git-cli/providers/staging.js +40 -15
  83. package/dist/git-cli/providers/staging.js.map +1 -1
  84. package/dist/git-cli/providers/stash.d.ts +2 -0
  85. package/dist/git-cli/providers/stash.js +33 -18
  86. package/dist/git-cli/providers/stash.js.map +1 -1
  87. package/dist/git-cli/providers/status.d.ts +3 -1
  88. package/dist/git-cli/providers/status.js +38 -20
  89. package/dist/git-cli/providers/status.js.map +1 -1
  90. package/dist/git-cli/providers/tags.js +6 -4
  91. package/dist/git-cli/providers/tags.js.map +1 -1
  92. package/dist/git-cli/providers/worktrees.d.ts +1 -0
  93. package/dist/git-cli/providers/worktrees.js +60 -31
  94. package/dist/git-cli/providers/worktrees.js.map +1 -1
  95. package/dist/git-cli/service.d.ts +6 -0
  96. package/dist/git-cli/service.js +5 -2
  97. package/dist/git-cli/service.js.map +1 -1
  98. package/dist/ipc/discovery.js +1 -1
  99. package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
  100. package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
  101. package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
  102. package/dist/plus/git-github/providers/github/status.d.ts +3 -1
  103. package/dist/plus/git-github/providers/github/status.js +1 -1
  104. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  105. package/dist/plus/integrations/collectionMetadata.d.ts +13 -2
  106. package/dist/plus/integrations/collectionMetadata.js +14 -2
  107. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  108. package/dist/plus/integrations/index.d.ts +2 -2
  109. package/dist/plus/integrations/index.js.map +1 -1
  110. package/dist/plus/integrations/integrationService.d.ts +1 -0
  111. package/dist/plus/integrations/integrationService.js +1 -1
  112. package/dist/plus/integrations/integrationService.js.map +1 -1
  113. package/dist/plus/integrations/manager.d.ts +1 -0
  114. package/dist/plus/integrations/models/gitHostIntegration.d.ts +41 -5
  115. package/dist/plus/integrations/models/gitHostIntegration.js +106 -29
  116. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  117. package/dist/plus/integrations/models/issueReads.d.ts +17 -4
  118. package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
  119. package/dist/plus/integrations/providers/apiClients.js +7 -1
  120. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  121. package/dist/plus/integrations/providers/azure/azure.d.ts +5 -4
  122. package/dist/plus/integrations/providers/azure/azure.js +37 -77
  123. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  124. package/dist/plus/integrations/providers/azure/azure.utils.js +20 -17
  125. package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
  126. package/dist/plus/integrations/providers/azure/models.d.ts +2 -2
  127. package/dist/plus/integrations/providers/azure/models.js +4 -4
  128. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  129. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +18 -14
  130. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +69 -17
  131. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  132. package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
  133. package/dist/plus/integrations/providers/azureDevOps.js +108 -17
  134. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  135. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +45 -1
  136. package/dist/plus/integrations/providers/azureDevOpsServer.js +121 -6
  137. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  138. package/dist/plus/integrations/providers/jira-server.js +18 -14
  139. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  140. package/dist/plus/integrations/providers/jira.js +18 -14
  141. package/dist/plus/integrations/providers/jira.js.map +1 -1
  142. package/dist/plus/integrations/providers/models.d.ts +1 -2
  143. package/dist/plus/integrations/providers/models.js +9 -2
  144. package/dist/plus/integrations/providers/models.js.map +1 -1
  145. package/dist/plus/integrations/providers/providersApi.d.ts +7 -17
  146. package/dist/plus/integrations/providers/providersApi.js +85 -78
  147. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  148. package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +22 -0
  149. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +69 -9
  150. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
  151. package/dist/plus/integrations/providers/utils/providerPaging.js +8 -2
  152. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  153. package/dist/plus/integrations/reads/filters.d.ts +3 -3
  154. package/dist/plus/integrations/reads/filters.js.map +1 -1
  155. package/dist/plus/integrations/reads/issueBatch.d.ts +2 -1
  156. package/dist/plus/integrations/reads/issueBatch.js +1 -1
  157. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  158. package/dist/plus/integrations/reads/issueTracker.js +23 -14
  159. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  160. package/dist/plus/integrations/reads/issues.d.ts +2 -1
  161. package/dist/plus/integrations/reads/issues.js +18 -3
  162. package/dist/plus/integrations/reads/issues.js.map +1 -1
  163. package/dist/plus/integrations/reads/pullRequestBatch.js +1 -1
  164. package/dist/plus/integrations/reads/resolveRepository.js +12 -3
  165. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  166. package/dist/plus/integrations/reads/warnings.d.ts +11 -4
  167. package/dist/plus/integrations/reads/warnings.js +15 -5
  168. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  169. package/dist/plus/integrations/results.d.ts +36 -2
  170. package/dist/plus/integrations/results.js +33 -13
  171. package/dist/plus/integrations/results.js.map +1 -1
  172. package/dist/utils/env/node/exec.js +2 -2
  173. package/dist/utils/promiseCache.d.ts +10 -0
  174. package/dist/utils/promiseCache.js +17 -2
  175. package/dist/utils/promiseCache.js.map +1 -1
  176. package/docs/integrations.md +52 -20
  177. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"cliGitProvider.js","names":["fs"],"sources":["../../../git-cli/src/cliGitProvider.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport * as path from 'node:path';\nimport { env } from 'node:process';\nimport type { CachedGitTypes, MergeTargetsChangeEvent } from '@gitlens/git/cache.js';\nimport { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport type { GitFileStatus } from '@gitlens/git/models/fileStatus.js';\nimport type { RepositoryChange } from '@gitlens/git/models/repository.js';\nimport { deletedOrMissing } from '@gitlens/git/models/revision.js';\nimport type { GitOperationRunOptions } from '@gitlens/git/providers/operations.js';\nimport type { GitProvider, GitProviderDescriptor } from '@gitlens/git/providers/provider.js';\nimport { parseGitRemoteUrl } from '@gitlens/git/utils/remote.utils.js';\nimport { isUncommitted } from '@gitlens/git/utils/revision.utils.js';\nimport { GitIgnoreFilter } from '@gitlens/git/watching/gitIgnoreFilter.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { joinPaths, maybeUri, normalizePath, splitPath } from '@gitlens/utils/path.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { fileUri, joinUriPath, parseUri, toFsPath } from '@gitlens/utils/uri.js';\nimport { fsExists } from './exec/exec.js';\nimport type { FilteredGitFeatures, GitFeatureOrPrefix, GitFeatures } from './exec/features.js';\nimport type { GitFeatureUnsupportedMessage, GitOptions } from './exec/git.js';\nimport { Git } from './exec/git.js';\nimport type { GitLocation } from './exec/locator.js';\nimport { BlameGitSubProvider } from './providers/blame.js';\nimport { BranchesGitSubProvider } from './providers/branches.js';\nimport { CommitsGitSubProvider } from './providers/commits.js';\nimport { ConfigGitSubProvider, isGitBooleanFalse, parseGitBoolean } from './providers/config.js';\nimport { ContributorsGitSubProvider } from './providers/contributors.js';\nimport { DiffGitSubProvider } from './providers/diff.js';\nimport { GraphGitSubProvider } from './providers/graph.js';\nimport { MaintenanceGitSubProvider } from './providers/maintenance.js';\nimport { OperationsGitSubProvider } from './providers/operations.js';\nimport { PatchGitSubProvider } from './providers/patch.js';\nimport { PausedOperationsGitSubProvider } from './providers/pausedOperations.js';\nimport { RefsGitSubProvider } from './providers/refs.js';\nimport { RemotesGitSubProvider } from './providers/remotes.js';\nimport { RevisionGitSubProvider } from './providers/revision.js';\nimport { StagingGitSubProvider } from './providers/staging.js';\nimport { StashGitSubProvider } from './providers/stash.js';\nimport { StatusGitSubProvider } from './providers/status.js';\nimport { TagsGitSubProvider } from './providers/tags.js';\nimport { WorktreesGitSubProvider } from './providers/worktrees.js';\n\nexport interface CliGitProviderOptions {\n\t/** Use an existing Cache instance, or one will be created */\n\tcache?: Cache;\n\t/** Context providing config and event bus hooks */\n\tcontext: GitServiceContext;\n\t/** Function that resolves the git binary location (path + version) */\n\tlocator: () => Promise<GitLocation>;\n\t/** Pre-built Git executor — when provided, used instead of creating a new Git from locator/options */\n\tgit?: Git;\n\t/** Git execution options (timeout, trust, queue config, hooks, etc.) */\n\tgitOptions?: GitOptions;\n\t/**\n\t * Whether repositories reached through this provider are on the LOCAL filesystem. `false` disables the\n\t * maintenance sub-provider entirely (Live Share): its probes stat local paths, and its writes are\n\t * rejected by the host's command allowlist, so inheriting it yields empty reports and swallowed\n\t * failures rather than an honest \"unsupported here\". Defaults to `true`.\n\t */\n\tlocalRepositories?: boolean;\n}\n\n/**\n * Exposes `isTrackedWithDetails` as a callable method for sub-providers.\n * The class declares it as a regular method, but TypeScript's structural\n * typing makes it inaccessible through the class type alone when consumed\n * internally — this type re-maps it to ensure sub-providers can call it.\n */\nexport type CliGitProviderInternal = Omit<CliGitProvider, 'isTrackedWithDetails'> & {\n\tisTrackedWithDetails: CliGitProvider['isTrackedWithDetails'];\n};\n\n/**\n * A fully-wired CLI-based git provider that executes git commands via `child_process`.\n * Implements both `CliGitProviderInternal` (for internal sub-provider wiring)\n * and `GitProvider` (for registration in a provider registry).\n *\n * @example\n * ```typescript\n * const provider = new CliGitProvider({\n * context: { events: myEvents },\n * locator: () => findGitPath(null),\n * gitOptions: { gitTimeout: 30000 },\n * });\n *\n * const branches = await provider.branches.getBranches(repoPath);\n * const status = await provider.status.getStatus(repoPath);\n * ```\n */\nexport class CliGitProvider implements GitProvider {\n\treadonly descriptor: GitProviderDescriptor = { id: 'git', name: 'Git', virtual: false };\n\n\tprivate readonly _cache: Cache;\n\tprivate readonly _cacheOwned: boolean;\n\tprivate readonly _git: Git;\n\n\treadonly context: GitServiceContext;\n\t/** The host's own hooks, before the provider wraps `cache.onReset` with its own clearing */\n\tprivate readonly _hostHooks: GitServiceContext['hooks'];\n\tprivate readonly _localRepositories: boolean;\n\n\tconstructor(options: CliGitProviderOptions) {\n\t\t// Every typed write announces itself through `onReset`, so the provider clears its own state there\n\t\t// rather than trusting each host to: a host with no handler still reads fresh after a write. A write\n\t\t// hard-evicts, so a caller after it never joins a read that started before it; watcher-driven clears\n\t\t// reach `Cache.clearCaches` directly and keep sharing in-flight reads.\n\t\tconst hostHooks = options.context.hooks;\n\t\tthis._hostHooks = hostHooks;\n\t\tthis.context = {\n\t\t\t...options.context,\n\t\t\thooks: {\n\t\t\t\t...hostHooks,\n\t\t\t\tcache: {\n\t\t\t\t\t...hostHooks?.cache,\n\t\t\t\t\tonReset: (repoPath, ...types) => {\n\t\t\t\t\t\tthis._cache.evictCaches(repoPath, ...types);\n\t\t\t\t\t\tthis._git.clearPendingCommands(this.getRepositoryPaths(repoPath));\n\t\t\t\t\t\thostHooks?.cache?.onReset?.(repoPath, ...types);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tthis._localRepositories = options.localRepositories ?? true;\n\t\tthis._git =\n\t\t\toptions.git ??\n\t\t\tnew Git(options.locator, {\n\t\t\t\t...options.gitOptions,\n\t\t\t\tisTrusted:\n\t\t\t\t\toptions.context.workspace?.isTrusted != null\n\t\t\t\t\t\t? () => options.context.workspace!.isTrusted!\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\tthis._cacheOwned = !(options.cache instanceof Cache);\n\t\tthis._cache = options.cache instanceof Cache ? options.cache : new Cache();\n\n\t\t// Wires a raw `git.run({ notify })` call back to this provider's own cache/host announcement —\n\t\t// unconditionally, so a host-supplied `options.git` gets bound exactly like one we construct here.\n\t\tthis._git.bindChangeNotifier((repoPaths, changes) => this.notifyChangedCore(repoPaths, changes));\n\t}\n\n\t/** The underlying cache instance (for repo path registration, etc.) */\n\tget cache(): Cache {\n\t\treturn this._cache;\n\t}\n\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._cache.onDidChangeMergeTargets;\n\t}\n\n\t/** The underlying git execution instance (for sub-providers that need direct exec/stream access) */\n\tget git(): Git {\n\t\treturn this._git;\n\t}\n\n\t/** Returns whether the installed git version supports the given feature. */\n\tsupports(feature: GitFeatures): boolean | Promise<boolean> {\n\t\treturn this._git.supports(feature);\n\t}\n\n\t/** Returns all features matching the prefix that the installed git version supports. */\n\tsupported<T extends GitFeatureOrPrefix>(feature: T): FilteredGitFeatures<T>[] | Promise<FilteredGitFeatures<T>[]> {\n\t\treturn this._git.supported(feature);\n\t}\n\n\t/** Throws if the installed git version does not support the given feature. */\n\tasync ensureSupports(feature: GitFeatures, prefix: string, suffix: string): Promise<void>;\n\tasync ensureSupports(feature: GitFeatures, message: GitFeatureUnsupportedMessage): Promise<void>;\n\tasync ensureSupports(\n\t\tfeature: GitFeatures,\n\t\tprefixOrMessage: string | GitFeatureUnsupportedMessage,\n\t\tsuffix?: string,\n\t): Promise<void> {\n\t\treturn typeof prefixOrMessage === 'function'\n\t\t\t? this._git.ensureSupports(feature, prefixOrMessage)\n\t\t\t: this._git.ensureSupports(feature, prefixOrMessage, suffix!);\n\t}\n\n\t/** Returns the installed git version string. */\n\tasync version(): Promise<string> {\n\t\treturn this._git.version();\n\t}\n\n\t/** Returns the resolved git binary path. */\n\tasync path(): Promise<string> {\n\t\treturn this._git.path();\n\t}\n\n\tasync clone(\n\t\turl: string,\n\t\tparentPath: string,\n\t\toptions?: { folderName?: string },\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<string | undefined> {\n\t\tlet folderPath: string;\n\t\tif (options?.folderName) {\n\t\t\tfolderPath = joinPaths(parentPath, options.folderName);\n\t\t} else {\n\t\t\tconst [, , remotePath] = parseGitRemoteUrl(url);\n\t\t\tconst remoteName = remotePath.split('/').pop();\n\t\t\tif (!remoteName) return undefined;\n\n\t\t\tlet count = 0;\n\t\t\tfolderPath = joinPaths(parentPath, remoteName);\n\t\t\twhile ((await fsExists(folderPath)) && count < 20) {\n\t\t\t\tcount++;\n\t\t\t\tfolderPath = joinPaths(parentPath, `${remoteName}-${count}`);\n\t\t\t}\n\t\t}\n\n\t\t// `throw`: the default handler swallows `remoteConnectionError`, which would return a folder never cloned\n\t\tawait this._git.run({ cwd: parentPath, errors: 'throw', ...runOptions }, 'clone', url, folderPath);\n\n\t\treturn folderPath;\n\t}\n\n\t/** Creates a new repository at `targetPath` (`git init`), creating it first if it doesn't yet exist. */\n\tasync init(targetPath: string, options?: { defaultBranch?: string; bare?: boolean }): Promise<void> {\n\t\tawait fs.mkdir(targetPath, { recursive: true });\n\n\t\tconst args = ['init'];\n\t\tif (options?.defaultBranch) {\n\t\t\targs.push('-b', options.defaultBranch);\n\t\t}\n\t\tif (options?.bare) {\n\t\t\targs.push('--bare');\n\t\t}\n\n\t\t// Run inside the target rather than naming it from its parent: a relative `targetPath` would otherwise\n\t\t// be resolved against the parent a second time\n\t\tawait this._git.run({ cwd: targetPath }, ...args);\n\t}\n\n\t/**\n\t * Notifies the provider that `repoPath` was mutated outside of a typed sub-provider method — e.g. a\n\t * consumer that ran a command through the raw `git.run`/`provider.git.run` escape hatch. Fires the same\n\t * `cache.onReset`/`repository.onChanged` hooks a typed mutator would, so, exactly as after a typed write,\n\t * the provider hard-evicts its own caches for `repoPath` and drops its pending commands for the repository\n\t * (its sibling worktrees included) before the host's handlers run.\n\t *\n\t * What is reset: `options.cache` when given (`'all'` for everything, `[]` for nothing); otherwise the\n\t * cache types `changes` map to, the same mapping a file watcher's changes get, status clock included.\n\t * An empty `changes`, or one naming `'unknown'` or `'closed'`, resets everything. Changes that map to\n\t * no cache type (e.g. `'starred'`) reset nothing, and `onReset` doesn't fire; `onChanged` always fires.\n\t */\n\tnotifyChanged(\n\t\trepoPath: string,\n\t\tchanges: readonly RepositoryChange[],\n\t\toptions?: { cache?: readonly CachedGitTypes[] | 'all' },\n\t): void {\n\t\tthis.notifyChangedCore([repoPath], changes, options);\n\t}\n\n\t/**\n\t * {@link notifyChanged} for one change seen from several paths, e.g. a `-C` shared-state verb announced\n\t * for both its target and the caller's `cwd`. Each path's caches are reset and the host's `onReset` fires\n\t * for each, since a host may keep per-worktree state, but a repository's pending commands are dropped once\n\t * however many of its worktrees are named.\n\t */\n\tprivate notifyChangedCore(\n\t\trepoPaths: readonly string[],\n\t\tchanges: readonly RepositoryChange[],\n\t\toptions?: { cache?: readonly CachedGitTypes[] | 'all' },\n\t): void {\n\t\tconst resets: { repoPath: string; types: CachedGitTypes[] }[] = [];\n\t\tconst pendingPaths = new Set<string>();\n\t\tfor (const repoPath of repoPaths) {\n\t\t\tconst types = this.resetForChanges(repoPath, changes, options?.cache);\n\t\t\tif (types == null) {\n\t\t\t\t// A gk config change is reconciled outside the cache types, but its re-read must not join a pre-write run\n\t\t\t\tif (options?.cache == null && changes.includes('gkConfig')) {\n\t\t\t\t\tfor (const path of this.getRepositoryPaths(repoPath)) {\n\t\t\t\t\t\tpendingPaths.add(path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tresets.push({ repoPath: repoPath, types: types });\n\t\t\tfor (const path of this.getRepositoryPaths(repoPath)) {\n\t\t\t\tpendingPaths.add(path);\n\t\t\t}\n\t\t}\n\n\t\tif (pendingPaths.size) {\n\t\t\tthis._git.clearPendingCommands([...pendingPaths]);\n\t\t}\n\t\tfor (const { repoPath, types } of resets) {\n\t\t\tthis._hostHooks?.cache?.onReset?.(repoPath, ...types);\n\t\t}\n\t\tfor (const repoPath of repoPaths) {\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, [...changes]);\n\t\t}\n\t}\n\n\t/** Evicts what a notified change reset for `repoPath`: the types reset (`[]` = all), or `undefined` for none. */\n\tprivate resetForChanges(\n\t\trepoPath: string,\n\t\tchanges: readonly RepositoryChange[],\n\t\tcache: readonly CachedGitTypes[] | 'all' | undefined,\n\t): CachedGitTypes[] | undefined {\n\t\tif (cache != null && cache !== 'all') {\n\t\t\tif (!cache.length) return undefined;\n\n\t\t\tthis._cache.evictCaches(repoPath, ...cache);\n\t\t\treturn [...cache];\n\t\t}\n\n\t\tif (cache === 'all' || !changes.length || changes.some(c => c === 'unknown' || c === 'closed')) {\n\t\t\tthis._cache.evictCaches(repoPath);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst types = this._cache.applyRepositoryChanges(repoPath, changes, 'evict');\n\t\treturn types.length ? types : undefined;\n\t}\n\n\t/** `repoPath` and every worktree registered as sharing its repository, since a shared-ref write moves them all. */\n\tprivate getRepositoryPaths(repoPath: string): string[] {\n\t\tconst commonPath = this._cache.getCommonPath(repoPath);\n\t\treturn [...new Set([repoPath, commonPath, ...this._cache.getWorktreePaths(commonPath)])];\n\t}\n\n\tprivate _blame: BlameGitSubProvider | undefined;\n\tget blame(): BlameGitSubProvider {\n\t\treturn (this._blame ??= new BlameGitSubProvider(\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _branches: BranchesGitSubProvider | undefined;\n\tget branches(): BranchesGitSubProvider {\n\t\treturn (this._branches ??= new BranchesGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _commits: CommitsGitSubProvider | undefined;\n\tget commits(): CommitsGitSubProvider {\n\t\treturn (this._commits ??= new CommitsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _config: ConfigGitSubProvider | undefined;\n\tget config(): ConfigGitSubProvider {\n\t\treturn (this._config ??= new ConfigGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _contributors: ContributorsGitSubProvider | undefined;\n\tget contributors(): ContributorsGitSubProvider {\n\t\treturn (this._contributors ??= new ContributorsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _diff: DiffGitSubProvider | undefined;\n\tget diff(): DiffGitSubProvider {\n\t\treturn (this._diff ??= new DiffGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _graph: GraphGitSubProvider | undefined;\n\tget graph(): GraphGitSubProvider {\n\t\treturn (this._graph ??= new GraphGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _maintenance: MaintenanceGitSubProvider | undefined;\n\t// Optional so subclasses whose repos aren't local can report the capability as absent — see\n\t// `VslsGitProvider`. Consumers already treat `maintenance == null` as \"unsupported here\" (web builds\n\t// never register it at all).\n\tget maintenance(): MaintenanceGitSubProvider | undefined {\n\t\tif (!this._localRepositories) return undefined;\n\n\t\treturn (this._maintenance ??= new MaintenanceGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _ops: OperationsGitSubProvider | undefined;\n\tget ops(): OperationsGitSubProvider {\n\t\treturn (this._ops ??= new OperationsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _patch: PatchGitSubProvider | undefined;\n\tget patch(): PatchGitSubProvider {\n\t\treturn (this._patch ??= new PatchGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _pausedOps: PausedOperationsGitSubProvider | undefined;\n\tget pausedOps(): PausedOperationsGitSubProvider {\n\t\treturn (this._pausedOps ??= new PausedOperationsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _refs: RefsGitSubProvider | undefined;\n\tget refs(): RefsGitSubProvider {\n\t\treturn (this._refs ??= new RefsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _remotes: RemotesGitSubProvider | undefined;\n\tget remotes(): RemotesGitSubProvider {\n\t\treturn (this._remotes ??= new RemotesGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _revision: RevisionGitSubProvider | undefined;\n\tget revision(): RevisionGitSubProvider {\n\t\treturn (this._revision ??= new RevisionGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _staging: StagingGitSubProvider | undefined;\n\tget staging(): StagingGitSubProvider {\n\t\treturn (this._staging ??= new StagingGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _stash: StashGitSubProvider | undefined;\n\tget stash(): StashGitSubProvider {\n\t\treturn (this._stash ??= new StashGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _status: StatusGitSubProvider | undefined;\n\tget status(): StatusGitSubProvider {\n\t\treturn (this._status ??= new StatusGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _tags: TagsGitSubProvider | undefined;\n\tget tags(): TagsGitSubProvider {\n\t\treturn (this._tags ??= new TagsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _worktrees: WorktreesGitSubProvider | undefined;\n\tget worktrees(): WorktreesGitSubProvider {\n\t\treturn (this._worktrees ??= new WorktreesGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tgetAbsoluteUri(pathOrUri: string | Uri, base: string | Uri): Uri {\n\t\tconst baseUri = typeof base !== 'string' ? base : fileUri(base);\n\t\tif (typeof pathOrUri !== 'string') return pathOrUri;\n\t\tif (maybeUri(pathOrUri)) return parseUri(pathOrUri);\n\t\tif (path.isAbsolute(pathOrUri)) return fileUri(pathOrUri);\n\t\treturn joinUriPath(baseUri, pathOrUri);\n\t}\n\n\tgetRelativePath(pathOrUri: string | Uri, base: string | Uri): string {\n\t\tconst [relativePath] = splitPath(toFsPath(pathOrUri), toFsPath(base));\n\t\treturn relativePath;\n\t}\n\n\tasync excludeIgnoredUris(repoPath: string, uris: Uri[]): Promise<Uri[]> {\n\t\tconst filter = this.getIgnoreFilter(repoPath);\n\t\tawait filter.ready();\n\t\treturn uris.filter(uri => {\n\t\t\tconst relativePath = normalizePath(path.relative(repoPath, toFsPath(uri)));\n\t\t\treturn !relativePath || !filter.isIgnored(relativePath);\n\t\t});\n\t}\n\n\tasync getIgnoredUrisFilter(repoPath: string): Promise<(uri: Uri) => boolean> {\n\t\tconst filter = this.getIgnoreFilter(repoPath);\n\t\tawait filter.ready();\n\t\treturn (uri: Uri) => {\n\t\t\tconst relativePath = normalizePath(path.relative(repoPath, toFsPath(uri)));\n\t\t\treturn relativePath ? filter.isIgnored(relativePath) : false;\n\t\t};\n\t}\n\n\tasync getLastFetchedTimestamp(repoPath: string): Promise<number | undefined> {\n\t\treturn this._cache.getLastFetchedTimestamp(repoPath, async (commonPath): Promise<number | undefined> => {\n\t\t\tconst gitDir = await this.config.getGitDir?.(commonPath);\n\t\t\tif (gitDir == null) return undefined;\n\n\t\t\tconst gitDirUri = gitDir.commonUri ?? gitDir.uri;\n\t\t\tconst fetchHeadUri = joinUriPath(gitDirUri, 'FETCH_HEAD');\n\t\t\tconst stats = await this.context.fs.stat(fetchHeadUri);\n\t\t\t// If the file is empty, assume the fetch failed, and don't update the timestamp\n\t\t\tif (stats != null && stats.size > 0) return stats.mtime;\n\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\tgetIgnoreFilter(repoPath: string): GitIgnoreFilter {\n\t\tlet filter = this._cache.gitIgnore.get(repoPath);\n\t\tif (filter == null) {\n\t\t\tconst gitDir = this._cache.gitDir.get(repoPath);\n\t\t\tconst gitDirPath = gitDir?.uri.fsPath ?? `${repoPath}/.git`;\n\t\t\tfilter = new GitIgnoreFilter({\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tgitDirPath: gitDirPath,\n\t\t\t\tfs: this.context.fs,\n\t\t\t\t// Resolved the way git resolves it; unset, git reads its default location instead\n\t\t\t\tgetGlobalExcludesPath:\n\t\t\t\t\tthis.config.getConfig != null\n\t\t\t\t\t\t? async () => {\n\t\t\t\t\t\t\t\tconst configured = await this.config.getConfig(repoPath, 'core.excludesFile');\n\t\t\t\t\t\t\t\treturn configured != null\n\t\t\t\t\t\t\t\t\t? resolveGitPathConfig(configured, repoPath)\n\t\t\t\t\t\t\t\t\t: getDefaultGlobalExcludesPath();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// `info/exclude` lives in the COMMON git dir — a linked worktree's own git dir has none\n\t\t\t\tgetInfoExcludePath: async () => {\n\t\t\t\t\tconst gitDir = await this.config.getGitDir?.(repoPath);\n\t\t\t\t\tif (gitDir == null) return undefined;\n\n\t\t\t\t\treturn `${(gitDir.commonUri ?? gitDir.uri).fsPath}/info/exclude`;\n\t\t\t\t},\n\t\t\t\tgetGitIgnorePaths: () => this.getGitIgnorePaths(repoPath),\n\t\t\t\tgetIgnoreCase: async () => {\n\t\t\t\t\tconst value = await this.config.getConfig?.(repoPath, 'core.ignorecase');\n\t\t\t\t\tif (value == null) return undefined;\n\t\t\t\t\tif (parseGitBoolean(value)) return true;\n\n\t\t\t\t\treturn isGitBooleanFalse(value) ? false : undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t\tthis._cache.gitIgnore.set(repoPath, filter);\n\t\t}\n\t\treturn filter;\n\t}\n\n\tasync getIgnoredDirectories(repoPath: string, cancellation?: AbortSignal): Promise<string[]> {\n\t\t// `--directory` collapses a directory to one entry when everything in it is ignored — which includes\n\t\t// directories no rule matches whose current contents merely happen to be ignored (a `logs/` holding only\n\t\t// `*.log` files under a `*.log` rule), so each candidate is confirmed against the rules below\n\t\tconst listed = await this._git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw', cancellation: cancellation },\n\t\t\t'ls-files',\n\t\t\t'-z',\n\t\t\t'--others',\n\t\t\t'--ignored',\n\t\t\t'--exclude-standard',\n\t\t\t'--directory',\n\t\t);\n\n\t\tconst candidates: string[] = [];\n\t\tfor (const entry of iterateByDelimiter(listed.stdout, '\\0')) {\n\t\t\tif (entry.length > 1 && entry.endsWith('/')) {\n\t\t\t\tcandidates.push(entry);\n\t\t\t}\n\t\t}\n\t\tif (!candidates.length) return [];\n\n\t\t// Exits 1 when nothing matches, which is an answer\n\t\tconst confirmed = await this._git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\terrors: 'throw',\n\t\t\t\tcancellation: cancellation,\n\t\t\t\tstdin: `${candidates.join('\\0')}\\0`,\n\t\t\t\texpectedExitCodes: [1],\n\t\t\t},\n\t\t\t'check-ignore',\n\t\t\t'-z',\n\t\t\t'--stdin',\n\t\t);\n\n\t\tconst directories: string[] = [];\n\t\tfor (const entry of iterateByDelimiter(confirmed.stdout, '\\0')) {\n\t\t\t// Never `.git` itself, and no name a watcher would read as a pattern\n\t\t\tif (entry.length < 2 || !entry.endsWith('/') || /[*?[\\]{}()!+@\\\\]/.test(entry)) continue;\n\n\t\t\tconst directory = entry.substring(0, entry.length - 1);\n\t\t\tif (directory === '.git') continue;\n\n\t\t\tdirectories.push(directory);\n\t\t}\n\n\t\t// A path sorts after every prefix of it, so an entry's ancestors have already been kept (or not) by the time it\n\t\t// is checked — and the kept set comes out sorted\n\t\tdirectories.sort();\n\n\t\tconst kept = new Set<string>();\n\t\tfor (const directory of directories) {\n\t\t\tlet beneathKept = false;\n\t\t\tfor (let i = directory.indexOf('/'); i !== -1; i = directory.indexOf('/', i + 1)) {\n\t\t\t\tif (kept.has(directory.substring(0, i))) {\n\t\t\t\t\tbeneathKept = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!beneathKept) {\n\t\t\t\tkept.add(directory);\n\t\t\t}\n\t\t}\n\n\t\treturn [...kept];\n\t}\n\n\t/** Every `.gitignore` git reads in the working tree (tracked, or untracked and not itself ignored), root included */\n\tprivate async getGitIgnorePaths(repoPath: string): Promise<string[]> {\n\t\tconst result = await this._git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw' },\n\t\t\t'ls-files',\n\t\t\t'-z',\n\t\t\t'--cached',\n\t\t\t'--others',\n\t\t\t'--exclude-standard',\n\t\t\t'--',\n\t\t\t':(glob)**/.gitignore',\n\t\t);\n\n\t\tconst paths: string[] = [];\n\t\tfor (const line of iterateByDelimiter(result.stdout, '\\0')) {\n\t\t\tif (!line.length) continue;\n\n\t\t\tpaths.push(line);\n\t\t}\n\t\treturn paths;\n\t}\n\n\tprivate async isTrackedWithDetails(\n\t\tfilePath: string,\n\t\trepoPath: string,\n\t\trev?: string,\n\t): Promise<{ path: string; status?: GitFileStatus; originalPath?: string } | undefined> {\n\t\tif (rev === deletedOrMissing) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst [relativePath, root] = splitPath(filePath, repoPath);\n\n\t\t\t// Check if the file exists in the working tree first (better cache reuse)\n\t\t\tlet tracked = await this.revision.exists(root, relativePath);\n\t\t\tif (tracked) return { path: relativePath };\n\n\t\t\t// If a specific revision was provided, check there too\n\t\t\tif (rev && !isUncommitted(rev)) {\n\t\t\t\ttracked = await this.revision.exists(root, relativePath, rev);\n\t\t\t\tif (!tracked) {\n\t\t\t\t\t// Check the parent revision (file might have been deleted in this rev)\n\t\t\t\t\ttracked = await this.revision.exists(root, relativePath, `${rev}^`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn tracked ? { path: relativePath } : undefined;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._git.dispose();\n\t\tif (this._cacheOwned) {\n\t\t\tthis._cache.dispose();\n\t\t}\n\t}\n}\n\n/**\n * Resolves a pathname config value the way git does: a leading `~/` (or a bare `~`) is the home directory, and a\n * relative path is taken from the repository root. Done here rather than with `git config --type=path`, which needs\n * git 2.18, newer than the minimum supported version.\n */\nfunction resolveGitPathConfig(value: string, repoPath: string): string {\n\tif (value === '~' || value.startsWith('~/')) {\n\t\treturn path.join(env.HOME || homedir(), value.substring(1));\n\t}\n\n\treturn path.resolve(repoPath, value);\n}\n\n/** Where git reads global excludes when `core.excludesFile` is unset: `$XDG_CONFIG_HOME/git/ignore`, else `$HOME/.config/git/ignore` */\nfunction getDefaultGlobalExcludesPath(): string | undefined {\n\tconst xdgConfigHome = env.XDG_CONFIG_HOME;\n\tif (xdgConfigHome) return path.join(xdgConfigHome, 'git', 'ignore');\n\n\tconst home = env.HOME || homedir();\n\treturn home ? path.join(home, '.config', 'git', 'ignore') : undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cliGitProvider.js","names":["fs"],"sources":["../../../git-cli/src/cliGitProvider.ts"],"sourcesContent":["import { promises as fs } from 'node:fs';\nimport { homedir } from 'node:os';\nimport * as path from 'node:path';\nimport { env } from 'node:process';\nimport type { CachedGitTypes, MergeTargetsChangeEvent } from '@gitlens/git/cache.js';\nimport { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport type { GitFileStatus } from '@gitlens/git/models/fileStatus.js';\nimport type { RepositoryChange } from '@gitlens/git/models/repository.js';\nimport { deletedOrMissing } from '@gitlens/git/models/revision.js';\nimport type { GitOperationRunOptions } from '@gitlens/git/providers/operations.js';\nimport type {\n\tAnnouncedRepositoryChanges,\n\tGitProvider,\n\tGitProviderDescriptor,\n} from '@gitlens/git/providers/provider.js';\nimport { parseGitRemoteUrl } from '@gitlens/git/utils/remote.utils.js';\nimport { isUncommitted } from '@gitlens/git/utils/revision.utils.js';\nimport { GitIgnoreFilter } from '@gitlens/git/watching/gitIgnoreFilter.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { joinPaths, maybeUri, normalizePath, splitPath } from '@gitlens/utils/path.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { fileUri, joinUriPath, parseUri, toFsPath } from '@gitlens/utils/uri.js';\nimport { worktreeSharedChanges } from './exec/commandClassifier.js';\nimport { fsExists } from './exec/exec.js';\nimport type { FilteredGitFeatures, GitFeatureOrPrefix, GitFeatures } from './exec/features.js';\nimport type { GitFeatureUnsupportedMessage, GitOptions } from './exec/git.js';\nimport { Git } from './exec/git.js';\nimport type { GitLocation } from './exec/locator.js';\nimport { BlameGitSubProvider } from './providers/blame.js';\nimport { BranchesGitSubProvider } from './providers/branches.js';\nimport { CommitsGitSubProvider } from './providers/commits.js';\nimport { ConfigGitSubProvider, isGitBooleanFalse, parseGitBoolean } from './providers/config.js';\nimport { ContributorsGitSubProvider } from './providers/contributors.js';\nimport { DiffGitSubProvider } from './providers/diff.js';\nimport { GraphGitSubProvider } from './providers/graph.js';\nimport { MaintenanceGitSubProvider } from './providers/maintenance.js';\nimport { OperationsGitSubProvider } from './providers/operations.js';\nimport { PatchGitSubProvider } from './providers/patch.js';\nimport { PausedOperationsGitSubProvider } from './providers/pausedOperations.js';\nimport { RefsGitSubProvider } from './providers/refs.js';\nimport { RemotesGitSubProvider } from './providers/remotes.js';\nimport { RevisionGitSubProvider } from './providers/revision.js';\nimport { StagingGitSubProvider } from './providers/staging.js';\nimport { StashGitSubProvider } from './providers/stash.js';\nimport { StatusGitSubProvider } from './providers/status.js';\nimport { TagsGitSubProvider } from './providers/tags.js';\nimport { WorktreesGitSubProvider } from './providers/worktrees.js';\n\nexport interface CliGitProviderOptions {\n\t/** Use an existing Cache instance, or one will be created */\n\tcache?: Cache;\n\t/** Context providing config and event bus hooks */\n\tcontext: GitServiceContext;\n\t/** Function that resolves the git binary location (path + version) */\n\tlocator: () => Promise<GitLocation>;\n\t/** Pre-built Git executor — when provided, used instead of creating a new Git from locator/options */\n\tgit?: Git;\n\t/** Git execution options (timeout, trust, queue config, hooks, etc.) */\n\tgitOptions?: GitOptions;\n\t/**\n\t * Whether repositories reached through this provider are on the LOCAL filesystem. `false` disables the\n\t * maintenance sub-provider entirely (Live Share): its probes stat local paths, and its writes are\n\t * rejected by the host's command allowlist, so inheriting it yields empty reports and swallowed\n\t * failures rather than an honest \"unsupported here\". Defaults to `true`.\n\t */\n\tlocalRepositories?: boolean;\n}\n\n/**\n * Exposes `isTrackedWithDetails` as a callable method for sub-providers.\n * The class declares it as a regular method, but TypeScript's structural\n * typing makes it inaccessible through the class type alone when consumed\n * internally — this type re-maps it to ensure sub-providers can call it.\n */\nexport type CliGitProviderInternal = Omit<CliGitProvider, 'isTrackedWithDetails'> & {\n\tisTrackedWithDetails: CliGitProvider['isTrackedWithDetails'];\n};\n\n/**\n * A fully-wired CLI-based git provider that executes git commands via `child_process`.\n * Implements both `CliGitProviderInternal` (for internal sub-provider wiring)\n * and `GitProvider` (for registration in a provider registry).\n *\n * @example\n * ```typescript\n * const provider = new CliGitProvider({\n * context: { events: myEvents },\n * locator: () => findGitPath(null),\n * gitOptions: { gitTimeout: 30000 },\n * });\n *\n * const branches = await provider.branches.getBranches(repoPath);\n * const status = await provider.status.getStatus(repoPath);\n * ```\n */\nexport class CliGitProvider implements GitProvider {\n\treadonly descriptor: GitProviderDescriptor = { id: 'git', name: 'Git', virtual: false };\n\n\tprivate readonly _cache: Cache;\n\tprivate readonly _cacheOwned: boolean;\n\tprivate readonly _git: Git;\n\n\treadonly context: GitServiceContext;\n\t/** The host's own hooks, before the provider wraps `cache.onReset` with its own clearing */\n\tprivate readonly _hostHooks: GitServiceContext['hooks'];\n\tprivate readonly _localRepositories: boolean;\n\n\tconstructor(options: CliGitProviderOptions) {\n\t\t// Every typed write announces itself through `onReset`, so the provider clears its own state there\n\t\t// rather than trusting each host to: a host with no handler still reads fresh after a write. A write\n\t\t// hard-evicts, so a caller after it never joins a read that started before it; watcher-driven clears\n\t\t// reach `Cache.clearCaches` directly and keep sharing in-flight reads.\n\t\tconst hostHooks = options.context.hooks;\n\t\tthis._hostHooks = hostHooks;\n\t\tthis.context = {\n\t\t\t...options.context,\n\t\t\thooks: {\n\t\t\t\t...hostHooks,\n\t\t\t\tcache: {\n\t\t\t\t\t...hostHooks?.cache,\n\t\t\t\t\tonReset: (repoPath, ...types) => {\n\t\t\t\t\t\tthis._cache.evictCaches(repoPath, ...types);\n\t\t\t\t\t\tthis._git.clearPendingCommands(this.getRepositoryPaths(repoPath));\n\t\t\t\t\t\thostHooks?.cache?.onReset?.(repoPath, ...types);\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\trepository: {\n\t\t\t\t\t...hostHooks?.repository,\n\t\t\t\t\tonChanged: (repoPath, changes) => {\n\t\t\t\t\t\t// Everything the watcher would have cleared for the write, since its own file events are\n\t\t\t\t\t\t// dropped once it is announced (the typed write's `onReset` names only what it must evict)\n\t\t\t\t\t\tthis.resetForChanges(repoPath, changes, undefined);\n\t\t\t\t\t\t// Worktrees share refs, tags, stash, remotes and config, so a typed write in one changes what\n\t\t\t\t\t\t// its siblings show; the host hears the shared part of it for each, and they read fresh.\n\t\t\t\t\t\t// Every path is reset before any is announced, so a read the host starts on hearing one path\n\t\t\t\t\t\t// isn't evicted by the next path's reset\n\t\t\t\t\t\tconst siblings = this.getSiblingChanges(repoPath, changes);\n\t\t\t\t\t\tfor (const [path, shared] of siblings) {\n\t\t\t\t\t\t\tthis.resetForChanges(path, shared, undefined, true);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.announceChanged(repoPath, changes);\n\t\t\t\t\t\tfor (const [path, shared] of siblings) {\n\t\t\t\t\t\t\tthis.announceChanged(path, shared);\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t\tthis._localRepositories = options.localRepositories ?? true;\n\t\tthis._git =\n\t\t\toptions.git ??\n\t\t\tnew Git(options.locator, {\n\t\t\t\t...options.gitOptions,\n\t\t\t\tisTrusted:\n\t\t\t\t\toptions.context.workspace?.isTrusted != null\n\t\t\t\t\t\t? () => options.context.workspace!.isTrusted!\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\tthis._cacheOwned = !(options.cache instanceof Cache);\n\t\tthis._cache = options.cache instanceof Cache ? options.cache : new Cache();\n\n\t\t// Wires a raw `git.run({ notify })` call back to this provider's own cache/host announcement —\n\t\t// unconditionally, so a host-supplied `options.git` gets bound exactly like one we construct here.\n\t\tthis._git.bindChangeNotifier((repoPaths, changes, sharedPaths) =>\n\t\t\tthis.notifyChangedCore(repoPaths, changes, undefined, sharedPaths),\n\t\t);\n\t}\n\n\t/** The underlying cache instance (for repo path registration, etc.) */\n\tget cache(): Cache {\n\t\treturn this._cache;\n\t}\n\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._cache.onDidChangeMergeTargets;\n\t}\n\n\tgetCommonPath(repoPath: string): string | undefined {\n\t\t// An unregistered path is its own common path in the cache, which would hide a sibling worktree's writes\n\t\treturn this._cache.isRegistered(repoPath) ? this._cache.getCommonPath(repoPath) : undefined;\n\t}\n\n\tprivate readonly _onDidAnnounceChanges = new Emitter<AnnouncedRepositoryChanges>();\n\tget onDidAnnounceChanges(): Event<AnnouncedRepositoryChanges> {\n\t\treturn this._onDidAnnounceChanges.event;\n\t}\n\n\t/** The underlying git execution instance (for sub-providers that need direct exec/stream access) */\n\tget git(): Git {\n\t\treturn this._git;\n\t}\n\n\t/** Returns whether the installed git version supports the given feature. */\n\tsupports(feature: GitFeatures): boolean | Promise<boolean> {\n\t\treturn this._git.supports(feature);\n\t}\n\n\t/** Returns all features matching the prefix that the installed git version supports. */\n\tsupported<T extends GitFeatureOrPrefix>(feature: T): FilteredGitFeatures<T>[] | Promise<FilteredGitFeatures<T>[]> {\n\t\treturn this._git.supported(feature);\n\t}\n\n\t/** Throws if the installed git version does not support the given feature. */\n\tasync ensureSupports(feature: GitFeatures, prefix: string, suffix: string): Promise<void>;\n\tasync ensureSupports(feature: GitFeatures, message: GitFeatureUnsupportedMessage): Promise<void>;\n\tasync ensureSupports(\n\t\tfeature: GitFeatures,\n\t\tprefixOrMessage: string | GitFeatureUnsupportedMessage,\n\t\tsuffix?: string,\n\t): Promise<void> {\n\t\treturn typeof prefixOrMessage === 'function'\n\t\t\t? this._git.ensureSupports(feature, prefixOrMessage)\n\t\t\t: this._git.ensureSupports(feature, prefixOrMessage, suffix!);\n\t}\n\n\t/** Returns the installed git version string. */\n\tasync version(): Promise<string> {\n\t\treturn this._git.version();\n\t}\n\n\t/** Returns the resolved git binary path. */\n\tasync path(): Promise<string> {\n\t\treturn this._git.path();\n\t}\n\n\tasync clone(\n\t\turl: string,\n\t\tparentPath: string,\n\t\toptions?: { folderName?: string },\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<string | undefined> {\n\t\tlet folderPath: string;\n\t\tif (options?.folderName) {\n\t\t\tfolderPath = joinPaths(parentPath, options.folderName);\n\t\t} else {\n\t\t\tconst [, , remotePath] = parseGitRemoteUrl(url);\n\t\t\tconst remoteName = remotePath.split('/').pop();\n\t\t\tif (!remoteName) return undefined;\n\n\t\t\tlet count = 0;\n\t\t\tfolderPath = joinPaths(parentPath, remoteName);\n\t\t\twhile ((await fsExists(folderPath)) && count < 20) {\n\t\t\t\tcount++;\n\t\t\t\tfolderPath = joinPaths(parentPath, `${remoteName}-${count}`);\n\t\t\t}\n\t\t}\n\n\t\t// `throw`: the default handler swallows `remoteConnectionError`, which would return a folder never cloned\n\t\tawait this._git.run({ cwd: parentPath, errors: 'throw', ...runOptions }, 'clone', url, folderPath);\n\n\t\treturn folderPath;\n\t}\n\n\t/** Creates a new repository at `targetPath` (`git init`), creating it first if it doesn't yet exist. */\n\tasync init(targetPath: string, options?: { defaultBranch?: string; bare?: boolean }): Promise<void> {\n\t\tawait fs.mkdir(targetPath, { recursive: true });\n\n\t\tconst args = ['init'];\n\t\tif (options?.defaultBranch) {\n\t\t\targs.push('-b', options.defaultBranch);\n\t\t}\n\t\tif (options?.bare) {\n\t\t\targs.push('--bare');\n\t\t}\n\n\t\t// Run inside the target rather than naming it from its parent: a relative `targetPath` would otherwise\n\t\t// be resolved against the parent a second time\n\t\tawait this._git.run({ cwd: targetPath }, ...args);\n\t}\n\n\t/**\n\t * Notifies the provider that `repoPath` was mutated outside of a typed sub-provider method — e.g. a\n\t * consumer that ran a command through the raw `git.run`/`provider.git.run` escape hatch. Fires the same\n\t * `cache.onReset`/`repository.onChanged` hooks a typed mutator would, so, exactly as after a typed write,\n\t * the provider hard-evicts its own caches for `repoPath` and drops its pending commands for the repository\n\t * (its sibling worktrees included) before the host's handlers run.\n\t *\n\t * What is reset: `options.cache` when given (`'all'` for everything, `[]` for nothing); otherwise the\n\t * cache types `changes` map to, the same mapping a file watcher's changes get, status clock included.\n\t * An empty `changes`, or one naming `'unknown'` or `'closed'`, resets everything. Changes that map to\n\t * no cache type (e.g. `'starred'`) reset nothing, and `onReset` doesn't fire; `onChanged` always fires.\n\t */\n\tnotifyChanged(\n\t\trepoPath: string,\n\t\tchanges: readonly RepositoryChange[],\n\t\toptions?: { cache?: readonly CachedGitTypes[] | 'all' },\n\t): void {\n\t\tthis.notifyChangedCore([repoPath], changes, options);\n\t}\n\n\t/**\n\t * {@link notifyChanged} for one change seen from several paths, e.g. a `-C` shared-state verb announced\n\t * for both its target and the caller's `cwd`. Each path's caches are reset and the host's `onReset` fires\n\t * for each, since a host may keep per-worktree state, but a repository's pending commands are dropped once\n\t * however many of its worktrees are named.\n\t *\n\t * Each path hears the announcement once: `repoPaths` hear all of `changes`, `sharedPaths` (reset and\n\t * `onReset`-ed like `repoPaths`) and then the other registered worktrees sharing a repository with any of\n\t * them (reset like a typed write's, with no `onReset`) hear the shared part of it, see\n\t * {@link getSiblingChanges}.\n\t */\n\tprivate notifyChangedCore(\n\t\trepoPaths: readonly string[],\n\t\tchanges: readonly RepositoryChange[],\n\t\toptions?: { cache?: readonly CachedGitTypes[] | 'all' },\n\t\tsharedPaths?: readonly string[],\n\t): void {\n\t\tconst announcements = new Map<string, readonly RepositoryChange[]>();\n\t\tfor (const repoPath of repoPaths) {\n\t\t\tannouncements.set(repoPath, [...changes]);\n\t\t}\n\t\tconst shared = sharedPaths?.length ? this.getSharedChanges(changes) : undefined;\n\t\tif (shared != null) {\n\t\t\tfor (const path of sharedPaths!) {\n\t\t\t\tif (!announcements.has(path)) {\n\t\t\t\t\tannouncements.set(path, shared);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst resets: { repoPath: string; types: CachedGitTypes[] }[] = [];\n\t\tconst pendingPaths = new Set<string>();\n\t\tfor (const [repoPath, announced] of announcements) {\n\t\t\tconst types = this.resetForChanges(repoPath, announced, options?.cache);\n\t\t\tif (types == null) {\n\t\t\t\t// A gk config change is reconciled outside the cache types, but its re-read must not join a pre-write run\n\t\t\t\tif (options?.cache == null && announced.includes('gkConfig')) {\n\t\t\t\t\tfor (const path of this.getRepositoryPaths(repoPath)) {\n\t\t\t\t\t\tpendingPaths.add(path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tresets.push({ repoPath: repoPath, types: types });\n\t\t\tfor (const path of this.getRepositoryPaths(repoPath)) {\n\t\t\t\tpendingPaths.add(path);\n\t\t\t}\n\t\t}\n\n\t\t// The worktrees no one named: their shared refs, tags, stash, remotes and config just changed too\n\t\tconst named = [...announcements.keys()];\n\t\tfor (const repoPath of named) {\n\t\t\tfor (const [path, siblingChanges] of this.getSiblingChanges(repoPath, changes)) {\n\t\t\t\tif (announcements.has(path)) continue;\n\n\t\t\t\tannouncements.set(path, siblingChanges);\n\t\t\t\tthis.resetForChanges(path, siblingChanges, options?.cache, true);\n\t\t\t}\n\t\t}\n\n\t\tif (pendingPaths.size) {\n\t\t\tthis._git.clearPendingCommands([...pendingPaths]);\n\t\t}\n\t\tfor (const { repoPath, types } of resets) {\n\t\t\tthis._hostHooks?.cache?.onReset?.(repoPath, ...types);\n\t\t}\n\t\t// The host's own hook: `context.hooks.repository.onChanged` also fans out to siblings, which would\n\t\t// announce a path a second time\n\t\tfor (const [path, announced] of announcements) {\n\t\t\tthis.announceChanged(path, [...announced]);\n\t\t}\n\t}\n\n\t/**\n\t * Tells only the host's own `repository.onChanged` about `changes` — for a follow-up that is not a write of\n\t * core's, such as re-announcing an outside change once it was reconciled. Nothing is evicted, no sibling hears\n\t * it, and the watch layer doesn't take it for a write's announcement.\n\t */\n\tnotifyHost(repoPath: string, changes: RepositoryChange[]): void {\n\t\tthis._hostHooks?.repository?.onChanged?.(repoPath, changes);\n\t}\n\n\t/** Tells the host about a write for one path, after its caches were reset — the watch layer hears of it first */\n\tprivate announceChanged(repoPath: string, changes: RepositoryChange[]): void {\n\t\tthis._onDidAnnounceChanges.fire({ repoPath: repoPath, changes: changes });\n\t\tthis._hostHooks?.repository?.onChanged?.(repoPath, changes);\n\t}\n\n\t/**\n\t * What the worktrees sharing a repository hear of `changes` that ran in one of them: the part worktrees\n\t * share (see {@link worktreeSharedChanges}), or `undefined` when `changes` has none. An empty `changes` or\n\t * `'unknown'` means everything may have changed, so it reaches them as every shared kind — never as\n\t * `'unknown'` itself, which a host treats as the end of a watch session; `'closed'` concerns the one path\n\t * alone and reaches none.\n\t */\n\tprivate getSharedChanges(changes: readonly RepositoryChange[]): RepositoryChange[] | undefined {\n\t\tif (changes.includes('closed')) return undefined;\n\t\tif (!changes.length || changes.includes('unknown')) return [...worktreeSharedChanges];\n\n\t\tconst shared = changes.filter(c => worktreeSharedChanges.has(c));\n\t\treturn shared.length ? shared : undefined;\n\t}\n\n\t/** What each registered worktree sharing `repoPath`'s repository, other than `repoPath`, hears of `changes`. */\n\tprivate getSiblingChanges(repoPath: string, changes: readonly RepositoryChange[]): Map<string, RepositoryChange[]> {\n\t\tconst siblings = new Map<string, RepositoryChange[]>();\n\t\tconst shared = this.getSharedChanges(changes);\n\t\tif (shared == null) return siblings;\n\n\t\tconst self = normalizePath(repoPath);\n\t\tfor (const path of this._cache.getWorktreePaths(this._cache.getCommonPath(repoPath))) {\n\t\t\tif (path !== self) {\n\t\t\t\tsiblings.set(path, [...shared]);\n\t\t\t}\n\t\t}\n\t\treturn siblings;\n\t}\n\n\t/**\n\t * Evicts what a notified change reset for `repoPath`: the types reset (`[]` = all), or `undefined` for none.\n\t * A `sibling` shares its repository with a path already reset for the same change, which evicted the shared\n\t * entries for every worktree, so the changes evict only its own entries and advance its status clock.\n\t */\n\tprivate resetForChanges(\n\t\trepoPath: string,\n\t\tchanges: readonly RepositoryChange[],\n\t\tcache: readonly CachedGitTypes[] | 'all' | undefined,\n\t\tsibling: boolean = false,\n\t): CachedGitTypes[] | undefined {\n\t\tif (cache != null && cache !== 'all') {\n\t\t\tif (!cache.length) return undefined;\n\n\t\t\tthis._cache.evictCaches(repoPath, ...cache);\n\t\t\treturn [...cache];\n\t\t}\n\n\t\tif (cache === 'all' || !changes.length || changes.some(c => c === 'unknown' || c === 'closed')) {\n\t\t\tthis._cache.evictCaches(repoPath);\n\t\t\treturn [];\n\t\t}\n\n\t\tconst types = this._cache.applyRepositoryChanges(\n\t\t\trepoPath,\n\t\t\tchanges,\n\t\t\t'evict',\n\t\t\tsibling ? { worktreeOnly: true } : undefined,\n\t\t);\n\t\treturn types.length ? types : undefined;\n\t}\n\n\t/** `repoPath` and every worktree registered as sharing its repository, since a shared-ref write moves them all. */\n\tprivate getRepositoryPaths(repoPath: string): string[] {\n\t\tconst commonPath = this._cache.getCommonPath(repoPath);\n\t\treturn [...new Set([repoPath, commonPath, ...this._cache.getWorktreePaths(commonPath)])];\n\t}\n\n\tprivate _blame: BlameGitSubProvider | undefined;\n\tget blame(): BlameGitSubProvider {\n\t\treturn (this._blame ??= new BlameGitSubProvider(\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _branches: BranchesGitSubProvider | undefined;\n\tget branches(): BranchesGitSubProvider {\n\t\treturn (this._branches ??= new BranchesGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _commits: CommitsGitSubProvider | undefined;\n\tget commits(): CommitsGitSubProvider {\n\t\treturn (this._commits ??= new CommitsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _config: ConfigGitSubProvider | undefined;\n\tget config(): ConfigGitSubProvider {\n\t\treturn (this._config ??= new ConfigGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _contributors: ContributorsGitSubProvider | undefined;\n\tget contributors(): ContributorsGitSubProvider {\n\t\treturn (this._contributors ??= new ContributorsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _diff: DiffGitSubProvider | undefined;\n\tget diff(): DiffGitSubProvider {\n\t\treturn (this._diff ??= new DiffGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _graph: GraphGitSubProvider | undefined;\n\tget graph(): GraphGitSubProvider {\n\t\treturn (this._graph ??= new GraphGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _maintenance: MaintenanceGitSubProvider | undefined;\n\t// Optional so subclasses whose repos aren't local can report the capability as absent — see\n\t// `VslsGitProvider`. Consumers already treat `maintenance == null` as \"unsupported here\" (web builds\n\t// never register it at all).\n\tget maintenance(): MaintenanceGitSubProvider | undefined {\n\t\tif (!this._localRepositories) return undefined;\n\n\t\treturn (this._maintenance ??= new MaintenanceGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _ops: OperationsGitSubProvider | undefined;\n\tget ops(): OperationsGitSubProvider {\n\t\treturn (this._ops ??= new OperationsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _patch: PatchGitSubProvider | undefined;\n\tget patch(): PatchGitSubProvider {\n\t\treturn (this._patch ??= new PatchGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _pausedOps: PausedOperationsGitSubProvider | undefined;\n\tget pausedOps(): PausedOperationsGitSubProvider {\n\t\treturn (this._pausedOps ??= new PausedOperationsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _refs: RefsGitSubProvider | undefined;\n\tget refs(): RefsGitSubProvider {\n\t\treturn (this._refs ??= new RefsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _remotes: RemotesGitSubProvider | undefined;\n\tget remotes(): RemotesGitSubProvider {\n\t\treturn (this._remotes ??= new RemotesGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _revision: RevisionGitSubProvider | undefined;\n\tget revision(): RevisionGitSubProvider {\n\t\treturn (this._revision ??= new RevisionGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _staging: StagingGitSubProvider | undefined;\n\tget staging(): StagingGitSubProvider {\n\t\treturn (this._staging ??= new StagingGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _stash: StashGitSubProvider | undefined;\n\tget stash(): StashGitSubProvider {\n\t\treturn (this._stash ??= new StashGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _status: StatusGitSubProvider | undefined;\n\tget status(): StatusGitSubProvider {\n\t\treturn (this._status ??= new StatusGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _tags: TagsGitSubProvider | undefined;\n\tget tags(): TagsGitSubProvider {\n\t\treturn (this._tags ??= new TagsGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tprivate _worktrees: WorktreesGitSubProvider | undefined;\n\tget worktrees(): WorktreesGitSubProvider {\n\t\treturn (this._worktrees ??= new WorktreesGitSubProvider(\n\t\t\tthis.context,\n\t\t\tthis._git,\n\t\t\tthis._cache,\n\t\t\tthis as unknown as CliGitProviderInternal,\n\t\t));\n\t}\n\n\tgetAbsoluteUri(pathOrUri: string | Uri, base: string | Uri): Uri {\n\t\tconst baseUri = typeof base !== 'string' ? base : fileUri(base);\n\t\tif (typeof pathOrUri !== 'string') return pathOrUri;\n\t\tif (maybeUri(pathOrUri)) return parseUri(pathOrUri);\n\t\tif (path.isAbsolute(pathOrUri)) return fileUri(pathOrUri);\n\t\treturn joinUriPath(baseUri, pathOrUri);\n\t}\n\n\tgetRelativePath(pathOrUri: string | Uri, base: string | Uri): string {\n\t\tconst [relativePath] = splitPath(toFsPath(pathOrUri), toFsPath(base));\n\t\treturn relativePath;\n\t}\n\n\tasync excludeIgnoredUris(repoPath: string, uris: Uri[]): Promise<Uri[]> {\n\t\tconst filter = this.getIgnoreFilter(repoPath);\n\t\tawait filter.ready();\n\t\treturn uris.filter(uri => {\n\t\t\tconst relativePath = normalizePath(path.relative(repoPath, toFsPath(uri)));\n\t\t\treturn !relativePath || !filter.isIgnored(relativePath);\n\t\t});\n\t}\n\n\tasync getIgnoredUrisFilter(repoPath: string): Promise<(uri: Uri) => boolean> {\n\t\tconst filter = this.getIgnoreFilter(repoPath);\n\t\tawait filter.ready();\n\t\treturn (uri: Uri) => {\n\t\t\tconst relativePath = normalizePath(path.relative(repoPath, toFsPath(uri)));\n\t\t\treturn relativePath ? filter.isIgnored(relativePath) : false;\n\t\t};\n\t}\n\n\tasync getLastFetchedTimestamp(repoPath: string): Promise<number | undefined> {\n\t\treturn this._cache.getLastFetchedTimestamp(repoPath, async (commonPath): Promise<number | undefined> => {\n\t\t\tconst gitDir = await this.config.getGitDir?.(commonPath);\n\t\t\tif (gitDir == null) return undefined;\n\n\t\t\tconst gitDirUri = gitDir.commonUri ?? gitDir.uri;\n\t\t\tconst fetchHeadUri = joinUriPath(gitDirUri, 'FETCH_HEAD');\n\t\t\tconst stats = await this.context.fs.stat(fetchHeadUri);\n\t\t\t// If the file is empty, assume the fetch failed, and don't update the timestamp\n\t\t\tif (stats != null && stats.size > 0) return stats.mtime;\n\n\t\t\treturn undefined;\n\t\t});\n\t}\n\n\tgetIgnoreFilter(repoPath: string): GitIgnoreFilter {\n\t\tlet filter = this._cache.gitIgnore.get(repoPath);\n\t\tif (filter == null) {\n\t\t\tconst gitDir = this._cache.gitDir.get(repoPath);\n\t\t\tconst gitDirPath = gitDir?.uri.fsPath ?? `${repoPath}/.git`;\n\t\t\tfilter = new GitIgnoreFilter({\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tgitDirPath: gitDirPath,\n\t\t\t\tfs: this.context.fs,\n\t\t\t\t// Resolved the way git resolves it; unset, git reads its default location instead\n\t\t\t\tgetGlobalExcludesPath:\n\t\t\t\t\tthis.config.getConfig != null\n\t\t\t\t\t\t? async () => {\n\t\t\t\t\t\t\t\tconst configured = await this.config.getConfig(repoPath, 'core.excludesFile');\n\t\t\t\t\t\t\t\treturn configured != null\n\t\t\t\t\t\t\t\t\t? resolveGitPathConfig(configured, repoPath)\n\t\t\t\t\t\t\t\t\t: getDefaultGlobalExcludesPath();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t: undefined,\n\t\t\t\t// `info/exclude` lives in the COMMON git dir — a linked worktree's own git dir has none\n\t\t\t\tgetInfoExcludePath: async () => {\n\t\t\t\t\tconst gitDir = await this.config.getGitDir?.(repoPath);\n\t\t\t\t\tif (gitDir == null) return undefined;\n\n\t\t\t\t\treturn `${(gitDir.commonUri ?? gitDir.uri).fsPath}/info/exclude`;\n\t\t\t\t},\n\t\t\t\tgetGitIgnorePaths: () => this.getGitIgnorePaths(repoPath),\n\t\t\t\tgetIgnoreCase: async () => {\n\t\t\t\t\tconst value = await this.config.getConfig?.(repoPath, 'core.ignorecase');\n\t\t\t\t\tif (value == null) return undefined;\n\t\t\t\t\tif (parseGitBoolean(value)) return true;\n\n\t\t\t\t\treturn isGitBooleanFalse(value) ? false : undefined;\n\t\t\t\t},\n\t\t\t});\n\t\t\tthis._cache.gitIgnore.set(repoPath, filter);\n\t\t}\n\t\treturn filter;\n\t}\n\n\tasync getIgnoredDirectories(repoPath: string, cancellation?: AbortSignal): Promise<string[]> {\n\t\t// `--directory` collapses a directory to one entry when everything in it is ignored — which includes\n\t\t// directories no rule matches whose current contents merely happen to be ignored (a `logs/` holding only\n\t\t// `*.log` files under a `*.log` rule), so each candidate is confirmed against the rules below\n\t\tconst listed = await this._git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw', cancellation: cancellation },\n\t\t\t'ls-files',\n\t\t\t'-z',\n\t\t\t'--others',\n\t\t\t'--ignored',\n\t\t\t'--exclude-standard',\n\t\t\t'--directory',\n\t\t);\n\n\t\tconst candidates: string[] = [];\n\t\tfor (const entry of iterateByDelimiter(listed.stdout, '\\0')) {\n\t\t\tif (entry.length > 1 && entry.endsWith('/')) {\n\t\t\t\tcandidates.push(entry);\n\t\t\t}\n\t\t}\n\t\tif (!candidates.length) return [];\n\n\t\tconst confirmed = await this.getIgnoredPaths(repoPath, candidates, cancellation);\n\n\t\tconst directories: string[] = [];\n\t\tfor (const entry of confirmed) {\n\t\t\t// Never `.git` itself, and no name a watcher would read as a pattern\n\t\t\tif (entry.length < 2 || !entry.endsWith('/') || /[*?[\\]{}()!+@\\\\]/.test(entry)) continue;\n\n\t\t\tconst directory = entry.substring(0, entry.length - 1);\n\t\t\tif (directory === '.git') continue;\n\n\t\t\tdirectories.push(directory);\n\t\t}\n\n\t\t// A path sorts after every prefix of it, so an entry's ancestors have already been kept (or not) by the time it\n\t\t// is checked — and the kept set comes out sorted\n\t\tdirectories.sort();\n\n\t\tconst kept = new Set<string>();\n\t\tfor (const directory of directories) {\n\t\t\tlet beneathKept = false;\n\t\t\tfor (let i = directory.indexOf('/'); i !== -1; i = directory.indexOf('/', i + 1)) {\n\t\t\t\tif (kept.has(directory.substring(0, i))) {\n\t\t\t\t\tbeneathKept = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!beneathKept) {\n\t\t\t\tkept.add(directory);\n\t\t\t}\n\t\t}\n\n\t\treturn [...kept];\n\t}\n\n\tasync getIgnoredEntries(repoPath: string, cancellation?: AbortSignal): Promise<string[]> {\n\t\tconst listed = await this._git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw', cancellation: cancellation },\n\t\t\t'ls-files',\n\t\t\t'-z',\n\t\t\t'--others',\n\t\t\t'--ignored',\n\t\t\t'--exclude-standard',\n\t\t\t'--directory',\n\t\t\t'--no-empty-directory',\n\t\t);\n\n\t\tconst entries: string[] = [];\n\t\tfor (const entry of iterateByDelimiter(listed.stdout, '\\0')) {\n\t\t\tif (!entry.length || entry === '.git/') continue;\n\n\t\t\tentries.push(entry);\n\t\t}\n\t\treturn entries.sort();\n\t}\n\n\tasync getIgnoredPaths(\n\t\trepoPath: string,\n\t\tpaths: readonly string[],\n\t\tcancellation?: AbortSignal,\n\t): Promise<Set<string>> {\n\t\tif (!paths.length) return new Set();\n\n\t\t// Exits 1 when nothing matches, which is an answer\n\t\tconst result = await this._git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\terrors: 'throw',\n\t\t\t\tcancellation: cancellation,\n\t\t\t\tstdin: `${paths.join('\\0')}\\0`,\n\t\t\t\texpectedExitCodes: [1],\n\t\t\t},\n\t\t\t'check-ignore',\n\t\t\t'-z',\n\t\t\t'--stdin',\n\t\t);\n\n\t\tconst ignored = new Set<string>();\n\t\tfor (const entry of iterateByDelimiter(result.stdout, '\\0')) {\n\t\t\tif (!entry.length) continue;\n\n\t\t\tignored.add(entry);\n\t\t}\n\t\treturn ignored;\n\t}\n\n\t/** Every `.gitignore` git reads in the working tree (tracked, or untracked and not itself ignored), root included */\n\tprivate async getGitIgnorePaths(repoPath: string): Promise<string[]> {\n\t\tconst result = await this._git.run(\n\t\t\t{ cwd: repoPath, errors: 'throw' },\n\t\t\t'ls-files',\n\t\t\t'-z',\n\t\t\t'--cached',\n\t\t\t'--others',\n\t\t\t'--exclude-standard',\n\t\t\t'--',\n\t\t\t':(glob)**/.gitignore',\n\t\t);\n\n\t\tconst paths: string[] = [];\n\t\tfor (const line of iterateByDelimiter(result.stdout, '\\0')) {\n\t\t\tif (!line.length) continue;\n\n\t\t\tpaths.push(line);\n\t\t}\n\t\treturn paths;\n\t}\n\n\tprivate async isTrackedWithDetails(\n\t\tfilePath: string,\n\t\trepoPath: string,\n\t\trev?: string,\n\t): Promise<{ path: string; status?: GitFileStatus; originalPath?: string } | undefined> {\n\t\tif (rev === deletedOrMissing) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst [relativePath, root] = splitPath(filePath, repoPath);\n\n\t\t\t// Check if the file exists in the working tree first (better cache reuse)\n\t\t\tlet tracked = await this.revision.exists(root, relativePath);\n\t\t\tif (tracked) return { path: relativePath };\n\n\t\t\t// If a specific revision was provided, check there too\n\t\t\tif (rev && !isUncommitted(rev)) {\n\t\t\t\ttracked = await this.revision.exists(root, relativePath, rev);\n\t\t\t\tif (!tracked) {\n\t\t\t\t\t// Check the parent revision (file might have been deleted in this rev)\n\t\t\t\t\ttracked = await this.revision.exists(root, relativePath, `${rev}^`);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn tracked ? { path: relativePath } : undefined;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._onDidAnnounceChanges.dispose();\n\t\tthis._git.dispose();\n\t\tif (this._cacheOwned) {\n\t\t\tthis._cache.dispose();\n\t\t}\n\t}\n}\n\n/**\n * Resolves a pathname config value the way git does: a leading `~/` (or a bare `~`) is the home directory, and a\n * relative path is taken from the repository root. Done here rather than with `git config --type=path`, which needs\n * git 2.18, newer than the minimum supported version.\n */\nfunction resolveGitPathConfig(value: string, repoPath: string): string {\n\tif (value === '~' || value.startsWith('~/')) {\n\t\treturn path.join(env.HOME || homedir(), value.substring(1));\n\t}\n\n\treturn path.resolve(repoPath, value);\n}\n\n/** Where git reads global excludes when `core.excludesFile` is unset: `$XDG_CONFIG_HOME/git/ignore`, else `$HOME/.config/git/ignore` */\nfunction getDefaultGlobalExcludesPath(): string | undefined {\n\tconst xdgConfigHome = env.XDG_CONFIG_HOME;\n\tif (xdgConfigHome) return path.join(xdgConfigHome, 'git', 'ignore');\n\n\tconst home = env.HOME || homedir();\n\treturn home ? path.join(home, '.config', 'git', 'ignore') : undefined;\n}\n"],"mappings":"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"}
@@ -5,6 +5,8 @@ export interface LeadingCommand {
5
5
  readOnly: boolean;
6
6
  /** The first token after the verb, unresolved — used for subcommand-shaped decisions (`worktree add`). */
7
7
  subcommand?: string;
8
+ /** Every token after the verb */
9
+ rest: readonly string[];
8
10
  /** The `-C <path>` target when the caller re-scopes the command to another working tree. */
9
11
  pathOverride?: string;
10
12
  }
@@ -13,6 +15,19 @@ export interface LeadingCommand {
13
15
  * `-C` re-scope so notification targets that tree.
14
16
  */
15
17
  export declare function leadingCommand(args: readonly string[]): LeadingCommand;
18
+ /**
19
+ * The change kinds every worktree sharing a `.git` sees: its refs, stash, worktree list and config. The rest a
20
+ * classified verb announces — HEAD, the index, a paused operation, FETCH_HEAD — belong to the one tree the
21
+ * command ran in, so a `-C`-scoped write tells the caller's own `cwd` only this subset of what it changed.
22
+ */
23
+ export declare const worktreeSharedChanges: ReadonlySet<RepositoryChange>;
24
+ /**
25
+ * Whether creating a branch from `startPoint` may write its tracking to `.git/config`, as git's default
26
+ * `branch.autoSetupMerge=true` does from a remote-tracking start point. Only a name holding a `/` can be one, so a
27
+ * local name with a `/` counts too. The `.git/config` watcher reports what this misses, such as
28
+ * `branch.autoSetupMerge=always`.
29
+ */
30
+ export declare function mayWriteTracking(startPoint: string | undefined, noTracking: boolean | undefined): boolean;
16
31
  export interface CommandChanges {
17
32
  changes: readonly RepositoryChange[];
18
33
  /** The `-C <path>` target when the caller re-scoped the command to another working tree. */
@@ -157,6 +157,9 @@ const configReadOptions = /* @__PURE__ */ new Set([
157
157
  function hasWriteOption(verb, rest) {
158
158
  const options = writeOptions[verb];
159
159
  if (options == null) return false;
160
+ return hasOption(rest, options);
161
+ }
162
+ function hasOption(rest, options) {
160
163
  return rest.some((arg) => options.some((opt) => arg === opt || arg.startsWith(`${opt}=`)));
161
164
  }
162
165
  /**
@@ -209,6 +212,7 @@ function leadingCommand(args) {
209
212
  verb,
210
213
  readOnly: !hasWriteOption(verb, rest) && (readOnlyVerbs.has(verb) || (readOnlySubcommands[verb]?.has(rest[0] ?? "") ?? false) || verb === "config" && isConfigRead(rest) || verb === "reflog" && !reflogWriteSubcommands.has(rest[0] ?? "") || verb === "symbolic-ref" && !rest.includes("-d") && !rest.includes("--delete") && rest.filter((a) => !a.startsWith("-")).length < 2),
211
214
  subcommand: rest[0],
215
+ rest,
212
216
  pathOverride
213
217
  };
214
218
  }
@@ -367,6 +371,45 @@ const changesByVerb = {
367
371
  ],
368
372
  notes: ["heads"]
369
373
  };
374
+ /**
375
+ * `branch` options that write `.git/config`: a rename, copy or delete moves or drops the branch's section, and the
376
+ * rest set or clear its upstream or description. A plain create (or `-f`) writes tracking only from a remote-tracking
377
+ * start point, which the `.git/config` watcher reports.
378
+ */
379
+ const branchConfigOptions = [
380
+ "-d",
381
+ "-D",
382
+ "--delete",
383
+ "-m",
384
+ "-M",
385
+ "--move",
386
+ "-c",
387
+ "-C",
388
+ "--copy",
389
+ "-t",
390
+ "--track",
391
+ "-u",
392
+ "--set-upstream-to",
393
+ "--unset-upstream",
394
+ "--edit-description"
395
+ ];
396
+ const branchConfigChanges = [
397
+ "config",
398
+ "heads",
399
+ "remotes",
400
+ "tags"
401
+ ];
402
+ /**
403
+ * Whether creating a branch from `startPoint` may write its tracking to `.git/config`, as git's default
404
+ * `branch.autoSetupMerge=true` does from a remote-tracking start point. Only a name holding a `/` can be one, so a
405
+ * local name with a `/` counts too. The `.git/config` watcher reports what this misses, such as
406
+ * `branch.autoSetupMerge=always`.
407
+ */
408
+ function mayWriteTracking(startPoint, noTracking) {
409
+ if (noTracking || !startPoint) return false;
410
+ if (startPoint.startsWith("refs/")) return startPoint.startsWith("refs/remotes/");
411
+ return startPoint.includes("/");
412
+ }
370
413
  const worktreeChanges = ["worktrees", "heads"];
371
414
  const emptyChanges = [];
372
415
  /**
@@ -378,7 +421,10 @@ const emptyChanges = [];
378
421
  function getChangesForCommand(args) {
379
422
  const command = leadingCommand(args);
380
423
  if (command.readOnly || objectOnlyVerbs.has(command.verb)) return void 0;
381
- const changes = command.verb === "worktree" ? worktreeChanges : changesByVerb[command.verb] ?? emptyChanges;
424
+ let changes;
425
+ if (command.verb === "worktree") changes = worktreeChanges;
426
+ else if (command.verb === "branch" && hasOption(command.rest, branchConfigOptions)) changes = branchConfigChanges;
427
+ else changes = changesByVerb[command.verb] ?? emptyChanges;
382
428
  return {
383
429
  changes,
384
430
  pathOverride: command.pathOverride,
@@ -386,6 +432,6 @@ function getChangesForCommand(args) {
386
432
  };
387
433
  }
388
434
  //#endregion
389
- export { getChangesForCommand, leadingCommand };
435
+ export { getChangesForCommand, leadingCommand, mayWriteTracking, worktreeSharedChanges };
390
436
 
391
437
  //# sourceMappingURL=commandClassifier.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"commandClassifier.js","names":[],"sources":["../../../../git-cli/src/exec/commandClassifier.ts"],"sourcesContent":["import { isAbsolute, join } from 'node:path';\nimport type { RepositoryChange } from '@gitlens/git/models/repository.js';\n\n/**\n * Verbs that never move a ref, the index or the working tree, so a raw run needs no notification.\n * Anything not listed is treated as a mutation: a needless notification costs a full cache eviction, a\n * missed one serves stale state. Object-writing plumbing (`hash-object -w`, `mktree`, `commit-tree`,\n * `write-tree`) is listed too: it only adds loose objects nothing cached observes.\n */\nconst readOnlyVerbs = new Set([\n\t'annotate',\n\t'archive',\n\t'blame',\n\t'cat-file',\n\t'check-attr',\n\t'check-ignore',\n\t'check-mailmap',\n\t'check-ref-format',\n\t'cherry',\n\t'commit-tree',\n\t'count-objects',\n\t'describe',\n\t'diff',\n\t'diff-files',\n\t'diff-index',\n\t'diff-tree',\n\t'for-each-ref',\n\t'fsck',\n\t'grep',\n\t'hash-object',\n\t'help',\n\t'log',\n\t'ls-files',\n\t// Asks a remote what it has; nothing local moves.\n\t'ls-remote',\n\t'ls-tree',\n\t'merge-base',\n\t'merge-tree',\n\t'mktree',\n\t'name-rev',\n\t'patch-id',\n\t'range-diff',\n\t'rev-list',\n\t'rev-parse',\n\t'shortlog',\n\t'show',\n\t'show-branch',\n\t'show-ref',\n\t'status',\n\t'var',\n\t'verify-commit',\n\t'verify-pack',\n\t'verify-tag',\n\t'version',\n\t'whatchanged',\n\t'write-tree',\n]);\n\nconst reflogWriteSubcommands: ReadonlySet<string> = new Set(['expire', 'delete', 'drop']);\n\n/**\n * Sub-commands of otherwise-mutating verbs that only list. `''` is the bare verb, which lists for `branch`,\n * `tag`, `remote` and `notes`; `branch -v` and every `tag` filter imply git's own list mode.\n */\nconst readOnlySubcommands: Readonly<Record<string, ReadonlySet<string>>> = {\n\tbranch: new Set([\n\t\t'',\n\t\t'-l',\n\t\t'--list',\n\t\t'--show-current',\n\t\t'-a',\n\t\t'-r',\n\t\t'-v',\n\t\t'-vv',\n\t\t'--verbose',\n\t\t'--contains',\n\t\t'--no-contains',\n\t\t'--merged',\n\t\t'--no-merged',\n\t\t'--points-at',\n\t]),\n\tconfig: new Set(['get', 'list']),\n\tnotes: new Set(['', 'list', 'show', 'get-ref']),\n\tstash: new Set(['list', 'show']),\n\ttag: new Set([\n\t\t'',\n\t\t'-l',\n\t\t'--list',\n\t\t'-n',\n\t\t'-v',\n\t\t'--verify',\n\t\t'--contains',\n\t\t'--no-contains',\n\t\t'--merged',\n\t\t'--no-merged',\n\t\t'--points-at',\n\t]),\n\tworktree: new Set(['list']),\n\tremote: new Set(['', '-v', 'get-url', 'show']),\n};\n\n/**\n * Options that make an argv write whatever else it contains, checked across the whole argv rather than\n * only its first token: `branch -r -d origin/x` leads with a listing flag and still deletes.\n */\nconst writeOptions: Readonly<Record<string, readonly string[]>> = {\n\tbranch: [\n\t\t'-d',\n\t\t'-D',\n\t\t'--delete',\n\t\t'-m',\n\t\t'-M',\n\t\t'--move',\n\t\t'-c',\n\t\t'-C',\n\t\t'--copy',\n\t\t'-f',\n\t\t'--force',\n\t\t'-t',\n\t\t'--track',\n\t\t'--no-track',\n\t\t'-u',\n\t\t'--set-upstream-to',\n\t\t'--unset-upstream',\n\t\t'--edit-description',\n\t],\n\tconfig: [\n\t\t'--add',\n\t\t'--replace-all',\n\t\t'--unset',\n\t\t'--unset-all',\n\t\t'--rename-section',\n\t\t'--remove-section',\n\t\t'-e',\n\t\t'--edit',\n\t],\n};\n\nconst configReadOptions = new Set([\n\t'--get',\n\t'--get-all',\n\t'--get-regexp',\n\t'--get-urlmatch',\n\t'--get-color',\n\t'--get-colorbool',\n\t'--list',\n\t'-l',\n]);\n\nfunction hasWriteOption(verb: string, rest: readonly string[]): boolean {\n\tconst options = writeOptions[verb];\n\tif (options == null) return false;\n\treturn rest.some(arg => options.some(opt => arg === opt || arg.startsWith(`${opt}=`)));\n}\n\n/**\n * `config <key>` with nothing after the key is git's own read form, as is any `--get*`/`--list`. `config edit`\n * (git 2.46's subcommand form of `--edit`) also has a single non-flag argument but writes.\n */\nfunction isConfigRead(rest: readonly string[]): boolean {\n\tif (rest[0] === 'edit') return false;\n\tif (rest.some(arg => configReadOptions.has(arg))) return true;\n\n\treturn rest.filter(arg => !arg.startsWith('-')).length === 1;\n}\n\n/** Global options whose value is the next argument, so the verb scan must skip both. */\nconst globalOptionsWithValue = new Set([\n\t'-c',\n\t'--config-env',\n\t'--exec-path',\n\t'--git-dir',\n\t'--namespace',\n\t'--super-prefix',\n\t'--work-tree',\n]);\n\nexport interface LeadingCommand {\n\tverb: string;\n\treadOnly: boolean;\n\t/** The first token after the verb, unresolved — used for subcommand-shaped decisions (`worktree add`). */\n\tsubcommand?: string;\n\t/** The `-C <path>` target when the caller re-scopes the command to another working tree. */\n\tpathOverride?: string;\n}\n\n/**\n * Skips the global options (`-c key=val`, `-C path`) git accepts before the verb to find it, and notes a\n * `-C` re-scope so notification targets that tree.\n */\nexport function leadingCommand(args: readonly string[]): LeadingCommand {\n\tlet pathOverride: string | undefined;\n\tlet i = 0;\n\twhile (i < args.length) {\n\t\tconst arg = args[i];\n\t\tif (arg === '-C') {\n\t\t\t// git applies each `-C` relative to the one before it\n\t\t\tconst path = args[i + 1];\n\t\t\tif (path != null) {\n\t\t\t\tpathOverride = pathOverride == null || isAbsolute(path) ? path : join(pathOverride, path);\n\t\t\t}\n\t\t\ti += 2;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (globalOptionsWithValue.has(arg)) {\n\t\t\ti += 2;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg.startsWith('-')) {\n\t\t\ti += 1;\n\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tconst verb = args[i] ?? '';\n\tconst rest = args.slice(i + 1);\n\tconst readOnly =\n\t\t!hasWriteOption(verb, rest) &&\n\t\t(readOnlyVerbs.has(verb) ||\n\t\t\t(readOnlySubcommands[verb]?.has(rest[0] ?? '') ?? false) ||\n\t\t\t(verb === 'config' && isConfigRead(rest)) ||\n\t\t\t// Every `reflog` form reads (bare, `<ref>`, `show`, `list`, `exists`) except the three that rewrite\n\t\t\t// it. Base-branch detection answers \"none\" unless a branch's reflog has exactly one creation entry,\n\t\t\t// so a rewrite can make a cached \"none\" derivable; no change kind names a reflog, so it resets all.\n\t\t\t(verb === 'reflog' && !reflogWriteSubcommands.has(rest[0] ?? '')) ||\n\t\t\t// A bare `symbolic-ref <name>` reads; a second non-flag argument, or a delete, writes.\n\t\t\t(verb === 'symbolic-ref' &&\n\t\t\t\t!rest.includes('-d') &&\n\t\t\t\t!rest.includes('--delete') &&\n\t\t\t\trest.filter(a => !a.startsWith('-')).length < 2));\n\n\treturn { verb: verb, readOnly: readOnly, subcommand: rest[0], pathOverride: pathOverride };\n}\n\n/**\n * The change kinds every worktree sharing a `.git` sees: its refs, stash, worktree list and config. The rest a\n * classified verb announces — HEAD, the index, a paused operation, FETCH_HEAD — belong to the one tree the\n * command ran in, so a `-C`-scoped write tells the caller's own `cwd` only this subset of what it changed.\n */\nconst worktreeSharedChanges: ReadonlySet<RepositoryChange> = new Set([\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'config',\n]);\n\n/**\n * Verbs whose effect is confined to the object store that nothing cached observes — repacking or pruning\n * `.git/objects` changes no cached read (branches, refs, status, worktrees, …). `init` and `clone` create a\n * repository rather than mutate a registered one, so there is nothing yet registered to notify.\n */\nconst objectOnlyVerbs = new Set([\n\t'gc',\n\t'repack',\n\t'prune',\n\t'maintenance',\n\t'commit-graph',\n\t'multi-pack-index',\n\t'init',\n\t'clone',\n]);\n\n/**\n * The {@link RepositoryChange} kinds each mutating verb announces. A worktree subcommand other than a\n * listing is handled separately (see {@link getChangesForCommand}), and any verb absent from both this\n * table and {@link objectOnlyVerbs} announces `[]` — the conservative default for an unclassified\n * mutation: the caller still learns something changed, just not what. An entry covers at least what the\n * verb's typed mutator announces; `update-ref` and `symbolic-ref` name every kind their target ref could be.\n * The kinds decide which caches the announcement resets, so a verb that can start or end a paused operation\n * names `pausedOp`: `commit` concludes a merge, cherry-pick or revert, `reset` aborts one, and `am` runs in\n * `rebase-apply`.\n */\nconst changesByVerb: Readonly<Record<string, readonly RepositoryChange[]>> = {\n\tcommit: ['head', 'heads', 'index', 'pausedOp'],\n\tam: ['head', 'heads', 'index', 'pausedOp'],\n\tbisect: ['head', 'index'],\n\tcheckout: ['head', 'heads', 'index'],\n\tswitch: ['head', 'heads', 'index'],\n\treset: ['head', 'heads', 'index', 'pausedOp'],\n\tmerge: ['head', 'heads', 'index', 'pausedOp', 'merge'],\n\trebase: ['head', 'heads', 'index', 'pausedOp', 'rebase'],\n\t'cherry-pick': ['head', 'heads', 'index', 'pausedOp', 'cherryPick'],\n\trevert: ['head', 'heads', 'index', 'pausedOp', 'revert'],\n\tpull: ['head', 'heads', 'index', 'remotes', 'tags', 'lastFetched', 'pausedOp', 'merge', 'rebase'],\n\tadd: ['index'],\n\trm: ['index'],\n\tmv: ['index'],\n\trestore: ['index'],\n\tapply: ['index'],\n\t'read-tree': ['index'],\n\t'update-index': ['index'],\n\tclean: ['index'],\n\tstash: ['stash', 'index'],\n\t'update-ref': ['head', 'heads', 'remotes', 'tags', 'stash'],\n\tbranch: ['heads', 'remotes', 'tags'],\n\t'symbolic-ref': ['head', 'heads', 'remotes', 'tags'],\n\t'pack-refs': ['heads', 'remotes', 'tags'],\n\ttag: ['tags'],\n\t// A refspec can write a local branch (`fetch origin main:main`), which only the typed fetch parses for\n\tfetch: ['heads', 'remotes', 'tags', 'lastFetched'],\n\tpush: ['remotes'],\n\tremote: ['remotes', 'config'],\n\tconfig: ['config', 'heads', 'remotes'],\n\tnotes: ['heads'],\n};\n\nconst worktreeChanges: readonly RepositoryChange[] = ['worktrees', 'heads'];\nconst emptyChanges: readonly RepositoryChange[] = [];\n\nexport interface CommandChanges {\n\tchanges: readonly RepositoryChange[];\n\t/** The `-C <path>` target when the caller re-scoped the command to another working tree. */\n\tpathOverride?: string;\n\t/** The subset of {@link changes} every worktree sharing the same `.git` sees, not just the tree the command ran in. */\n\tsharedChanges: readonly RepositoryChange[];\n}\n\n/**\n * Classifies a raw git argv for the notification `git.run`'s `notify: 'infer'` applies after the run\n * settles. Returns `undefined` for a read-only or object-only command — nothing to announce — otherwise\n * the {@link RepositoryChange} kinds the verb announces, the `-C` re-scope target (if any), and which of\n * those kinds every worktree sees.\n */\nexport function getChangesForCommand(args: readonly string[]): CommandChanges | undefined {\n\tconst command = leadingCommand(args);\n\tif (command.readOnly || objectOnlyVerbs.has(command.verb)) return undefined;\n\n\t// `worktree list` is read-only (filtered out above); every other subcommand adds, removes or\n\t// relocks a checkout, which is both a worktree-list change and, for `add`, a brand-new HEAD.\n\tconst changes = command.verb === 'worktree' ? worktreeChanges : (changesByVerb[command.verb] ?? emptyChanges);\n\treturn {\n\t\tchanges: changes,\n\t\tpathOverride: command.pathOverride,\n\t\tsharedChanges: changes.filter(c => worktreeSharedChanges.has(c)),\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"commandClassifier.js","names":[],"sources":["../../../../git-cli/src/exec/commandClassifier.ts"],"sourcesContent":["import { isAbsolute, join } from 'node:path';\nimport type { RepositoryChange } from '@gitlens/git/models/repository.js';\n\n/**\n * Verbs that never move a ref, the index or the working tree, so a raw run needs no notification.\n * Anything not listed is treated as a mutation: a needless notification costs a full cache eviction, a\n * missed one serves stale state. Object-writing plumbing (`hash-object -w`, `mktree`, `commit-tree`,\n * `write-tree`) is listed too: it only adds loose objects nothing cached observes.\n */\nconst readOnlyVerbs = new Set([\n\t'annotate',\n\t'archive',\n\t'blame',\n\t'cat-file',\n\t'check-attr',\n\t'check-ignore',\n\t'check-mailmap',\n\t'check-ref-format',\n\t'cherry',\n\t'commit-tree',\n\t'count-objects',\n\t'describe',\n\t'diff',\n\t'diff-files',\n\t'diff-index',\n\t'diff-tree',\n\t'for-each-ref',\n\t'fsck',\n\t'grep',\n\t'hash-object',\n\t'help',\n\t'log',\n\t'ls-files',\n\t// Asks a remote what it has; nothing local moves.\n\t'ls-remote',\n\t'ls-tree',\n\t'merge-base',\n\t'merge-tree',\n\t'mktree',\n\t'name-rev',\n\t'patch-id',\n\t'range-diff',\n\t'rev-list',\n\t'rev-parse',\n\t'shortlog',\n\t'show',\n\t'show-branch',\n\t'show-ref',\n\t'status',\n\t'var',\n\t'verify-commit',\n\t'verify-pack',\n\t'verify-tag',\n\t'version',\n\t'whatchanged',\n\t'write-tree',\n]);\n\nconst reflogWriteSubcommands: ReadonlySet<string> = new Set(['expire', 'delete', 'drop']);\n\n/**\n * Sub-commands of otherwise-mutating verbs that only list. `''` is the bare verb, which lists for `branch`,\n * `tag`, `remote` and `notes`; `branch -v` and every `tag` filter imply git's own list mode.\n */\nconst readOnlySubcommands: Readonly<Record<string, ReadonlySet<string>>> = {\n\tbranch: new Set([\n\t\t'',\n\t\t'-l',\n\t\t'--list',\n\t\t'--show-current',\n\t\t'-a',\n\t\t'-r',\n\t\t'-v',\n\t\t'-vv',\n\t\t'--verbose',\n\t\t'--contains',\n\t\t'--no-contains',\n\t\t'--merged',\n\t\t'--no-merged',\n\t\t'--points-at',\n\t]),\n\tconfig: new Set(['get', 'list']),\n\tnotes: new Set(['', 'list', 'show', 'get-ref']),\n\tstash: new Set(['list', 'show']),\n\ttag: new Set([\n\t\t'',\n\t\t'-l',\n\t\t'--list',\n\t\t'-n',\n\t\t'-v',\n\t\t'--verify',\n\t\t'--contains',\n\t\t'--no-contains',\n\t\t'--merged',\n\t\t'--no-merged',\n\t\t'--points-at',\n\t]),\n\tworktree: new Set(['list']),\n\tremote: new Set(['', '-v', 'get-url', 'show']),\n};\n\n/**\n * Options that make an argv write whatever else it contains, checked across the whole argv rather than\n * only its first token: `branch -r -d origin/x` leads with a listing flag and still deletes.\n */\nconst writeOptions: Readonly<Record<string, readonly string[]>> = {\n\tbranch: [\n\t\t'-d',\n\t\t'-D',\n\t\t'--delete',\n\t\t'-m',\n\t\t'-M',\n\t\t'--move',\n\t\t'-c',\n\t\t'-C',\n\t\t'--copy',\n\t\t'-f',\n\t\t'--force',\n\t\t'-t',\n\t\t'--track',\n\t\t'--no-track',\n\t\t'-u',\n\t\t'--set-upstream-to',\n\t\t'--unset-upstream',\n\t\t'--edit-description',\n\t],\n\tconfig: [\n\t\t'--add',\n\t\t'--replace-all',\n\t\t'--unset',\n\t\t'--unset-all',\n\t\t'--rename-section',\n\t\t'--remove-section',\n\t\t'-e',\n\t\t'--edit',\n\t],\n};\n\nconst configReadOptions = new Set([\n\t'--get',\n\t'--get-all',\n\t'--get-regexp',\n\t'--get-urlmatch',\n\t'--get-color',\n\t'--get-colorbool',\n\t'--list',\n\t'-l',\n]);\n\nfunction hasWriteOption(verb: string, rest: readonly string[]): boolean {\n\tconst options = writeOptions[verb];\n\tif (options == null) return false;\n\treturn hasOption(rest, options);\n}\n\nfunction hasOption(rest: readonly string[], options: readonly string[]): boolean {\n\treturn rest.some(arg => options.some(opt => arg === opt || arg.startsWith(`${opt}=`)));\n}\n\n/**\n * `config <key>` with nothing after the key is git's own read form, as is any `--get*`/`--list`. `config edit`\n * (git 2.46's subcommand form of `--edit`) also has a single non-flag argument but writes.\n */\nfunction isConfigRead(rest: readonly string[]): boolean {\n\tif (rest[0] === 'edit') return false;\n\tif (rest.some(arg => configReadOptions.has(arg))) return true;\n\n\treturn rest.filter(arg => !arg.startsWith('-')).length === 1;\n}\n\n/** Global options whose value is the next argument, so the verb scan must skip both. */\nconst globalOptionsWithValue = new Set([\n\t'-c',\n\t'--config-env',\n\t'--exec-path',\n\t'--git-dir',\n\t'--namespace',\n\t'--super-prefix',\n\t'--work-tree',\n]);\n\nexport interface LeadingCommand {\n\tverb: string;\n\treadOnly: boolean;\n\t/** The first token after the verb, unresolved — used for subcommand-shaped decisions (`worktree add`). */\n\tsubcommand?: string;\n\t/** Every token after the verb */\n\trest: readonly string[];\n\t/** The `-C <path>` target when the caller re-scopes the command to another working tree. */\n\tpathOverride?: string;\n}\n\n/**\n * Skips the global options (`-c key=val`, `-C path`) git accepts before the verb to find it, and notes a\n * `-C` re-scope so notification targets that tree.\n */\nexport function leadingCommand(args: readonly string[]): LeadingCommand {\n\tlet pathOverride: string | undefined;\n\tlet i = 0;\n\twhile (i < args.length) {\n\t\tconst arg = args[i];\n\t\tif (arg === '-C') {\n\t\t\t// git applies each `-C` relative to the one before it\n\t\t\tconst path = args[i + 1];\n\t\t\tif (path != null) {\n\t\t\t\tpathOverride = pathOverride == null || isAbsolute(path) ? path : join(pathOverride, path);\n\t\t\t}\n\t\t\ti += 2;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (globalOptionsWithValue.has(arg)) {\n\t\t\ti += 2;\n\n\t\t\tcontinue;\n\t\t}\n\t\tif (arg.startsWith('-')) {\n\t\t\ti += 1;\n\n\t\t\tcontinue;\n\t\t}\n\t\tbreak;\n\t}\n\tconst verb = args[i] ?? '';\n\tconst rest = args.slice(i + 1);\n\tconst readOnly =\n\t\t!hasWriteOption(verb, rest) &&\n\t\t(readOnlyVerbs.has(verb) ||\n\t\t\t(readOnlySubcommands[verb]?.has(rest[0] ?? '') ?? false) ||\n\t\t\t(verb === 'config' && isConfigRead(rest)) ||\n\t\t\t// Every `reflog` form reads (bare, `<ref>`, `show`, `list`, `exists`) except the three that rewrite\n\t\t\t// it. Base-branch detection answers \"none\" unless a branch's reflog has exactly one creation entry,\n\t\t\t// so a rewrite can make a cached \"none\" derivable; no change kind names a reflog, so it resets all.\n\t\t\t(verb === 'reflog' && !reflogWriteSubcommands.has(rest[0] ?? '')) ||\n\t\t\t// A bare `symbolic-ref <name>` reads; a second non-flag argument, or a delete, writes.\n\t\t\t(verb === 'symbolic-ref' &&\n\t\t\t\t!rest.includes('-d') &&\n\t\t\t\t!rest.includes('--delete') &&\n\t\t\t\trest.filter(a => !a.startsWith('-')).length < 2));\n\n\treturn { verb: verb, readOnly: readOnly, subcommand: rest[0], rest: rest, pathOverride: pathOverride };\n}\n\n/**\n * The change kinds every worktree sharing a `.git` sees: its refs, stash, worktree list and config. The rest a\n * classified verb announces — HEAD, the index, a paused operation, FETCH_HEAD — belong to the one tree the\n * command ran in, so a `-C`-scoped write tells the caller's own `cwd` only this subset of what it changed.\n */\nexport const worktreeSharedChanges: ReadonlySet<RepositoryChange> = new Set([\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'config',\n]);\n\n/**\n * Verbs whose effect is confined to the object store that nothing cached observes — repacking or pruning\n * `.git/objects` changes no cached read (branches, refs, status, worktrees, …). `init` and `clone` create a\n * repository rather than mutate a registered one, so there is nothing yet registered to notify.\n */\nconst objectOnlyVerbs = new Set([\n\t'gc',\n\t'repack',\n\t'prune',\n\t'maintenance',\n\t'commit-graph',\n\t'multi-pack-index',\n\t'init',\n\t'clone',\n]);\n\n/**\n * The {@link RepositoryChange} kinds each mutating verb announces. A worktree subcommand other than a\n * listing is handled separately (see {@link getChangesForCommand}), and any verb absent from both this\n * table and {@link objectOnlyVerbs} announces `[]` — the conservative default for an unclassified\n * mutation: the caller still learns something changed, just not what. An entry covers at least what the\n * verb's typed mutator announces; `update-ref` and `symbolic-ref` name every kind their target ref could be.\n * The kinds decide which caches the announcement resets, so a verb that can start or end a paused operation\n * names `pausedOp`: `commit` concludes a merge, cherry-pick or revert, `reset` aborts one, and `am` runs in\n * `rebase-apply`.\n */\nconst changesByVerb: Readonly<Record<string, readonly RepositoryChange[]>> = {\n\tcommit: ['head', 'heads', 'index', 'pausedOp'],\n\tam: ['head', 'heads', 'index', 'pausedOp'],\n\tbisect: ['head', 'index'],\n\tcheckout: ['head', 'heads', 'index'],\n\tswitch: ['head', 'heads', 'index'],\n\treset: ['head', 'heads', 'index', 'pausedOp'],\n\tmerge: ['head', 'heads', 'index', 'pausedOp', 'merge'],\n\trebase: ['head', 'heads', 'index', 'pausedOp', 'rebase'],\n\t'cherry-pick': ['head', 'heads', 'index', 'pausedOp', 'cherryPick'],\n\trevert: ['head', 'heads', 'index', 'pausedOp', 'revert'],\n\tpull: ['head', 'heads', 'index', 'remotes', 'tags', 'lastFetched', 'pausedOp', 'merge', 'rebase'],\n\tadd: ['index'],\n\trm: ['index'],\n\tmv: ['index'],\n\trestore: ['index'],\n\tapply: ['index'],\n\t'read-tree': ['index'],\n\t'update-index': ['index'],\n\tclean: ['index'],\n\tstash: ['stash', 'index'],\n\t'update-ref': ['head', 'heads', 'remotes', 'tags', 'stash'],\n\tbranch: ['heads', 'remotes', 'tags'],\n\t'symbolic-ref': ['head', 'heads', 'remotes', 'tags'],\n\t'pack-refs': ['heads', 'remotes', 'tags'],\n\ttag: ['tags'],\n\t// A refspec can write a local branch (`fetch origin main:main`), which only the typed fetch parses for\n\tfetch: ['heads', 'remotes', 'tags', 'lastFetched'],\n\tpush: ['remotes'],\n\tremote: ['remotes', 'config'],\n\tconfig: ['config', 'heads', 'remotes'],\n\tnotes: ['heads'],\n};\n\n/**\n * `branch` options that write `.git/config`: a rename, copy or delete moves or drops the branch's section, and the\n * rest set or clear its upstream or description. A plain create (or `-f`) writes tracking only from a remote-tracking\n * start point, which the `.git/config` watcher reports.\n */\nconst branchConfigOptions: readonly string[] = [\n\t'-d',\n\t'-D',\n\t'--delete',\n\t'-m',\n\t'-M',\n\t'--move',\n\t'-c',\n\t'-C',\n\t'--copy',\n\t'-t',\n\t'--track',\n\t'-u',\n\t'--set-upstream-to',\n\t'--unset-upstream',\n\t'--edit-description',\n];\nconst branchConfigChanges: readonly RepositoryChange[] = ['config', 'heads', 'remotes', 'tags'];\n\n/**\n * Whether creating a branch from `startPoint` may write its tracking to `.git/config`, as git's default\n * `branch.autoSetupMerge=true` does from a remote-tracking start point. Only a name holding a `/` can be one, so a\n * local name with a `/` counts too. The `.git/config` watcher reports what this misses, such as\n * `branch.autoSetupMerge=always`.\n */\nexport function mayWriteTracking(startPoint: string | undefined, noTracking: boolean | undefined): boolean {\n\tif (noTracking || !startPoint) return false;\n\tif (startPoint.startsWith('refs/')) return startPoint.startsWith('refs/remotes/');\n\n\treturn startPoint.includes('/');\n}\n\nconst worktreeChanges: readonly RepositoryChange[] = ['worktrees', 'heads'];\nconst emptyChanges: readonly RepositoryChange[] = [];\n\nexport interface CommandChanges {\n\tchanges: readonly RepositoryChange[];\n\t/** The `-C <path>` target when the caller re-scoped the command to another working tree. */\n\tpathOverride?: string;\n\t/** The subset of {@link changes} every worktree sharing the same `.git` sees, not just the tree the command ran in. */\n\tsharedChanges: readonly RepositoryChange[];\n}\n\n/**\n * Classifies a raw git argv for the notification `git.run`'s `notify: 'infer'` applies after the run\n * settles. Returns `undefined` for a read-only or object-only command — nothing to announce — otherwise\n * the {@link RepositoryChange} kinds the verb announces, the `-C` re-scope target (if any), and which of\n * those kinds every worktree sees.\n */\nexport function getChangesForCommand(args: readonly string[]): CommandChanges | undefined {\n\tconst command = leadingCommand(args);\n\tif (command.readOnly || objectOnlyVerbs.has(command.verb)) return undefined;\n\n\t// `worktree list` is read-only (filtered out above); every other subcommand adds, removes or\n\t// relocks a checkout, which is both a worktree-list change and, for `add`, a brand-new HEAD.\n\tlet changes: readonly RepositoryChange[];\n\tif (command.verb === 'worktree') {\n\t\tchanges = worktreeChanges;\n\t} else if (command.verb === 'branch' && hasOption(command.rest, branchConfigOptions)) {\n\t\tchanges = branchConfigChanges;\n\t} else {\n\t\tchanges = changesByVerb[command.verb] ?? emptyChanges;\n\t}\n\treturn {\n\t\tchanges: changes,\n\t\tpathOverride: command.pathOverride,\n\t\tsharedChanges: changes.filter(c => worktreeSharedChanges.has(c)),\n\t};\n}\n"],"mappings":";;;;;;;;AASA,MAAM,gCAAgB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAED,MAAM,yCAA8C,IAAI,IAAI;CAAC;CAAU;CAAU;AAAM,CAAC;;;;;AAMxF,MAAM,sBAAqE;CAC1E,wBAAQ,IAAI,IAAI;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACD,CAAC;CACD,wBAAQ,IAAI,IAAI,CAAC,OAAO,MAAM,CAAC;CAC/B,uBAAO,IAAI,IAAI;EAAC;EAAI;EAAQ;EAAQ;CAAS,CAAC;CAC9C,uBAAO,IAAI,IAAI,CAAC,QAAQ,MAAM,CAAC;CAC/B,qBAAK,IAAI,IAAI;EACZ;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACD,CAAC;CACD,0BAAU,IAAI,IAAI,CAAC,MAAM,CAAC;CAC1B,wBAAQ,IAAI,IAAI;EAAC;EAAI;EAAM;EAAW;CAAM,CAAC;AAC9C;;;;;AAMA,MAAM,eAA4D;CACjE,QAAQ;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACD;CACA,QAAQ;EACP;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACD;AACD;AAEA,MAAM,oCAAoB,IAAI,IAAI;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;AAED,SAAS,eAAe,MAAc,MAAkC;CACvE,MAAM,UAAU,aAAa;CAC7B,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO,UAAU,MAAM,OAAO;AAC/B;AAEA,SAAS,UAAU,MAAyB,SAAqC;CAChF,OAAO,KAAK,MAAK,QAAO,QAAQ,MAAK,QAAO,QAAQ,OAAO,IAAI,WAAW,GAAG,IAAI,EAAE,CAAC,CAAC;AACtF;;;;;AAMA,SAAS,aAAa,MAAkC;CACvD,IAAI,KAAK,OAAO,QAAQ,OAAO;CAC/B,IAAI,KAAK,MAAK,QAAO,kBAAkB,IAAI,GAAG,CAAC,GAAG,OAAO;CAEzD,OAAO,KAAK,QAAO,QAAO,CAAC,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC,WAAW;AAC5D;;AAGA,MAAM,yCAAyB,IAAI,IAAI;CACtC;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;AAiBD,SAAgB,eAAe,MAAyC;CACvE,IAAI;CACJ,IAAI,IAAI;CACR,OAAO,IAAI,KAAK,QAAQ;EACvB,MAAM,MAAM,KAAK;EACjB,IAAI,QAAQ,MAAM;GAEjB,MAAM,OAAO,KAAK,IAAI;GACtB,IAAI,QAAQ,MACX,eAAe,gBAAgB,QAAQ,WAAW,IAAI,IAAI,OAAO,KAAK,cAAc,IAAI;GAEzF,KAAK;GAEL;EACD;EACA,IAAI,uBAAuB,IAAI,GAAG,GAAG;GACpC,KAAK;GAEL;EACD;EACA,IAAI,IAAI,WAAW,GAAG,GAAG;GACxB,KAAK;GAEL;EACD;EACA;CACD;CACA,MAAM,OAAO,KAAK,MAAM;CACxB,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC;CAgB7B,OAAO;EAAQ;EAAM,UAdpB,CAAC,eAAe,MAAM,IAAI,MACzB,cAAc,IAAI,IAAI,MACrB,oBAAoB,KAAK,EAAE,IAAI,KAAK,MAAM,EAAE,KAAK,UACjD,SAAS,YAAY,aAAa,IAAI,KAItC,SAAS,YAAY,CAAC,uBAAuB,IAAI,KAAK,MAAM,EAAE,KAE9D,SAAS,kBACT,CAAC,KAAK,SAAS,IAAI,KACnB,CAAC,KAAK,SAAS,UAAU,KACzB,KAAK,QAAO,MAAK,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC,SAAS;EAER,YAAY,KAAK;EAAU;EAAoB;CAAa;AACtG;;;;;;AAOA,MAAa,wCAAuD,IAAI,IAAI;CAC3E;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;AAOD,MAAM,kCAAkB,IAAI,IAAI;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;;;;;;AAYD,MAAM,gBAAuE;CAC5E,QAAQ;EAAC;EAAQ;EAAS;EAAS;CAAU;CAC7C,IAAI;EAAC;EAAQ;EAAS;EAAS;CAAU;CACzC,QAAQ,CAAC,QAAQ,OAAO;CACxB,UAAU;EAAC;EAAQ;EAAS;CAAO;CACnC,QAAQ;EAAC;EAAQ;EAAS;CAAO;CACjC,OAAO;EAAC;EAAQ;EAAS;EAAS;CAAU;CAC5C,OAAO;EAAC;EAAQ;EAAS;EAAS;EAAY;CAAO;CACrD,QAAQ;EAAC;EAAQ;EAAS;EAAS;EAAY;CAAQ;CACvD,eAAe;EAAC;EAAQ;EAAS;EAAS;EAAY;CAAY;CAClE,QAAQ;EAAC;EAAQ;EAAS;EAAS;EAAY;CAAQ;CACvD,MAAM;EAAC;EAAQ;EAAS;EAAS;EAAW;EAAQ;EAAe;EAAY;EAAS;CAAQ;CAChG,KAAK,CAAC,OAAO;CACb,IAAI,CAAC,OAAO;CACZ,IAAI,CAAC,OAAO;CACZ,SAAS,CAAC,OAAO;CACjB,OAAO,CAAC,OAAO;CACf,aAAa,CAAC,OAAO;CACrB,gBAAgB,CAAC,OAAO;CACxB,OAAO,CAAC,OAAO;CACf,OAAO,CAAC,SAAS,OAAO;CACxB,cAAc;EAAC;EAAQ;EAAS;EAAW;EAAQ;CAAO;CAC1D,QAAQ;EAAC;EAAS;EAAW;CAAM;CACnC,gBAAgB;EAAC;EAAQ;EAAS;EAAW;CAAM;CACnD,aAAa;EAAC;EAAS;EAAW;CAAM;CACxC,KAAK,CAAC,MAAM;CAEZ,OAAO;EAAC;EAAS;EAAW;EAAQ;CAAa;CACjD,MAAM,CAAC,SAAS;CAChB,QAAQ,CAAC,WAAW,QAAQ;CAC5B,QAAQ;EAAC;EAAU;EAAS;CAAS;CACrC,OAAO,CAAC,OAAO;AAChB;;;;;;AAOA,MAAM,sBAAyC;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AACA,MAAM,sBAAmD;CAAC;CAAU;CAAS;CAAW;AAAM;;;;;;;AAQ9F,SAAgB,iBAAiB,YAAgC,YAA0C;CAC1G,IAAI,cAAc,CAAC,YAAY,OAAO;CACtC,IAAI,WAAW,WAAW,OAAO,GAAG,OAAO,WAAW,WAAW,eAAe;CAEhF,OAAO,WAAW,SAAS,GAAG;AAC/B;AAEA,MAAM,kBAA+C,CAAC,aAAa,OAAO;AAC1E,MAAM,eAA4C,CAAC;;;;;;;AAgBnD,SAAgB,qBAAqB,MAAqD;CACzF,MAAM,UAAU,eAAe,IAAI;CACnC,IAAI,QAAQ,YAAY,gBAAgB,IAAI,QAAQ,IAAI,GAAG,OAAO,KAAA;CAIlE,IAAI;CACJ,IAAI,QAAQ,SAAS,YACpB,UAAU;MACJ,IAAI,QAAQ,SAAS,YAAY,UAAU,QAAQ,MAAM,mBAAmB,GAClF,UAAU;MAEV,UAAU,cAAc,QAAQ,SAAS;CAE1C,OAAO;EACG;EACT,cAAc,QAAQ;EACtB,eAAe,QAAQ,QAAO,MAAK,sBAAsB,IAAI,CAAC,CAAC;CAChE;AACD"}
@@ -172,7 +172,7 @@ export interface GitOptions {
172
172
  env?: Record<string, string | undefined>;
173
173
  /** Dynamic environment provider called for each command (e.g., host-discovered SCM credentials) */
174
174
  getEnvironment?: () => Record<string, string | undefined> | undefined;
175
- /** Git command timeout in milliseconds. Defaults to 60000. Set to 0 to disable. */
175
+ /** Git command timeout in milliseconds, for runs that don't set their own. Defaults to 60000. Set to 0 to disable. */
176
176
  gitTimeout?: number;
177
177
  /** Returns whether the workspace is currently trusted. Defaults to true when not provided. Called on every exec/stream. */
178
178
  isTrusted?: () => boolean;
@@ -228,6 +228,10 @@ export declare class Git {
228
228
  private readonly pendingCommands;
229
229
  /** Queue for throttling background git operations */
230
230
  private readonly _queue;
231
+ /** Orders the index-writing runs of each checkout, so they don't race for its `index.lock` */
232
+ private readonly _indexWrites;
233
+ /** The spawn behind every non-streaming run — a field only so tests can substitute it */
234
+ private readonly _runSpawn;
231
235
  /** Detects event-loop stalls while at least one git command is in flight */
232
236
  private readonly _eventLoopMonitor;
233
237
  /** Count of git commands currently in flight, across both the exec and streaming paths */
@@ -246,9 +250,11 @@ export declare class Git {
246
250
  * to its own `notifyChanged` from its constructor — including when it's handed a pre-built `Git` via
247
251
  * `options.git`, so a host-supplied instance gets bound too. With nothing bound, `notify` is a no-op.
248
252
  * `repoPaths` holds more than one path only for a `-C`-scoped shared-state verb: the `-C` target, then the
249
- * caller's own `cwd`.
253
+ * caller's own `cwd`. `sharedPaths` names the paths that were not run in but share the repository, which
254
+ * hear only the part of `changes` every worktree sees (the caller's `cwd`, when the verb also changed
255
+ * something local to the `-C` target).
250
256
  */
251
- bindChangeNotifier(fn: (repoPaths: readonly string[], changes: readonly RepositoryChange[]) => void): void;
257
+ bindChangeNotifier(fn: (repoPaths: readonly string[], changes: readonly RepositoryChange[], sharedPaths?: readonly string[]) => void): void;
252
258
  /**
253
259
  * Returns the base environment for git commands. Cached to avoid spreading
254
260
  * process.env (30-100+ keys) on every call. Includes static options.env and
@@ -298,6 +304,12 @@ export declare class Git {
298
304
  private resolveNotifyPath;
299
305
  private runCore;
300
306
  stream(options: GitSpawnOptions, ...args: readonly (string | undefined)[]): AsyncGenerator<string>;
307
+ /**
308
+ * Resolves the git dir (and common dir, for a linked worktree) of `cwd`.
309
+ *
310
+ * Resolves `undefined` when `cwd` is not in a repository: git reported so, or `cwd` doesn't exist. Any
311
+ * other failure (unsafe ownership, a timeout or cancellation, a missing git binary) rejects.
312
+ */
301
313
  rev_parse__git_dir(cwd: string): Promise<{
302
314
  path: string;
303
315
  commonPath?: string;
@@ -7,14 +7,15 @@ import { defer } from "../../utils/promise.js";
7
7
  import { arePathsEqual, dirname as dirname$1, isAbsolute as isAbsolute$1, isDescendant, joinPaths, normalizePath } from "../../utils/path.js";
8
8
  import { GitWarnings, WorkspaceUntrustedError, getGitWarning } from "../../git/errors.js";
9
9
  import { gitFeaturesByVersion } from "../../git/features.js";
10
+ import { getChangesForCommand, leadingCommand } from "./commandClassifier.js";
10
11
  import { CancelledRunError, RunError, endStdin, fsExists, runSpawn } from "../../utils/env/node/exec.js";
11
12
  import { compare, fromString } from "../../utils/version.js";
12
- import { getChangesForCommand, leadingCommand } from "./commandClassifier.js";
13
13
  import { EventLoopMonitor } from "./eventLoopMonitor.js";
14
14
  import { GitQueue, getPrimaryGitCommand, inferGitCommandPriority } from "./gitQueue.js";
15
+ import { KeyedSequencer, isConfigCommand, isIndexWriteCommand, retryWhileConfigLocked, retryWhileIndexLocked } from "./indexLock.js";
15
16
  import { resolve } from "path";
16
- import { spawn } from "child_process";
17
17
  import * as process from "process";
18
+ import { spawn } from "child_process";
18
19
  //#region ../git-cli/src/exec/git.ts
19
20
  const slowCallWarningThreshold = 2e3;
20
21
  /** Below this, a queue-wait annotation isn't worth adding to the log line. */
@@ -418,6 +419,10 @@ var Git = class {
418
419
  pendingCommands = /* @__PURE__ */ new Map();
419
420
  /** Queue for throttling background git operations */
420
421
  _queue;
422
+ /** Orders the index-writing runs of each checkout, so they don't race for its `index.lock` */
423
+ _indexWrites = new KeyedSequencer();
424
+ /** The spawn behind every non-streaming run — a field only so tests can substitute it */
425
+ _runSpawn = runSpawn;
421
426
  /** Detects event-loop stalls while at least one git command is in flight */
422
427
  _eventLoopMonitor = new EventLoopMonitor();
423
428
  /** Count of git commands currently in flight, across both the exec and streaming paths */
@@ -440,7 +445,9 @@ var Git = class {
440
445
  * to its own `notifyChanged` from its constructor — including when it's handed a pre-built `Git` via
441
446
  * `options.git`, so a host-supplied instance gets bound too. With nothing bound, `notify` is a no-op.
442
447
  * `repoPaths` holds more than one path only for a `-C`-scoped shared-state verb: the `-C` target, then the
443
- * caller's own `cwd`.
448
+ * caller's own `cwd`. `sharedPaths` names the paths that were not run in but share the repository, which
449
+ * hear only the part of `changes` every worktree sees (the caller's `cwd`, when the verb also changed
450
+ * something local to the `-C` target).
444
451
  */
445
452
  bindChangeNotifier(fn) {
446
453
  this._changeNotifier = fn;
@@ -601,10 +608,7 @@ var Git = class {
601
608
  const cwd = normalizePath(options.cwd);
602
609
  if (command.pathOverride != null && cwd !== ranIn && (command.sharedChanges.length || !command.changes.length)) {
603
610
  if (command.sharedChanges.length === command.changes.length) this._changeNotifier([ranIn, cwd], command.changes);
604
- else {
605
- this._changeNotifier([ranIn], command.changes);
606
- this._changeNotifier([cwd], command.sharedChanges);
607
- }
611
+ else this._changeNotifier([ranIn], command.changes, [cwd]);
608
612
  return;
609
613
  }
610
614
  this._changeNotifier([ranIn], command.changes);
@@ -617,10 +621,10 @@ var Git = class {
617
621
  const startEpochMs = Date.now();
618
622
  gitCommand = `[${options.cwd}] ${gitCommand}`;
619
623
  const { cancellation, configs, correlationKey, errors: errorHandling, encoding, expectedExitCodes, runLocally: _, selfMaintenance, slownessCategory, ...opts } = options;
620
- const defaultTimeout = this.options.gitTimeout ?? 6e4;
624
+ const runTimeout = opts.timeout ?? this.options.gitTimeout ?? 6e4;
621
625
  const runOpts = {
622
626
  ...opts,
623
- timeout: opts.timeout === 0 || defaultTimeout === 0 ? void 0 : opts.timeout ?? defaultTimeout,
627
+ timeout: runTimeout === 0 ? void 0 : runTimeout,
624
628
  encoding: (encoding ?? "utf8") === "utf8" ? "utf8" : "buffer",
625
629
  decode: this.options.decode,
626
630
  env: this.buildEnv(options.env),
@@ -639,15 +643,19 @@ var Git = class {
639
643
  cwd: options.cwd,
640
644
  promise
641
645
  });
646
+ const writesIndex = isIndexWriteCommand(args);
647
+ const checkoutKey = writesIndex && options.cwd != null && runOpts.env?.GIT_INDEX_FILE == null ? this.resolveNotifyPath(options.cwd, leadingCommand(args).pathOverride) : void 0;
642
648
  args.unshift("-c", "core.quotepath=false", "-c", "color.ui=false", ...configs ?? emptyArray);
643
649
  if (process.platform === "win32") args.unshift("-c", "core.longpaths=true");
644
650
  if (cancellation != null) runOpts.cancellation = cancellation;
645
651
  const priority = options.priority ?? inferGitCommandPriority(args);
646
652
  const gitPath = await this.path();
647
- this._queue.run(priority, () => {
653
+ const spawnQueued = () => this._queue.run(priority, () => {
648
654
  execStart = hrtime();
649
- return runSpawn(gitPath, args, encoding ?? "utf8", runOpts);
650
- }, cancellation).then(deferred.fulfill, (e) => deferred.cancel(e instanceof Error ? e : new Error(String(e)))).finally(() => {
655
+ return this._runSpawn(gitPath, args, encoding ?? "utf8", runOpts);
656
+ }, cancellation);
657
+ const spawnWithRetry = writesIndex ? () => retryWhileIndexLocked(spawnQueued, cancellation) : isConfigCommand(args) ? () => retryWhileConfigLocked(spawnQueued, cancellation) : spawnQueued;
658
+ (checkoutKey != null ? this._indexWrites.run(checkoutKey, cancellation, spawnWithRetry) : spawnWithRetry()).then(deferred.fulfill, (e) => deferred.cancel(e instanceof Error ? e : new Error(String(e)))).finally(() => {
651
659
  if (this.pendingCommands.get(cacheKey)?.promise === deferred.promise) this.pendingCommands.delete(cacheKey);
652
660
  }).catch(() => {});
653
661
  } else {
@@ -858,11 +866,24 @@ var Git = class {
858
866
  }
859
867
  return void cleanup();
860
868
  }
869
+ /**
870
+ * Resolves the git dir (and common dir, for a linked worktree) of `cwd`.
871
+ *
872
+ * Resolves `undefined` when `cwd` is not in a repository: git reported so, or `cwd` doesn't exist. Any
873
+ * other failure (unsafe ownership, a timeout or cancellation, a missing git binary) rejects.
874
+ */
861
875
  async rev_parse__git_dir(cwd) {
862
- const result = await this.run({
863
- cwd,
864
- errors: "ignore"
865
- }, "rev-parse", "--git-dir", "--git-common-dir");
876
+ let result;
877
+ try {
878
+ result = await this.run({
879
+ cwd,
880
+ errors: "throw"
881
+ }, "rev-parse", "--git-dir", "--git-common-dir");
882
+ } catch (ex) {
883
+ if (ex instanceof GitError && GitWarnings.notARepository.test(ex.stderr ?? "")) return void 0;
884
+ if (!await fsExists(cwd)) return void 0;
885
+ throw ex;
886
+ }
866
887
  if (!result.stdout) return void 0;
867
888
  let [dotGitPath, commonDotGitPath] = result.stdout.split("\n").map((r) => r.trimStart());
868
889
  if (!isAbsolute$1(dotGitPath)) dotGitPath = joinPaths(cwd, dotGitPath);
@@ -913,7 +934,7 @@ var Git = class {
913
934
  errors: "ignore"
914
935
  }, "rev-parse", "--is-bare-repository");
915
936
  if (result.stdout.trim() === "true") {
916
- const result = await this.rev_parse__git_dir(cwd);
937
+ const result = await this.rev_parse__git_dir(cwd).catch(() => void 0);
917
938
  const repoPath = result?.commonPath ?? result?.path;
918
939
  if (repoPath?.length) return [true, repoPath];
919
940
  }