@gitkraken/core-gitlens 0.10.7 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/dist/git/cache.d.ts +34 -2
  3. package/dist/git/cache.js +53 -7
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/context.d.ts +1 -1
  6. package/dist/git/models/repository.d.ts +3 -2
  7. package/dist/git/models/repository.js +5 -4
  8. package/dist/git/models/repository.js.map +1 -1
  9. package/dist/git/providers/branches.d.ts +22 -0
  10. package/dist/git/providers/config.d.ts +3 -1
  11. package/dist/git/providers/provider.d.ts +13 -1
  12. package/dist/git/providers/staging.d.ts +1 -1
  13. package/dist/git/service.d.ts +17 -1
  14. package/dist/git/service.js +59 -8
  15. package/dist/git/service.js.map +1 -1
  16. package/dist/git/watching/changeEvent.d.ts +48 -5
  17. package/dist/git/watching/changeEvent.js +82 -7
  18. package/dist/git/watching/changeEvent.js.map +1 -1
  19. package/dist/git/watching/classifyChange.d.ts +13 -2
  20. package/dist/git/watching/classifyChange.js +40 -5
  21. package/dist/git/watching/classifyChange.js.map +1 -1
  22. package/dist/git/watching/gitIgnoreFilter.d.ts +57 -10
  23. package/dist/git/watching/gitIgnoreFilter.js +186 -29
  24. package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
  25. package/dist/git/watching/initWatcher.d.ts +5 -1
  26. package/dist/git/watching/initWatcher.js +1 -1
  27. package/dist/git/watching/initWatcher.js.map +1 -1
  28. package/dist/git/watching/provider.d.ts +33 -8
  29. package/dist/git/watching/watchGroup.d.ts +13 -2
  30. package/dist/git/watching/watchGroup.js +60 -14
  31. package/dist/git/watching/watchGroup.js.map +1 -1
  32. package/dist/git/watching/watchService.d.ts +42 -1
  33. package/dist/git/watching/watchService.js +172 -20
  34. package/dist/git/watching/watchService.js.map +1 -1
  35. package/dist/git/watching/watchSession.d.ts +50 -17
  36. package/dist/git/watching/watchSession.js +274 -83
  37. package/dist/git/watching/watchSession.js.map +1 -1
  38. package/dist/git/watching/watcherPatterns.d.ts +13 -2
  39. package/dist/git/watching/watcherPatterns.js +22 -2
  40. package/dist/git/watching/watcherPatterns.js.map +1 -1
  41. package/dist/git/watching/worktreeSummary.d.ts +121 -0
  42. package/dist/git/watching/worktreeSummary.js +427 -0
  43. package/dist/git/watching/worktreeSummary.js.map +1 -0
  44. package/dist/git-cli/cliGitProvider.d.ts +6 -1
  45. package/dist/git-cli/cliGitProvider.js +87 -2
  46. package/dist/git-cli/cliGitProvider.js.map +1 -1
  47. package/dist/git-cli/providers/blame.js +1 -1
  48. package/dist/git-cli/providers/branches.d.ts +32 -1
  49. package/dist/git-cli/providers/branches.js +195 -0
  50. package/dist/git-cli/providers/branches.js.map +1 -1
  51. package/dist/git-cli/providers/commits.js +1 -1
  52. package/dist/git-cli/providers/config.d.ts +3 -0
  53. package/dist/git-cli/providers/config.js +12 -6
  54. package/dist/git-cli/providers/config.js.map +1 -1
  55. package/dist/git-cli/providers/contributors.js +1 -1
  56. package/dist/git-cli/providers/graph.js +1 -1
  57. package/dist/git-cli/providers/maintenance.js +1 -1
  58. package/dist/git-cli/providers/refs.js +1 -1
  59. package/dist/git-cli/providers/staging.js +1 -1
  60. package/dist/git-cli/providers/status.js +1 -1
  61. package/dist/git-cli/service.js +1 -0
  62. package/dist/git-cli/service.js.map +1 -1
  63. package/dist/ipc/ipcServer.js +1 -1
  64. package/dist/plus/agents/providers/gkAgentProvider.js +2 -2
  65. package/dist/plus/git-github/api/github.d.ts +1 -1
  66. package/dist/plus/git-github/api/github.js +1 -1
  67. package/dist/plus/git-github/providers/github/blame.js +1 -1
  68. package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
  69. package/dist/plus/integrations/context.d.ts +1 -1
  70. package/dist/plus/integrations/integrationService.js +1 -1
  71. package/dist/plus/integrations/models/integration.js +1 -1
  72. package/dist/plus/integrations/providers/bitbucket-server.d.ts +1 -1
  73. package/dist/utils/event.d.ts +1 -1
  74. package/package.json +1 -1
@@ -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 * as path from 'node:path';\nimport type { CachedGitTypes } 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 { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { joinPaths, maybeUri, normalizePath, splitPath } from '@gitlens/utils/path.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 } 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\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\tgetGlobalExcludesPath:\n\t\t\t\t\tthis.config.getConfig != null\n\t\t\t\t\t\t? async () => this.config.getConfig(repoPath, 'core.excludesFile')\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t\tthis._cache.gitIgnore.set(repoPath, filter);\n\t\t}\n\t\treturn filter;\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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFA,IAAa,iBAAb,MAAmD;CAClD,aAA6C;EAAE,IAAI;EAAO,MAAM;EAAO,SAAS;CAAM;CAEtF;CACA;CACA;CAEA;;CAEA;CACA;CAEA,YAAY,SAAgC;EAK3C,MAAM,YAAY,QAAQ,QAAQ;EAClC,KAAK,aAAa;EAClB,KAAK,UAAU;GACd,GAAG,QAAQ;GACX,OAAO;IACN,GAAG;IACH,OAAO;KACN,GAAG,WAAW;KACd,UAAU,UAAU,GAAG,UAAU;MAChC,KAAK,OAAO,YAAY,UAAU,GAAG,KAAK;MAC1C,KAAK,KAAK,qBAAqB,KAAK,mBAAmB,QAAQ,CAAC;MAChE,WAAW,OAAO,UAAU,UAAU,GAAG,KAAK;KAC/C;IACD;GACD;EACD;EACA,KAAK,qBAAqB,QAAQ,qBAAqB;EACvD,KAAK,OACJ,QAAQ,OACR,IAAI,IAAI,QAAQ,SAAS;GACxB,GAAG,QAAQ;GACX,WACC,QAAQ,QAAQ,WAAW,aAAa,aAC/B,QAAQ,QAAQ,UAAW,YACjC,KAAA;EACL,CAAC;EACF,KAAK,cAAc,EAAE,QAAQ,iBAAiB;EAC9C,KAAK,SAAS,QAAQ,iBAAiB,QAAQ,QAAQ,QAAQ,IAAI,MAAM;EAIzE,KAAK,KAAK,oBAAoB,WAAW,YAAY,KAAK,kBAAkB,WAAW,OAAO,CAAC;CAChG;;CAGA,IAAI,QAAe;EAClB,OAAO,KAAK;CACb;;CAGA,IAAI,MAAW;EACd,OAAO,KAAK;CACb;;CAGA,SAAS,SAAkD;EAC1D,OAAO,KAAK,KAAK,SAAS,OAAO;CAClC;;CAGA,UAAwC,SAA0E;EACjH,OAAO,KAAK,KAAK,UAAU,OAAO;CACnC;CAKA,MAAM,eACL,SACA,iBACA,QACgB;EAChB,OAAO,OAAO,oBAAoB,aAC/B,KAAK,KAAK,eAAe,SAAS,eAAe,IACjD,KAAK,KAAK,eAAe,SAAS,iBAAiB,MAAO;CAC9D;;CAGA,MAAM,UAA2B;EAChC,OAAO,KAAK,KAAK,QAAQ;CAC1B;;CAGA,MAAM,OAAwB;EAC7B,OAAO,KAAK,KAAK,KAAK;CACvB;CAEA,MAAM,MACL,KACA,YACA,SACA,YAC8B;EAC9B,IAAI;EACJ,IAAI,SAAS,YACZ,aAAa,UAAU,YAAY,QAAQ,UAAU;OAC/C;GACN,MAAM,KAAK,cAAc,kBAAkB,GAAG;GAC9C,MAAM,aAAa,WAAW,MAAM,GAAG,CAAC,CAAC,IAAI;GAC7C,IAAI,CAAC,YAAY,OAAO,KAAA;GAExB,IAAI,QAAQ;GACZ,aAAa,UAAU,YAAY,UAAU;GAC7C,OAAQ,MAAM,SAAS,UAAU,KAAM,QAAQ,IAAI;IAClD;IACA,aAAa,UAAU,YAAY,GAAG,WAAW,GAAG,OAAO;GAC5D;EACD;EAGA,MAAM,KAAK,KAAK,IAAI;GAAE,KAAK;GAAY,QAAQ;GAAS,GAAG;EAAW,GAAG,SAAS,KAAK,UAAU;EAEjG,OAAO;CACR;;CAGA,MAAM,KAAK,YAAoB,SAAqE;EACnG,MAAMA,SAAG,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;EAE9C,MAAM,OAAO,CAAC,MAAM;EACpB,IAAI,SAAS,eACZ,KAAK,KAAK,MAAM,QAAQ,aAAa;EAEtC,IAAI,SAAS,MACZ,KAAK,KAAK,QAAQ;EAKnB,MAAM,KAAK,KAAK,IAAI,EAAE,KAAK,WAAW,GAAG,GAAG,IAAI;CACjD;;;;;;;;;;;;;CAcA,cACC,UACA,SACA,SACO;EACP,KAAK,kBAAkB,CAAC,QAAQ,GAAG,SAAS,OAAO;CACpD;;;;;;;CAQA,kBACC,WACA,SACA,SACO;EACP,MAAM,SAA0D,CAAC;EACjE,MAAM,+BAAe,IAAI,IAAY;EACrC,KAAK,MAAM,YAAY,WAAW;GACjC,MAAM,QAAQ,KAAK,gBAAgB,UAAU,SAAS,SAAS,KAAK;GACpE,IAAI,SAAS,MAAM;IAElB,IAAI,SAAS,SAAS,QAAQ,QAAQ,SAAS,UAAU,GACxD,KAAK,MAAM,QAAQ,KAAK,mBAAmB,QAAQ,GAClD,aAAa,IAAI,IAAI;IAGvB;GACD;GAEA,OAAO,KAAK;IAAY;IAAiB;GAAM,CAAC;GAChD,KAAK,MAAM,QAAQ,KAAK,mBAAmB,QAAQ,GAClD,aAAa,IAAI,IAAI;EAEvB;EAEA,IAAI,aAAa,MAChB,KAAK,KAAK,qBAAqB,CAAC,GAAG,YAAY,CAAC;EAEjD,KAAK,MAAM,EAAE,UAAU,WAAW,QACjC,KAAK,YAAY,OAAO,UAAU,UAAU,GAAG,KAAK;EAErD,KAAK,MAAM,YAAY,WACtB,KAAK,QAAQ,OAAO,YAAY,YAAY,UAAU,CAAC,GAAG,OAAO,CAAC;CAEpE;;CAGA,gBACC,UACA,SACA,OAC+B;EAC/B,IAAI,SAAS,QAAQ,UAAU,OAAO;GACrC,IAAI,CAAC,MAAM,QAAQ,OAAO,KAAA;GAE1B,KAAK,OAAO,YAAY,UAAU,GAAG,KAAK;GAC1C,OAAO,CAAC,GAAG,KAAK;EACjB;EAEA,IAAI,UAAU,SAAS,CAAC,QAAQ,UAAU,QAAQ,MAAK,MAAK,MAAM,aAAa,MAAM,QAAQ,GAAG;GAC/F,KAAK,OAAO,YAAY,QAAQ;GAChC,OAAO,CAAC;EACT;EAEA,MAAM,QAAQ,KAAK,OAAO,uBAAuB,UAAU,SAAS,OAAO;EAC3E,OAAO,MAAM,SAAS,QAAQ,KAAA;CAC/B;;CAGA,mBAA2B,UAA4B;EACtD,MAAM,aAAa,KAAK,OAAO,cAAc,QAAQ;EACrD,OAAO,CAAC,mBAAG,IAAI,IAAI;GAAC;GAAU;GAAY,GAAG,KAAK,OAAO,iBAAiB,UAAU;EAAC,CAAC,CAAC;CACxF;CAEA;CACA,IAAI,QAA6B;EAChC,OAAQ,KAAK,WAAW,IAAI,oBAC3B,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,WAAmC;EACtC,OAAQ,KAAK,cAAc,IAAI,uBAC9B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,UAAiC;EACpC,OAAQ,KAAK,aAAa,IAAI,sBAC7B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,SAA+B;EAClC,OAAQ,KAAK,YAAY,IAAI,qBAC5B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,eAA2C;EAC9C,OAAQ,KAAK,kBAAkB,IAAI,2BAClC,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,OAA2B;EAC9B,OAAQ,KAAK,UAAU,IAAI,mBAC1B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,QAA6B;EAChC,OAAQ,KAAK,WAAW,IAAI,oBAC3B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CAIA,IAAI,cAAqD;EACxD,IAAI,CAAC,KAAK,oBAAoB,OAAO,KAAA;EAErC,OAAQ,KAAK,iBAAiB,IAAI,0BACjC,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,MAAgC;EACnC,OAAQ,KAAK,SAAS,IAAI,yBACzB,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,QAA6B;EAChC,OAAQ,KAAK,WAAW,IAAI,oBAC3B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,YAA4C;EAC/C,OAAQ,KAAK,eAAe,IAAI,+BAC/B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,OAA2B;EAC9B,OAAQ,KAAK,UAAU,IAAI,mBAC1B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,UAAiC;EACpC,OAAQ,KAAK,aAAa,IAAI,sBAC7B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,WAAmC;EACtC,OAAQ,KAAK,cAAc,IAAI,uBAC9B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,UAAiC;EACpC,OAAQ,KAAK,aAAa,IAAI,sBAC7B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,QAA6B;EAChC,OAAQ,KAAK,WAAW,IAAI,oBAC3B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,SAA+B;EAClC,OAAQ,KAAK,YAAY,IAAI,qBAC5B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,OAA2B;EAC9B,OAAQ,KAAK,UAAU,IAAI,mBAC1B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA;CACA,IAAI,YAAqC;EACxC,OAAQ,KAAK,eAAe,IAAI,wBAC/B,KAAK,SACL,KAAK,MACL,KAAK,QACL,IACD;CACD;CAEA,eAAe,WAAyB,MAAyB;EAChE,MAAM,UAAU,OAAO,SAAS,WAAW,OAAO,QAAQ,IAAI;EAC9D,IAAI,OAAO,cAAc,UAAU,OAAO;EAC1C,IAAI,SAAS,SAAS,GAAG,OAAO,SAAS,SAAS;EAClD,IAAI,KAAK,WAAW,SAAS,GAAG,OAAO,QAAQ,SAAS;EACxD,OAAO,YAAY,SAAS,SAAS;CACtC;CAEA,gBAAgB,WAAyB,MAA4B;EACpE,MAAM,CAAC,gBAAgB,UAAU,SAAS,SAAS,GAAG,SAAS,IAAI,CAAC;EACpE,OAAO;CACR;CAEA,MAAM,mBAAmB,UAAkB,MAA6B;EACvE,MAAM,SAAS,KAAK,gBAAgB,QAAQ;EAC5C,MAAM,OAAO,MAAM;EACnB,OAAO,KAAK,QAAO,QAAO;GACzB,MAAM,eAAe,cAAc,KAAK,SAAS,UAAU,SAAS,GAAG,CAAC,CAAC;GACzE,OAAO,CAAC,gBAAgB,CAAC,OAAO,UAAU,YAAY;EACvD,CAAC;CACF;CAEA,MAAM,qBAAqB,UAAkD;EAC5E,MAAM,SAAS,KAAK,gBAAgB,QAAQ;EAC5C,MAAM,OAAO,MAAM;EACnB,QAAQ,QAAa;GACpB,MAAM,eAAe,cAAc,KAAK,SAAS,UAAU,SAAS,GAAG,CAAC,CAAC;GACzE,OAAO,eAAe,OAAO,UAAU,YAAY,IAAI;EACxD;CACD;CAEA,MAAM,wBAAwB,UAA+C;EAC5E,OAAO,KAAK,OAAO,wBAAwB,UAAU,OAAO,eAA4C;GACvG,MAAM,SAAS,MAAM,KAAK,OAAO,YAAY,UAAU;GACvD,IAAI,UAAU,MAAM,OAAO,KAAA;GAE3B,MAAM,YAAY,OAAO,aAAa,OAAO;GAC7C,MAAM,eAAe,YAAY,WAAW,YAAY;GACxD,MAAM,QAAQ,MAAM,KAAK,QAAQ,GAAG,KAAK,YAAY;GAErD,IAAI,SAAS,QAAQ,MAAM,OAAO,GAAG,OAAO,MAAM;EAGnD,CAAC;CACF;CAEA,gBAAgB,UAAmC;EAClD,IAAI,SAAS,KAAK,OAAO,UAAU,IAAI,QAAQ;EAC/C,IAAI,UAAU,MAAM;GAEnB,MAAM,aADS,KAAK,OAAO,OAAO,IAAI,QACd,CAAC,EAAE,IAAI,UAAU,GAAG,SAAS;GACrD,SAAS,IAAI,gBAAgB;IAClB;IACE;IACZ,IAAI,KAAK,QAAQ;IACjB,uBACC,KAAK,OAAO,aAAa,OACtB,YAAY,KAAK,OAAO,UAAU,UAAU,mBAAmB,IAC/D,KAAA;GACL,CAAC;GACD,KAAK,OAAO,UAAU,IAAI,UAAU,MAAM;EAC3C;EACA,OAAO;CACR;CAEA,MAAc,qBACb,UACA,UACA,KACuF;EACvF,IAAI,QAAA,6CAA0B,OAAO,KAAA;EAErC,MAAM,QAAQ,gBAAgB;EAE9B,IAAI;GACH,MAAM,CAAC,cAAc,QAAQ,UAAU,UAAU,QAAQ;GAGzD,IAAI,UAAU,MAAM,KAAK,SAAS,OAAO,MAAM,YAAY;GAC3D,IAAI,SAAS,OAAO,EAAE,MAAM,aAAa;GAGzC,IAAI,OAAO,CAAC,cAAc,GAAG,GAAG;IAC/B,UAAU,MAAM,KAAK,SAAS,OAAO,MAAM,cAAc,GAAG;IAC5D,IAAI,CAAC,SAEJ,UAAU,MAAM,KAAK,SAAS,OAAO,MAAM,cAAc,GAAG,IAAI,EAAE;GAEpE;GAEA,OAAO,UAAU,EAAE,MAAM,aAAa,IAAI,KAAA;EAC3C,SAAS,IAAI;GACZ,OAAO,MAAM,EAAE;GACf;EACD;CACD;CAEA,UAAgB;EACf,KAAK,KAAK,QAAQ;EAClB,IAAI,KAAK,aACR,KAAK,OAAO,QAAQ;CAEtB;AACD"}
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,12 +1,12 @@
1
1
  import { first } from "../../utils/iterable.js";
2
2
  import { defer, isPromise } from "../../utils/promise.js";
3
+ import { Emitter } from "../../utils/event.js";
3
4
  import { fileUri, joinUriPath } from "../../utils/uri.js";
4
5
  import { isAbsolute, joinPaths, normalizePath, splitPath } from "../../utils/path.js";
5
6
  import { uncommitted } from "../../git/models/revision.js";
6
7
  import { isUncommittedStaged } from "../../git/utils/revision.utils.js";
7
8
  import { shouldEvictBlameCacheEntry } from "../../git/cache.js";
8
9
  import { BlameIgnoreRevsFileBadRevisionError, BlameIgnoreRevsFileError } from "../../git/errors.js";
9
- import { Emitter } from "../../utils/event.js";
10
10
  import { fsExists } from "../../utils/env/node/exec.js";
11
11
  import { GitErrors } from "../exec/git.js";
12
12
  import { GitFileIndexStatus } from "../../git/models/fileStatus.js";
@@ -5,7 +5,7 @@ import { ConflictDetectionResult } from "../../git/models/mergeConflicts.js";
5
5
  import { GitServiceContext } from "../../git/context.js";
6
6
  import { GitCommandPriority } from "../../git/run.types.js";
7
7
  import { BranchSortOptions } from "../../git/utils/sorting.js";
8
- import { BranchContributionsOverview, GitBranchMergedStatus, GitBranchesSubProvider } from "../../git/providers/branches.js";
8
+ import { AheadOfBase, BranchContributionsOverview, GitBranchMergedStatus, GitBranchesSubProvider } from "../../git/providers/branches.js";
9
9
  import { Cache, GkConfigInvalidationTarget } from "../../git/cache.js";
10
10
  import { Git } from "../exec/git.js";
11
11
  import { CliGitProviderInternal } from "../cliGitProvider.js";
@@ -99,6 +99,37 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
99
99
  * caches the former forever and must not cache the latter.
100
100
  */
101
101
  private getBaseBranchFromReflog;
102
+ getAheadOfBase(repoPath: string, branch: string, cancellation?: AbortSignal): Promise<AheadOfBase>;
103
+ /** Counts `range` with a fresh `rev-list`, bypassing the cached count. Rejects on any failure, with git's error */
104
+ private getLeftRightCountUncached;
105
+ /** Whether HEAD names a branch with no commit yet. Rejects when that can't be read, so a failure isn't taken for an empty branch */
106
+ private isUnbornHead;
107
+ /**
108
+ * Whether `ref` names `branch` itself: the same name, or `<remote>/<branch>` for one of the repo's actual remotes.
109
+ * Splitting at the first slash instead would call a local `release/main` the same branch as `main`.
110
+ */
111
+ private isSelfRef;
112
+ /** Whether `ref` names `branch` itself (see {@link isSelfRef}) and sits at HEAD's commit; tips are read only after the name matched */
113
+ private isSelfAtHead;
114
+ /**
115
+ * The bases recorded against a branch, most trusted first: the user's merge target, the detected one, then the
116
+ * base a launch recorded. Only the stored value of the latter — `getBaseBranchName` would fall back to a reflog
117
+ * guess, which on a long-lived checkout names whatever branch was last checked out from. A failed read is absent.
118
+ */
119
+ private getRecordedBaseCandidates;
120
+ /**
121
+ * The repo's default branch from local refs only: the `origin/HEAD` symref as its local name, else
122
+ * `init.defaultBranch`, else `main`, else `master` — each only if it names a commit.
123
+ */
124
+ private getLocalDefaultBranchName;
125
+ /**
126
+ * A candidate base as the ref HEAD is measured against: the candidate or its tracking ref, whichever forked
127
+ * nearer HEAD, so a stale local base defers to its upstream while one carrying unpushed work wins over it. A
128
+ * candidate neither forks from is used as-is if it names a commit. `undefined` when the candidate is unusable.
129
+ */
130
+ private getNearestForkTarget;
131
+ /** The merge base of HEAD and `ref`, preferring git's fork-point reading of `ref`'s reflog when it has one */
132
+ private getForkPoint;
102
133
  /**
103
134
  * The stored merge target — the user's pick, else the one detected from its PR — skipping any whose branch git
104
135
  * confirms is gone so resolution falls through to the next source. A target that couldn't be checked is kept.
@@ -976,6 +976,188 @@ var BranchesGitSubProvider = class {
976
976
  failed: false
977
977
  };
978
978
  }
979
+ async getAheadOfBase(repoPath, branch, cancellation) {
980
+ if (isDetachedHead(branch)) return {
981
+ base: void 0,
982
+ dependsOn: []
983
+ };
984
+ const dependsOn = /* @__PURE__ */ new Set([`refs/heads/${branch}`]);
985
+ let target;
986
+ for (const candidate of await this.getRecordedBaseCandidates(repoPath, branch)) {
987
+ target = await this.getNearestForkTarget(repoPath, branch, candidate, dependsOn, cancellation);
988
+ if (target != null) break;
989
+ }
990
+ if (target == null) {
991
+ const defaultBranch = await this.getLocalDefaultBranchName(repoPath, dependsOn, cancellation);
992
+ target = await this.getNearestForkTarget(repoPath, branch, defaultBranch, dependsOn, cancellation);
993
+ }
994
+ if (cancellation?.aborted) throw new CancellationError();
995
+ if (target == null) return {
996
+ base: void 0,
997
+ dependsOn: [...dependsOn]
998
+ };
999
+ if (await this.isSelfAtHead(repoPath, target, branch, cancellation)) return {
1000
+ base: void 0,
1001
+ dependsOn: [...dependsOn]
1002
+ };
1003
+ const range = createRevisionRange(target, "HEAD", "...");
1004
+ let counts = await this.provider.commits.getLeftRightCommitCount(repoPath, range, void 0, cancellation);
1005
+ if (counts == null) try {
1006
+ counts = await this.getLeftRightCountUncached(repoPath, range, cancellation);
1007
+ } catch (ex) {
1008
+ if (isCancellationError(ex)) throw ex;
1009
+ if (await this.isUnbornHead(repoPath, cancellation)) return {
1010
+ base: void 0,
1011
+ dependsOn: [...dependsOn]
1012
+ };
1013
+ throw ex;
1014
+ }
1015
+ return {
1016
+ base: {
1017
+ ref: target,
1018
+ ahead: counts.right
1019
+ },
1020
+ dependsOn: [...dependsOn]
1021
+ };
1022
+ }
1023
+ /** Counts `range` with a fresh `rev-list`, bypassing the cached count. Rejects on any failure, with git's error */
1024
+ async getLeftRightCountUncached(repoPath, range, cancellation) {
1025
+ const result = await this.git.run({
1026
+ cwd: repoPath,
1027
+ cancellation,
1028
+ errors: "throw"
1029
+ }, "rev-list", "--left-right", "--count", range, "--");
1030
+ if (result.completion.status === "cancelled" || cancellation?.aborted) throw new CancellationError();
1031
+ const parts = result.stdout.split(" ");
1032
+ const left = parseInt(parts[0], 10);
1033
+ const right = parseInt(parts[1], 10);
1034
+ if (parts.length !== 2 || isNaN(left) || isNaN(right)) throw new Error(`Unexpected output counting the commits in '${range}': ${result.stdout}`);
1035
+ return {
1036
+ left,
1037
+ right
1038
+ };
1039
+ }
1040
+ /** Whether HEAD names a branch with no commit yet. Rejects when that can't be read, so a failure isn't taken for an empty branch */
1041
+ async isUnbornHead(repoPath, cancellation) {
1042
+ return (await this.git.run({
1043
+ cwd: repoPath,
1044
+ cancellation,
1045
+ errors: "throw",
1046
+ expectedExitCodes: [1]
1047
+ }, "rev-parse", "--verify", "--quiet", "HEAD")).exitCode === 1;
1048
+ }
1049
+ /**
1050
+ * Whether `ref` names `branch` itself: the same name, or `<remote>/<branch>` for one of the repo's actual remotes.
1051
+ * Splitting at the first slash instead would call a local `release/main` the same branch as `main`.
1052
+ */
1053
+ async isSelfRef(repoPath, ref, branch, cancellation) {
1054
+ if (ref === branch) return true;
1055
+ const suffix = `/${branch}`;
1056
+ if (!ref.endsWith(suffix)) return false;
1057
+ const remoteName = ref.slice(0, -suffix.length);
1058
+ if (!remoteName) return false;
1059
+ try {
1060
+ return (await this.provider.remotes.getRemotes(repoPath, void 0, cancellation)).some((r) => r.name === remoteName);
1061
+ } catch (ex) {
1062
+ if (isCancellationError(ex)) throw ex;
1063
+ return false;
1064
+ }
1065
+ }
1066
+ /** Whether `ref` names `branch` itself (see {@link isSelfRef}) and sits at HEAD's commit; tips are read only after the name matched */
1067
+ async isSelfAtHead(repoPath, ref, branch, cancellation) {
1068
+ if (!await this.isSelfRef(repoPath, ref, branch, cancellation)) return false;
1069
+ const [refSha, headSha] = await Promise.all([this.provider.refs.validateReference(repoPath, ref, void 0, cancellation), this.provider.refs.validateReference(repoPath, "HEAD", void 0, cancellation)]);
1070
+ return refSha != null && refSha === headSha;
1071
+ }
1072
+ /**
1073
+ * The bases recorded against a branch, most trusted first: the user's merge target, the detected one, then the
1074
+ * base a launch recorded. Only the stored value of the latter — `getBaseBranchName` would fall back to a reflog
1075
+ * guess, which on a long-lived checkout names whatever branch was last checked out from. A failed read is absent.
1076
+ */
1077
+ async getRecordedBaseCandidates(repoPath, branch) {
1078
+ const tolerate = async (read) => {
1079
+ try {
1080
+ return (await read())?.trim() || void 0;
1081
+ } catch (ex) {
1082
+ if (isCancellationError(ex)) throw ex;
1083
+ return;
1084
+ }
1085
+ };
1086
+ const reads = await Promise.all([
1087
+ tolerate(() => this.getStoredUserMergeTargetBranchName(repoPath, branch)),
1088
+ tolerate(() => this.getStoredDetectedMergeTargetBranchName(repoPath, branch)),
1089
+ tolerate(() => this.provider.config.getGkConfig(repoPath, `branch.${branch}.gk-merge-base`))
1090
+ ]);
1091
+ const candidates = [];
1092
+ for (const candidate of reads) if (candidate && !candidates.includes(candidate)) candidates.push(candidate);
1093
+ return candidates;
1094
+ }
1095
+ /**
1096
+ * The repo's default branch from local refs only: the `origin/HEAD` symref as its local name, else
1097
+ * `init.defaultBranch`, else `main`, else `master` — each only if it names a commit.
1098
+ */
1099
+ async getLocalDefaultBranchName(repoPath, dependsOn, cancellation) {
1100
+ dependsOn.add("refs/remotes/origin/HEAD");
1101
+ const symref = await this.getDefaultBranchName(repoPath, "origin", { local: true }, cancellation);
1102
+ if (symref) {
1103
+ const slash = symref.indexOf("/");
1104
+ const local = slash >= 0 ? symref.slice(slash + 1) : symref;
1105
+ if (local) {
1106
+ addRefDependencies(dependsOn, local);
1107
+ if (await this.provider.refs.validateReference(repoPath, local, void 0, cancellation)) return local;
1108
+ }
1109
+ }
1110
+ let fromConfig;
1111
+ try {
1112
+ fromConfig = (await this.provider.config.getConfig(repoPath, "init.defaultBranch"))?.trim() || void 0;
1113
+ } catch {}
1114
+ for (const candidate of [
1115
+ fromConfig,
1116
+ "main",
1117
+ "master"
1118
+ ]) {
1119
+ if (!candidate) continue;
1120
+ addRefDependencies(dependsOn, candidate);
1121
+ if (await this.provider.refs.validateReference(repoPath, candidate, void 0, cancellation)) return candidate;
1122
+ }
1123
+ }
1124
+ /**
1125
+ * A candidate base as the ref HEAD is measured against: the candidate or its tracking ref, whichever forked
1126
+ * nearer HEAD, so a stale local base defers to its upstream while one carrying unpushed work wins over it. A
1127
+ * candidate neither forks from is used as-is if it names a commit. `undefined` when the candidate is unusable.
1128
+ */
1129
+ async getNearestForkTarget(repoPath, branch, candidate, dependsOn, cancellation) {
1130
+ if (!candidate || candidate.startsWith("-")) return void 0;
1131
+ if (candidate === branch) return void 0;
1132
+ addRefDependencies(dependsOn, candidate);
1133
+ const getUpstreamFork = async () => {
1134
+ const upstream = await this.provider.refs.getSymbolicReferenceName(repoPath, `${candidate}@{u}`, void 0, cancellation);
1135
+ if (!upstream || upstream.startsWith("-")) return void 0;
1136
+ addRefDependencies(dependsOn, upstream);
1137
+ return {
1138
+ ref: upstream,
1139
+ mergeBase: await this.getForkPoint(repoPath, upstream, cancellation)
1140
+ };
1141
+ };
1142
+ const [candidateMergeBase, upstreamFork] = await Promise.all([this.getForkPoint(repoPath, candidate, cancellation), getUpstreamFork()]);
1143
+ let nearest;
1144
+ for (const fork of [{
1145
+ ref: candidate,
1146
+ mergeBase: candidateMergeBase
1147
+ }, upstreamFork]) {
1148
+ if (fork?.mergeBase == null) continue;
1149
+ if (nearest == null || nearest.mergeBase === fork.mergeBase || await this.provider.commits.isAncestorOf(repoPath, nearest.mergeBase, fork.mergeBase, cancellation)) nearest = {
1150
+ ref: fork.ref,
1151
+ mergeBase: fork.mergeBase
1152
+ };
1153
+ }
1154
+ if (nearest != null) return nearest.ref;
1155
+ return await this.provider.refs.validateReference(repoPath, candidate, void 0, cancellation) ? candidate : void 0;
1156
+ }
1157
+ /** The merge base of HEAD and `ref`, preferring git's fork-point reading of `ref`'s reflog when it has one */
1158
+ async getForkPoint(repoPath, ref, cancellation) {
1159
+ return await this.provider.refs.getMergeBase(repoPath, ref, "HEAD", { forkPoint: true }, cancellation) ?? this.provider.refs.getMergeBase(repoPath, "HEAD", ref, void 0, cancellation);
1160
+ }
979
1161
  /**
980
1162
  * The stored merge target — the user's pick, else the one detected from its PR — skipping any whose branch git
981
1163
  * confirms is gone so resolution falls through to the next source. A target that couldn't be checked is kept.
@@ -1151,6 +1333,7 @@ __decorate([debug()], BranchesGitSubProvider.prototype, "getPotentialApplyConfli
1151
1333
  __decorate([debug()], BranchesGitSubProvider.prototype, "getPotentialMergeConflicts", null);
1152
1334
  __decorate([debug()], BranchesGitSubProvider.prototype, "getPotentialStashReapplyConflicts", null);
1153
1335
  __decorate([debug({ exit: true })], BranchesGitSubProvider.prototype, "getBaseBranchName", null);
1336
+ __decorate([debug({ exit: true })], BranchesGitSubProvider.prototype, "getAheadOfBase", null);
1154
1337
  __decorate([debug({ exit: true })], BranchesGitSubProvider.prototype, "getStoredMergeTargetBranchName", null);
1155
1338
  __decorate([debug({ exit: true })], BranchesGitSubProvider.prototype, "getStoredDetectedMergeTargetBranchName", null);
1156
1339
  __decorate([debug()], BranchesGitSubProvider.prototype, "getStoredUserMergeTargetBranchName", null);
@@ -1162,6 +1345,18 @@ __decorate([debug()], BranchesGitSubProvider.prototype, "setUpstreamBranch", nul
1162
1345
  __decorate([debug()], BranchesGitSubProvider.prototype, "storeBaseBranchName", null);
1163
1346
  __decorate([debug()], BranchesGitSubProvider.prototype, "storeMergeTargetBranchName", null);
1164
1347
  __decorate([debug()], BranchesGitSubProvider.prototype, "storeUserMergeTargetBranchName", null);
1348
+ /**
1349
+ * Adds the full ref names a `.git/refs` watcher would report for `name`. A short name can't be told local from
1350
+ * remote without a spawn, so it adds both forms; a spurious one only costs a recompute.
1351
+ */
1352
+ function addRefDependencies(dependsOn, name) {
1353
+ if (name.startsWith("refs/")) {
1354
+ dependsOn.add(name);
1355
+ return;
1356
+ }
1357
+ dependsOn.add(`refs/heads/${name}`);
1358
+ dependsOn.add(`refs/remotes/${name}`);
1359
+ }
1165
1360
  //#endregion
1166
1361
  export { BranchesGitSubProvider };
1167
1362