@gitkraken/core-gitlens 0.7.2 → 0.9.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 (192) hide show
  1. package/CHANGELOG.md +72 -1
  2. package/dist/git/cache.d.ts +29 -5
  3. package/dist/git/cache.js +52 -11
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/context.d.ts +6 -1
  6. package/dist/git/context.js.map +1 -1
  7. package/dist/git/errors.d.ts +28 -1
  8. package/dist/git/errors.js +45 -1
  9. package/dist/git/errors.js.map +1 -1
  10. package/dist/git/features.d.ts +1 -1
  11. package/dist/git/features.js +1 -0
  12. package/dist/git/features.js.map +1 -1
  13. package/dist/git/models/reflog.d.ts +6 -0
  14. package/dist/git/models/reflog.js.map +1 -1
  15. package/dist/git/models/revision.d.ts +0 -1
  16. package/dist/git/models/revision.js +1 -2
  17. package/dist/git/models/revision.js.map +1 -1
  18. package/dist/git/providers/branches.d.ts +5 -1
  19. package/dist/git/providers/commits.d.ts +44 -2
  20. package/dist/git/providers/config.d.ts +7 -1
  21. package/dist/git/providers/operations.d.ts +19 -0
  22. package/dist/git/providers/provider.d.ts +29 -2
  23. package/dist/git/providers/refs.d.ts +48 -3
  24. package/dist/git/providers/revision.d.ts +7 -1
  25. package/dist/git/providers/staging.d.ts +13 -0
  26. package/dist/git/providers/stash.d.ts +3 -2
  27. package/dist/git/providers/status.d.ts +4 -0
  28. package/dist/git/providers/worktrees.d.ts +23 -3
  29. package/dist/git/run.types.d.ts +22 -0
  30. package/dist/git-cli/cliGitProvider.d.ts +39 -2
  31. package/dist/git-cli/cliGitProvider.js +109 -11
  32. package/dist/git-cli/cliGitProvider.js.map +1 -1
  33. package/dist/git-cli/exec/commandClassifier.d.ts +31 -0
  34. package/dist/git-cli/exec/commandClassifier.js +391 -0
  35. package/dist/git-cli/exec/commandClassifier.js.map +1 -0
  36. package/dist/git-cli/exec/git.d.ts +36 -5
  37. package/dist/git-cli/exec/git.js +163 -28
  38. package/dist/git-cli/exec/git.js.map +1 -1
  39. package/dist/git-cli/exec/gitQueue.d.ts +4 -1
  40. package/dist/git-cli/exec/gitQueue.js +1 -1
  41. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  42. package/dist/git-cli/parsers/reflogParser.d.ts +7 -0
  43. package/dist/git-cli/parsers/reflogParser.js +11 -1
  44. package/dist/git-cli/parsers/reflogParser.js.map +1 -1
  45. package/dist/git-cli/providers/branches.d.ts +10 -1
  46. package/dist/git-cli/providers/branches.js +31 -22
  47. package/dist/git-cli/providers/branches.js.map +1 -1
  48. package/dist/git-cli/providers/commits.d.ts +27 -3
  49. package/dist/git-cli/providers/commits.js +112 -12
  50. package/dist/git-cli/providers/commits.js.map +1 -1
  51. package/dist/git-cli/providers/config.js +2 -1
  52. package/dist/git-cli/providers/config.js.map +1 -1
  53. package/dist/git-cli/providers/diff.js +4 -4
  54. package/dist/git-cli/providers/diff.js.map +1 -1
  55. package/dist/git-cli/providers/operations.d.ts +13 -0
  56. package/dist/git-cli/providers/operations.js +64 -5
  57. package/dist/git-cli/providers/operations.js.map +1 -1
  58. package/dist/git-cli/providers/patch.js +21 -45
  59. package/dist/git-cli/providers/patch.js.map +1 -1
  60. package/dist/git-cli/providers/refs.d.ts +20 -3
  61. package/dist/git-cli/providers/refs.js +140 -10
  62. package/dist/git-cli/providers/refs.js.map +1 -1
  63. package/dist/git-cli/providers/remotes.js +3 -2
  64. package/dist/git-cli/providers/remotes.js.map +1 -1
  65. package/dist/git-cli/providers/revision.d.ts +5 -1
  66. package/dist/git-cli/providers/revision.js +20 -5
  67. package/dist/git-cli/providers/revision.js.map +1 -1
  68. package/dist/git-cli/providers/staging.d.ts +9 -0
  69. package/dist/git-cli/providers/staging.js +63 -6
  70. package/dist/git-cli/providers/staging.js.map +1 -1
  71. package/dist/git-cli/providers/stash.d.ts +3 -2
  72. package/dist/git-cli/providers/stash.js +34 -22
  73. package/dist/git-cli/providers/stash.js.map +1 -1
  74. package/dist/git-cli/providers/status.d.ts +2 -0
  75. package/dist/git-cli/providers/status.js +27 -9
  76. package/dist/git-cli/providers/status.js.map +1 -1
  77. package/dist/git-cli/providers/worktrees.d.ts +31 -2
  78. package/dist/git-cli/providers/worktrees.js +75 -7
  79. package/dist/git-cli/providers/worktrees.js.map +1 -1
  80. package/dist/plus/git-github/api/github.d.ts +64 -3
  81. package/dist/plus/git-github/api/github.js +237 -24
  82. package/dist/plus/git-github/api/github.js.map +1 -1
  83. package/dist/plus/git-github/providers/github/branches.d.ts +3 -1
  84. package/dist/plus/git-github/providers/github/branches.js +2 -1
  85. package/dist/plus/git-github/providers/github/branches.js.map +1 -1
  86. package/dist/plus/git-github/providers/github/commits.d.ts +4 -2
  87. package/dist/plus/git-github/providers/github/commits.js +3 -1
  88. package/dist/plus/git-github/providers/github/commits.js.map +1 -1
  89. package/dist/plus/git-github/providers/github/refs.d.ts +10 -3
  90. package/dist/plus/git-github/providers/github/refs.js +4 -4
  91. package/dist/plus/git-github/providers/github/refs.js.map +1 -1
  92. package/dist/plus/git-github/providers/github/revision.d.ts +4 -1
  93. package/dist/plus/git-github/providers/github/revision.js +6 -1
  94. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  95. package/dist/plus/integrations/index.d.ts +5 -2
  96. package/dist/plus/integrations/index.js.map +1 -1
  97. package/dist/plus/integrations/integrationService.d.ts +35 -9
  98. package/dist/plus/integrations/integrationService.js +16 -3
  99. package/dist/plus/integrations/integrationService.js.map +1 -1
  100. package/dist/plus/integrations/manager.d.ts +107 -37
  101. package/dist/plus/integrations/models/gitHostIntegration.d.ts +128 -13
  102. package/dist/plus/integrations/models/gitHostIntegration.js +130 -17
  103. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  104. package/dist/plus/integrations/models/integration.d.ts +51 -3
  105. package/dist/plus/integrations/models/integration.js +125 -18
  106. package/dist/plus/integrations/models/integration.js.map +1 -1
  107. package/dist/plus/integrations/models/issuesIntegration.d.ts +18 -5
  108. package/dist/plus/integrations/models/issuesIntegration.js +40 -10
  109. package/dist/plus/integrations/models/issuesIntegration.js.map +1 -1
  110. package/dist/plus/integrations/providers/azure/azure.d.ts +1 -1
  111. package/dist/plus/integrations/providers/azure/azure.js +13 -17
  112. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  113. package/dist/plus/integrations/providers/azure/models.d.ts +29 -0
  114. package/dist/plus/integrations/providers/azure/models.js +112 -1
  115. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  116. package/dist/plus/integrations/providers/azureDevOps.d.ts +54 -2
  117. package/dist/plus/integrations/providers/azureDevOps.js +80 -2
  118. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  119. package/dist/plus/integrations/providers/azureDevOpsServer.js +2 -1
  120. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  121. package/dist/plus/integrations/providers/bitbucket/bitbucket.d.ts +61 -0
  122. package/dist/plus/integrations/providers/bitbucket/bitbucket.js +165 -23
  123. package/dist/plus/integrations/providers/bitbucket/bitbucket.js.map +1 -1
  124. package/dist/plus/integrations/providers/bitbucket/models.d.ts +6 -1
  125. package/dist/plus/integrations/providers/bitbucket/models.js +4 -3
  126. package/dist/plus/integrations/providers/bitbucket/models.js.map +1 -1
  127. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js +1 -1
  128. package/dist/plus/integrations/providers/bitbucket-server/pullRequestSearch.js.map +1 -1
  129. package/dist/plus/integrations/providers/bitbucket-server.d.ts +38 -1
  130. package/dist/plus/integrations/providers/bitbucket-server.js +33 -1
  131. package/dist/plus/integrations/providers/bitbucket-server.js.map +1 -1
  132. package/dist/plus/integrations/providers/bitbucket.d.ts +36 -1
  133. package/dist/plus/integrations/providers/bitbucket.js +29 -1
  134. package/dist/plus/integrations/providers/bitbucket.js.map +1 -1
  135. package/dist/plus/integrations/providers/github.d.ts +48 -4
  136. package/dist/plus/integrations/providers/github.js +101 -6
  137. package/dist/plus/integrations/providers/github.js.map +1 -1
  138. package/dist/plus/integrations/providers/gitlab/gitlab.d.ts +35 -0
  139. package/dist/plus/integrations/providers/gitlab/gitlab.js +132 -15
  140. package/dist/plus/integrations/providers/gitlab/gitlab.js.map +1 -1
  141. package/dist/plus/integrations/providers/gitlab/models.d.ts +1 -0
  142. package/dist/plus/integrations/providers/gitlab/models.js +1 -1
  143. package/dist/plus/integrations/providers/gitlab/models.js.map +1 -1
  144. package/dist/plus/integrations/providers/gitlab.d.ts +48 -1
  145. package/dist/plus/integrations/providers/gitlab.js +79 -3
  146. package/dist/plus/integrations/providers/gitlab.js.map +1 -1
  147. package/dist/plus/integrations/providers/jira-server.d.ts +3 -3
  148. package/dist/plus/integrations/providers/jira-server.js +3 -3
  149. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  150. package/dist/plus/integrations/providers/models.d.ts +12 -1
  151. package/dist/plus/integrations/providers/models.js +11 -1
  152. package/dist/plus/integrations/providers/models.js.map +1 -1
  153. package/dist/plus/integrations/providers/providerErrors.d.ts +9 -1
  154. package/dist/plus/integrations/providers/providerErrors.js +10 -1
  155. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  156. package/dist/plus/integrations/providers/providersApi.d.ts +59 -0
  157. package/dist/plus/integrations/providers/providersApi.js +136 -1
  158. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  159. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +42 -0
  160. package/dist/plus/integrations/providers/utils/providerPaging.js +58 -1
  161. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  162. package/dist/plus/integrations/providers/utils.d.ts +1 -1
  163. package/dist/plus/integrations/reads/currentAccount.d.ts +35 -0
  164. package/dist/plus/integrations/reads/currentAccount.js +46 -0
  165. package/dist/plus/integrations/reads/currentAccount.js.map +1 -0
  166. package/dist/plus/integrations/reads/drains.d.ts +4 -1
  167. package/dist/plus/integrations/reads/drains.js +7 -3
  168. package/dist/plus/integrations/reads/drains.js.map +1 -1
  169. package/dist/plus/integrations/reads/issueBatch.d.ts +72 -12
  170. package/dist/plus/integrations/reads/issueBatch.js +193 -48
  171. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  172. package/dist/plus/integrations/reads/pullRequestBatch.d.ts +57 -0
  173. package/dist/plus/integrations/reads/pullRequestBatch.js +80 -0
  174. package/dist/plus/integrations/reads/pullRequestBatch.js.map +1 -0
  175. package/dist/plus/integrations/reads/pullRequestBranches.d.ts +58 -0
  176. package/dist/plus/integrations/reads/pullRequestBranches.js +111 -0
  177. package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -0
  178. package/dist/plus/integrations/reads/pullRequests.js +3 -3
  179. package/dist/plus/integrations/reads/pullRequests.js.map +1 -1
  180. package/dist/plus/integrations/reads/sweeps.js +3 -3
  181. package/dist/plus/integrations/reads/sweeps.js.map +1 -1
  182. package/dist/plus/integrations/results.d.ts +5 -2
  183. package/dist/plus/integrations/results.js.map +1 -1
  184. package/dist/utils/env/node/exec.js +1 -1
  185. package/dist/utils/env/node/exec.js.map +1 -1
  186. package/docs/integrations.md +90 -36
  187. package/docs/kepler-git-write-api-parity.md +365 -0
  188. package/docs/kepler-read-api-parity.md +75 -28
  189. package/package.json +1 -1
  190. package/dist/plus/integrations/reads/trackerIssue.d.ts +0 -33
  191. package/dist/plus/integrations/reads/trackerIssue.js +0 -60
  192. package/dist/plus/integrations/reads/trackerIssue.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":["process_env","fs"],"sources":["../../../../git-cli/src/providers/config.ts"],"sourcesContent":["import { promises as fs } from 'fs';\nimport { hostname, userInfo } from 'os';\nimport { env as process_env } from 'process';\nimport type { Cache, GkConfigInvalidationTarget, GkReconcileOutcome } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { WorkspaceUntrustedError } from '@gitlens/git/errors.js';\nimport type { GitDir } from '@gitlens/git/models/repository.js';\nimport type { SigningConfig, SigningFormat, ValidationResult } from '@gitlens/git/models/signature.js';\nimport type { GitUser } from '@gitlens/git/models/user.js';\nimport type {\n\tDeprecatedGkConfigKeys,\n\tGitConfigKeys,\n\tGitConfigSubProvider,\n\tGitConfigType,\n\tGkConfigKeys,\n} from '@gitlens/git/providers/config.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport { first, some } from '@gitlens/utils/iterable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { dirname, getBestPath, isAbsolute, joinPaths, normalizePath } from '@gitlens/utils/path.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport { fsExists } from '../exec/exec.js';\nimport type { Git } from '../exec/git.js';\n\nconst mappedAuthorRegex = /(.+)\\s<(.+)>/;\nconst emptyArray: readonly never[] = Object.freeze([]);\n\n/** Catch-all pattern matching every key in `.git/gk/config` for the one-shot bulk read. */\nconst gkConfigAllPattern = '.';\n\n/**\n * Canonicalizes a git config key for lookup against `--get-regexp` output, which git emits with the\n * section and variable-name lowercased and only the subsection case-preserved. Without this a\n * subsection-less key like `gk.defaultRemote` would miss the lowercased `gk.defaultremote` in the map.\n */\nexport function canonicalizeGitConfigKey(key: string): string {\n\tconst first = key.indexOf('.');\n\tif (first === -1) return key.toLowerCase();\n\n\tconst last = key.lastIndexOf('.');\n\t// `section.name` (no subsection) → fully lowercased; `section.subsection.name` → lowercase\n\t// section + name, preserve the case-sensitive subsection in the middle.\n\tif (first === last) return key.toLowerCase();\n\treturn `${key.slice(0, first).toLowerCase()}${key.slice(first, last)}${key.slice(last).toLowerCase()}`;\n}\n\n/**\n * Parses git config --get-regex output into a Map.\n * The output format is \"key value\" per line, where key and value are space-separated.\n *\n * A line with NO space at all is git's rendering of a bareword/valueless entry (`key`, no `=`) — git's\n * OWN config parser reads that as boolean TRUE (git-config(1): \"a variable defined without `= <value>`\n * is taken as true\"), confirmed against git's `format_config` (builtin/config.c): the key-delimiter is\n * appended unconditionally, then backed out ONLY when the stored value is NULL (the bareword case) —\n * an explicit empty value (`key =`) is a real, non-NULL empty string, so its line KEEPS the trailing\n * space and is already handled by the branch below, byte-distinct from a bareword line.\n *\n * By default a bareword line is skipped (the historical behavior, which reads as \"unset\" — wrong, but\n * safe); pass `includeValueless` to map it to the literal string `'true'` instead, so `parseGitBoolean`\n * and callers checking `.has()` for \"configured\" both read it correctly.\n */\nexport function parseConfigRegexOutput(\n\tdata: string | undefined,\n\toptions?: { includeValueless?: boolean },\n): Map<string, string> {\n\tconst configMap = new Map<string, string>();\n\tif (!data) return configMap;\n\n\tfor (const line of data.split('\\n')) {\n\t\tif (!line) continue;\n\n\t\tconst spaceIndex = line.indexOf(' ');\n\t\tif (spaceIndex === -1) {\n\t\t\tif (options?.includeValueless) {\n\t\t\t\tconfigMap.set(line, 'true');\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconfigMap.set(line.substring(0, spaceIndex), line.substring(spaceIndex + 1));\n\t}\n\n\treturn configMap;\n}\n\nexport function parseGitBoolean(value: string | undefined): boolean {\n\tif (value == null) return false;\n\n\tconst normalized = value.toLowerCase().trim();\n\treturn normalized === 'true' || normalized === 'yes' || normalized === 'on' || normalized === '1';\n}\n\n/** Whether a git config value spells boolean-false (`false`/`no`/`off`/`0`/empty), per git's maybe-bool parsing. */\nexport function isGitBooleanFalse(value: string): boolean {\n\tconst normalized = value.toLowerCase().trim();\n\treturn (\n\t\tnormalized === '' || normalized === 'false' || normalized === 'no' || normalized === 'off' || normalized === '0'\n\t);\n}\n\nexport class ConfigGitSubProvider implements GitConfigSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {\n\t\tthis.cache.setGkConfigReconciler((repoPath, priorSnapshot) => this.reconcileGkConfig(repoPath, priorSnapshot));\n\t}\n\n\t/**\n\t * In-flight gk-config reconciles keyed by commonPath. `pending` records that another `'gkConfig'`\n\t * event landed mid-pass, so one trailing pass runs after the current one settles. `paths` holds every\n\t * worktree path that reported the change, mapped to its close generation at that moment — the pass\n\t * reads through one that's still open and notifies all of them.\n\t */\n\tprivate readonly _gkConfigReconciles = new Map<string, { pending: boolean; paths: Map<string, number> }>();\n\n\t/**\n\t * Pending `setGkConfig` writes keyed by `(commonPath, key, value, skip-option)`. Concurrent\n\t * callers writing the same `(key, value)` pair share the in-flight Promise so only one\n\t * `git config --set` subprocess actually runs. Cleared on settle.\n\t */\n\tprivate readonly _pendingGkConfigWrites = new Map<string, Promise<void>>();\n\n\t@trace()\n\tgetConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfig(global ? undefined : repoPath, key, () =>\n\t\t\tthis.getConfigCore(repoPath, key, options),\n\t\t);\n\t}\n\n\tprivate async getConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst args = ['config', '--get'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\tif (options?.type) {\n\t\t\targs.push(`--type=${options.type}`);\n\t\t}\n\t\targs.push(key);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t@trace()\n\tgetConfigRegex(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfigRegex(global ? undefined : repoPath, pattern, () =>\n\t\t\tthis.getConfigRegexCore(repoPath, pattern, options),\n\t\t);\n\t}\n\n\tprivate async getConfigRegexCore(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst args = ['config', '--get-regex'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\targs.push(pattern);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\t// Deliberately NOT `.trim()`-ed: an explicit-empty value's LAST line ends in a meaningful trailing\n\t\t// space, and `.trim()`-ing the whole (possibly multi-line) buffer strips it — `parseConfigRegexOutput`\n\t\t// already skips the blank line a trailing newline produces, so no trim is needed.\n\t\treturn parseConfigRegexOutput(result.stdout);\n\t}\n\n\t@debug()\n\tasync setConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean },\n\t): Promise<void> {\n\t\treturn this.setConfigCore(repoPath, key, value, options);\n\t}\n\n\tprivate async setConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean },\n\t): Promise<void> {\n\t\tconst args: string[] = ['config'];\n\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\targs.push(global ? '--global' : '--local');\n\t\t}\n\n\t\tif (value == null) {\n\t\t\targs.push('--unset', key);\n\t\t} else {\n\t\t\targs.push(key, value);\n\t\t}\n\n\t\tawait this.git.run({ cwd: repoPath ?? '', runLocally: true }, ...args);\n\n\t\t// Only invalidate cache when not using a custom file (custom files aren't cached)\n\t\tif (!options?.file) {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\t// Invalidate the cached value for this key and clear all regex patterns for this scope\n\t\t\tthis.cache.deleteConfig(global ? undefined : repoPath, key);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug()\n\tasync getCurrentUser(repoPath: string): Promise<GitUser | undefined> {\n\t\tif (!repoPath) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst cached = this.cache.currentUser.get(repoPath);\n\t\tif (cached != null) return cached;\n\t\t// If we found null, user data was not found - don't bother trying again\n\t\tif (cached === null) return undefined;\n\n\t\tconst user: GitUser = { name: undefined, email: undefined };\n\n\t\ttry {\n\t\t\tconst configMap = await this.getConfigRegex(repoPath, '^user\\\\.(name|email)$', {\n\t\t\t\trunGitLocally: true,\n\t\t\t});\n\t\t\tif (configMap.size) {\n\t\t\t\tuser.name = configMap.get('user.name');\n\t\t\t\tuser.email = configMap.get('user.email');\n\t\t\t} else {\n\t\t\t\tuser.name =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_NAME || process_env.GIT_COMMITTER_NAME || userInfo()?.username || undefined;\n\t\t\t\tif (!user.name) {\n\t\t\t\t\t// If we found no user data, mark it so we won't bother trying again\n\t\t\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tuser.email =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_EMAIL ||\n\t\t\t\t\tprocess_env.GIT_COMMITTER_EMAIL ||\n\t\t\t\t\tprocess_env.EMAIL ||\n\t\t\t\t\t`${user.name}@${hostname()}`;\n\t\t\t}\n\n\t\t\tconst author = `${user.name} <${user.email}>`;\n\t\t\t// Check if there is a mailmap for the current user\n\t\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'check-mailmap', author);\n\n\t\t\tif (result.stdout && result.stdout !== author) {\n\t\t\t\tconst match = mappedAuthorRegex.exec(result.stdout);\n\t\t\t\tif (match != null) {\n\t\t\t\t\t[, user.name, user.email] = match;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.cache.currentUser.set(repoPath, user);\n\t\t\treturn user;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\n\t\t\t// Mark it so we won't bother trying again\n\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@gate()\n\t@trace({ exit: r => `returned ${r}` })\n\tasync getDefaultWorktreePath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn getBestPath(normalizePath(joinPaths(basePath, '..')));\n\t}\n\n\t@gate()\n\t@trace({\n\t\texit: r =>\n\t\t\t`returned ${r.uri.toString(true)}, commonUri=${r.commonUri?.toString(true)}, parentUri=${r.parentUri?.toString(true)}`,\n\t})\n\tasync getGitDir(repoPath: string): Promise<GitDir> {\n\t\tconst cached = this.cache.gitDir.get(repoPath);\n\t\tif (cached != null) return cached;\n\n\t\tconst scope = getScopedLogger();\n\t\tconst repoInfo = await this.getRepositoryInfo(repoPath);\n\n\t\tlet gitDir: GitDir;\n\t\tif (!Array.isArray(repoInfo) && repoInfo != null) {\n\t\t\tgitDir = {\n\t\t\t\turi: fileUri(repoInfo.gitDir),\n\t\t\t\tcommonUri: repoInfo.commonGitDir ? fileUri(repoInfo.commonGitDir) : undefined,\n\t\t\t\tparentUri: repoInfo.superprojectPath ? fileUri(repoInfo.superprojectPath) : undefined,\n\t\t\t};\n\t\t} else {\n\t\t\tgitDir = {\n\t\t\t\turi: this.provider.getAbsoluteUri(joinPaths(repoPath, '.git'), repoPath),\n\t\t\t};\n\n\t\t\tconst gitDirPath = gitDir.uri.toString(true);\n\t\t\tscope?.warn(`rev-parse failed for '${repoPath}'; falling back to '${gitDirPath}'`);\n\t\t\tthis.context.hooks?.operations?.onGitDirResolveFailed?.(\n\t\t\t\trepoPath,\n\t\t\t\tgitDirPath,\n\t\t\t\t`rev_parse returned ${JSON.stringify(repoInfo)}`,\n\t\t\t);\n\t\t}\n\t\tthis.cache.gitDir.set(repoPath, gitDir);\n\n\t\treturn gitDir;\n\t}\n\n\t/**\n\t * Gets the path to the .git/gk/config file for storing GitKraken-specific metadata.\n\t * Uses commonPath for worktrees so all worktrees share the same data.\n\t */\n\tprivate async getGkConfigPath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\t// Use commonUri (main .git dir) for worktrees, otherwise use uri\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn joinPaths(basePath, 'gk', 'config');\n\t}\n\n\t@debug()\n\tasync getGkConfig(repoPath: string, key: GkConfigKeys | DeprecatedGkConfigKeys): Promise<string | undefined> {\n\t\t// Served in-memory from a single bulk read of the whole `.git/gk/config` (no gk read needs `--type`).\n\t\t// Canonicalize the key the way git does for `--get-regexp` output so subsection-less keys\n\t\t// (e.g. `gk.defaultRemote`) match, and coerce an empty value to undefined to match the old\n\t\t// `git config --get` (`.trim() || undefined`) contract.\n\t\treturn (await this.getGkConfigMap(repoPath)).get(canonicalizeGitConfigKey(key)) || undefined;\n\t}\n\n\t@debug()\n\tasync getGkConfigRegex(repoPath: string, pattern: string): Promise<Map<string, string>> {\n\t\tconst all = await this.getGkConfigMap(repoPath);\n\n\t\t// Filter the bulk map in-memory. The old path passed `pattern` to `git config --get-regex`\n\t\t// (errors ignored → empty result on a bad pattern); preserve that no-throw contract here\n\t\t// rather than letting an invalid-JS pattern reject the read.\n\t\tlet re: RegExp;\n\t\ttry {\n\t\t\tre = new RegExp(pattern);\n\t\t} catch {\n\t\t\treturn new Map();\n\t\t}\n\n\t\tconst result = new Map<string, string>();\n\t\tfor (const [k, v] of all) {\n\t\t\tif (re.test(k)) {\n\t\t\t\tresult.set(k, v);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Reads the entire `.git/gk/config` in one `git config --get-regexp` and caches the parsed map\n\t * per commonPath. Per-key (getGkConfig) and per-namespace (getGkConfigRegex) lookups are served\n\t * from this single cached read — so a branch-overview render that fans out across several gk\n\t * namespaces costs one `git config` call instead of one per namespace.\n\t */\n\tprivate async getGkConfigMap(repoPath: string): Promise<Map<string, string>> {\n\t\t// Migrate BEFORE populating the cache, not inside the factory: a first-time migration calls\n\t\t// clearCaches('gkConfig'), which would otherwise evict the in-flight gkConfigMap entry the\n\t\t// cache wrapper synchronously stored and force a redundant bulk re-read.\n\t\tawait this.migrateGkConfigFromGitConfig(this.cache.getCommonPath(repoPath));\n\t\treturn this.cache.getGkConfigMap(repoPath, async cacheable => {\n\t\t\tconst { map, failed } = await this.getGkConfigRegexCore(repoPath, gkConfigAllPattern);\n\t\t\t// This map has no TTL and everything gk-related reads through it, so a failed read cached as an\n\t\t\t// empty map would report every branch as having no stored base or merge target for the session —\n\t\t\t// and would make the section-cleanup pre-checks skip real work. Only cache a read that happened.\n\t\t\tif (failed) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t}\n\t\t\treturn map;\n\t\t});\n\t}\n\n\tprivate async getGkConfigRegexCore(\n\t\trepoPath: string,\n\t\tpattern: string,\n\t): Promise<{ map: Map<string, string>; failed: boolean }> {\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\t// No `.git/gk/config` yet is a real answer, not a failure — a repo GitLens has never written to.\n\t\tif (!gkConfigPath) return { map: new Map(), failed: false };\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t'config',\n\t\t\t'--get-regex',\n\t\t\t'-f',\n\t\t\tgkConfigPath,\n\t\t\tpattern,\n\t\t);\n\t\t// `--get-regex` exits 1 when nothing matches, which IS an answer (an empty gk config).\n\t\tconst exited = result.completion.status === 'exited';\n\t\treturn {\n\t\t\t// Deliberately NOT `.trim()`-ed — see `getConfigRegexCore`'s comment: it strips the trailing\n\t\t\t// space that marks an explicit-empty value's LAST line, silently dropping it from the map.\n\t\t\tmap: parseConfigRegexOutput(result.stdout),\n\t\t\tfailed: !exited || (result.exitCode !== 0 && result.exitCode !== 1),\n\t\t};\n\t}\n\n\t@debug()\n\tasync setGkConfig(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },\n\t): Promise<void> {\n\t\t// Encode the skip-invalidation option in the dedup key: a self-write (with skip targets)\n\t\t// and a user-driven write of the same `(key, value)` (no skip targets) have different\n\t\t// cache-invalidation semantics, so sharing their Promise would silently let the user-driven\n\t\t// write skip invalidation. Sort the array so callers passing the same targets in different\n\t\t// orders share a single in-flight write.\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst skipKey = options?.skipInvalidation?.length ? options.skipInvalidation.toSorted().join(',') : '';\n\t\tconst dedupKey = `${commonPath}\\0${key}\\0${value ?? ''}\\0${skipKey}`;\n\n\t\tconst pending = this._pendingGkConfigWrites.get(dedupKey);\n\t\tif (pending != null) return pending;\n\n\t\tconst promise = this.setGkConfigCore(repoPath, key, value, options);\n\t\tthis._pendingGkConfigWrites.set(dedupKey, promise);\n\t\t// Swallow the cleanup chain's rejection so a failed write doesn't surface as an unhandled\n\t\t// rejection separate from the caller's own handling of the returned promise.\n\t\tvoid promise.finally(() => this._pendingGkConfigWrites.delete(dedupKey)).catch(() => {});\n\t\treturn promise;\n\t}\n\n\tprivate async setGkConfigCore(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) return;\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;\n\n\t\tawait this.setConfigCore(repoPath, key, value, { file: gkConfigPath });\n\n\t\t// Invalidate the bulk map (so the next read re-fetches) and the derived caches for this key's\n\t\t// ref. The `.git/gk/config` file-watcher also fires `'gkConfig'` shortly after, which reconciles\n\t\t// the same change (see `reconcileGkConfig`) — recording our own write here keeps that later\n\t\t// reconcile from re-diffing this key as an external change.\n\t\tthis.cache.deleteGkConfig(repoPath, key, options);\n\t\tthis.cache.recordGkConfigWrite(repoPath, key, value);\n\t}\n\n\t@debug()\n\tasync removeGkConfigBranchSection(repoPath: string, ref: string): Promise<void> {\n\t\tawait this.runGkConfigSectionCommand(repoPath, ref, '--remove-section', `branch.${ref}`);\n\t}\n\n\t@debug()\n\tasync renameGkConfigBranchSection(repoPath: string, oldRef: string, newRef: string): Promise<void> {\n\t\t// Clear the destination first, unconditionally. `--rename-section` onto a section that already\n\t\t// exists APPENDS rather than replaces, leaving duplicate keys — and git resolves a duplicated key\n\t\t// to its LAST value, so the orphan left by an earlier branch of this name would win over the\n\t\t// metadata being moved. Doing this outside `runGkConfigSectionCommand` also covers the case where\n\t\t// the source has no gk keys at all, where its pre-check would otherwise skip the whole call and\n\t\t// leave the orphan in place for the renamed branch to inherit.\n\t\tawait this.removeGkConfigBranchSection(repoPath, newRef);\n\t\tawait this.runGkConfigSectionCommand(\n\t\t\trepoPath,\n\t\t\toldRef,\n\t\t\t'--rename-section',\n\t\t\t`branch.${oldRef}`,\n\t\t\t`branch.${newRef}`,\n\t\t);\n\t}\n\n\t/**\n\t * Runs a section-level `git config` against `.git/gk/config` for `ref`'s section, skipping the\n\t * subprocess entirely when the bulk map (already cached on any warm path) shows that ref has no gk\n\t * keys — the overwhelmingly common case, since most branches never get gk metadata written at all.\n\t *\n\t * Never throws: callers invoke this as bookkeeping after a branch op has already succeeded, so a\n\t * failure here must not be reported as (or roll back) the branch/checkout/worktree operation itself.\n\t * The worst case is metadata that stays behind until the next op on that name.\n\t */\n\tprivate async runGkConfigSectionCommand(repoPath: string, ref: string, ...args: string[]): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\t\ttry {\n\t\t\tconst prefix = `branch.${ref}.`;\n\t\t\tconst map = await this.getGkConfigMap(repoPath);\n\t\t\t// The shortcut needs a map with CONTENT to be trustworthy. A failed bulk read resolves empty\n\t\t\t// (see `getGkConfigMap`) — the cache refuses it, but this caller still holds it — and an empty map\n\t\t\t// read as authoritative would skip the removal entirely, leaving a deleted branch's base,\n\t\t\t// disposition and issue links behind for the next branch reusing that name. When it's empty, run\n\t\t\t// the command: worst case is a no-op on a section that isn't there.\n\t\t\tif (map.size && !some(map.keys(), k => k.startsWith(prefix))) return;\n\n\t\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\t\tif (!gkConfigPath) return;\n\t\t\t// `getGkConfigPath` only joins a path, so check the file: with no `.git/gk/config` there is\n\t\t\t// genuinely nothing to remove, and running the command would fail with a fatal we'd then warn\n\t\t\t// about on every branch delete in a repo that has never had gk metadata.\n\t\t\tif (!(await fsExists(gkConfigPath))) return;\n\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, runLocally: true, errors: 'ignore' },\n\t\t\t\t'config',\n\t\t\t\t'-f',\n\t\t\t\tgkConfigPath,\n\t\t\t\t...args,\n\t\t\t);\n\t\t\t// `errors: 'ignore'` is deliberate — see the note above on why this must never throw — but that\n\t\t\t// also means the `catch` below never sees a failed write, so without this the metadata silently\n\t\t\t// stays behind (or, on a rename, stays under the old name) with nothing recorded anywhere.\n\t\t\t// Reported, not thrown: the branch op it follows has already succeeded.\n\t\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0) {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`Failed to update gk config section for '${ref}' in '${repoPath}': ${\n\t\t\t\t\t\tresult.completion.status === 'exited'\n\t\t\t\t\t\t\t? `git config exited ${result.exitCode}`\n\t\t\t\t\t\t\t: `${result.completion.status} · ${result.completion.error.message}`\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// A whole section moved or vanished, which the per-key `deleteGkConfig` cascade can't express.\n\t\t\t// Drop the bulk map so the next read hits disk; the derived caches for the affected ref are\n\t\t\t// evicted by the caller's own `deleteBaseBranchName` plus the `'branches'` cascade it fires.\n\t\t\tthis.cache.deleteGkConfigMap(repoPath);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to update gk config section for '${ref}' in '${repoPath}'`);\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles a watcher-observed `'gkConfig'` change — registered with the shared `Cache` as its\n\t * gk-config reconciler (see the constructor). Re-reads the bulk map (hard-evicted by\n\t * `Cache.handleGkConfigChanged`, so this hits disk) and diffs it against `priorSnapshot` to cascade\n\t * `baseBranchName`/`branchOverviews` only for refs whose merge-relevant keys actually changed.\n\t *\n\t * Serialized per commonPath: a reconcile is a read-then-diff against a single shared baseline, so\n\t * two overlapping passes would both diff against the same snapshot and the later change could fall\n\t * out entirely. An event arriving mid-pass instead queues one trailing pass, which re-reads and\n\t * diffs against the baseline the previous pass just established.\n\t *\n\t * `.git/gk/config` lives in the common dir, so the watcher delivers one event per worktree sharing it\n\t * (see `WatchGroup.onCommonEvent`). Siblings never run concurrently — the later ones fold into the\n\t * in-flight pass — but folding in arms the trailing pass, so a fan-out costs one extra read that\n\t * re-reads and diffs an unchanged file. However many siblings land while a pass is running, they\n\t * coalesce into that single trailing pass; the cost is the price of never dropping a real change.\n\t * Every reporting path is retained because `Repository` matches the follow-up change event on exact\n\t * path, and a sibling worktree that never receives one would sit on stale derived values.\n\t */\n\tprivate reconcileGkConfig(\n\t\trepoPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t): GkReconcileOutcome | Promise<GkReconcileOutcome> {\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\n\t\tconst inflight = this._gkConfigReconciles.get(commonPath);\n\t\tif (inflight != null) {\n\t\t\tinflight.paths.set(repoPath, this.cache.getCloseGeneration(repoPath));\n\t\t\tinflight.pending = true;\n\t\t\t// Handed to the in-flight pass's trailing loop, so it IS being reconciled — just not by this\n\t\t\t// call. Reporting a failure here would coarse-clear on every write in a burst.\n\t\t\treturn 'deferred';\n\t\t}\n\n\t\tconst entry = { pending: false, paths: new Map([[repoPath, this.cache.getCloseGeneration(repoPath)]]) };\n\t\tthis._gkConfigReconciles.set(commonPath, entry);\n\t\treturn this.runGkConfigReconcile(commonPath, priorSnapshot, entry);\n\t}\n\n\t/** Drops paths that closed since they reported the change; returns whichever remain. */\n\tprivate pruneClosedReconcilePaths(paths: Map<string, number>): Map<string, number> {\n\t\tfor (const [path, generation] of paths) {\n\t\t\tif (this.cache.getCloseGeneration(path) !== generation) {\n\t\t\t\tpaths.delete(path);\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\tprivate async runGkConfigReconcile(\n\t\tcommonPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t\tentry: { pending: boolean; paths: Map<string, number> },\n\t): Promise<GkReconcileOutcome> {\n\t\tconst scope = getScopedLogger();\n\t\tlet snapshot = priorSnapshot;\n\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tentry.pending = false;\n\n\t\t\t\t// Read through a path that's still open — one worktree closing must not drop the event for\n\t\t\t\t// its still-open siblings, which is why this reads from the surviving set rather than from\n\t\t\t\t// whichever path happened to report first.\n\t\t\t\tconst readPath = first(this.pruneClosedReconcilePaths(entry.paths).keys());\n\t\t\t\t// Every path that reported the change has since closed — nothing left to reconcile, and\n\t\t\t\t// nothing left holding a stale derived value either.\n\t\t\t\tif (readPath == null) return 'reconciled';\n\n\t\t\t\t// A trailing pass must not reuse the entry the pass before it installed.\n\t\t\t\tthis.cache.deleteGkConfigMap(readPath);\n\t\t\t\tconst freshMap = await this.getGkConfigMap(readPath);\n\t\t\t\t// Paths may have closed while the re-read was in flight; if every one did, there's nothing\n\t\t\t\t// left to reconcile against.\n\t\t\t\tif (!this.pruneClosedReconcilePaths(entry.paths).size) return 'reconciled';\n\n\t\t\t\tif (this.cache.reconcileGkConfigMap(readPath, snapshot, freshMap)) {\n\t\t\t\t\t// Fire a follow-up change: a consumer that re-read on the original event may have done so\n\t\t\t\t\t// before this async cascade landed, so it would otherwise hold a stale derived value with\n\t\t\t\t\t// nothing left to prompt another re-read.\n\t\t\t\t\tfor (const path of entry.paths.keys()) {\n\t\t\t\t\t\tthis.context.hooks?.repository?.onChanged?.(path, ['gkConfig']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// `reconcileGkConfigMap` advanced the stored baseline to what we just read; a trailing pass\n\t\t\t\t// diffs its own read against that.\n\t\t\t\tsnapshot = this.cache.getGkConfigMergeSnapshot(readPath);\n\t\t\t} while (entry.pending);\n\n\t\t\treturn 'reconciled';\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to reconcile gk config for '${commonPath}'`);\n\t\t\t// The caller falls back to the coarse clear — derived caches are otherwise left holding\n\t\t\t// pre-change values with nothing to correct them.\n\t\t\treturn 'failed';\n\t\t} finally {\n\t\t\tthis._gkConfigReconciles.delete(commonPath);\n\t\t}\n\t}\n\n\t@debug()\n\tasync getSigningConfig(repoPath: string): Promise<SigningConfig> {\n\t\t// Fetch all signing-related config in one call\n\t\tconst configMap = await this.getConfigRegex(\n\t\t\trepoPath,\n\t\t\t'^(commit\\\\.gpgsign|gpg\\\\.format|user\\\\.signingkey|gpg\\\\.program|gpg\\\\.ssh\\\\.program|gpg\\\\.ssh\\\\.allowedsignersfile)$',\n\t\t);\n\n\t\t// Extract values (keys are lowercase in git output)\n\t\tconst enabledRaw = configMap.get('commit.gpgsign');\n\t\tconst format = configMap.get('gpg.format');\n\t\tconst signingKey = configMap.get('user.signingkey');\n\t\tconst gpgProgram = configMap.get('gpg.program');\n\t\tconst sshProgram = configMap.get('gpg.ssh.program');\n\t\tconst allowedSignersFile = configMap.get('gpg.ssh.allowedsignersfile');\n\n\t\t// Check if git config has commit signing enabled, falling back to the host-level override\n\t\t// (e.g. VS Code's `git.enableCommitSigning`) when `commit.gpgsign` is unset/false. The host\n\t\t// override can only enable signing, never force it off.\n\t\tconst isEnabled = parseGitBoolean(enabledRaw) || this.context.config?.signing?.enabled === true;\n\n\t\treturn {\n\t\t\tenabled: isEnabled,\n\t\t\tformat: (format as SigningFormat) ?? 'gpg',\n\t\t\tsigningKey: signingKey,\n\t\t\tgpgProgram: gpgProgram,\n\t\t\tsshProgram: sshProgram,\n\t\t\tallowedSignersFile: allowedSignersFile,\n\t\t};\n\t}\n\n\t@debug()\n\tasync validateSigningSetup(repoPath: string): Promise<ValidationResult> {\n\t\tconst config = await this.getSigningConfig(repoPath);\n\n\t\tif (!config.signingKey) {\n\t\t\treturn { valid: false, error: 'No signing key configured' };\n\t\t}\n\n\t\t// Basic validation: just check that a signing key is configured\n\t\t// Git will handle the actual validation when signing commits\n\t\treturn { valid: true };\n\t}\n\n\t@debug()\n\tasync setSigningConfig(\n\t\trepoPath: string,\n\t\tconfig: Partial<SigningConfig>,\n\t\toptions?: { global?: boolean },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tif (config.enabled != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'commit.gpgsign', config.enabled ? 'true' : 'false', options);\n\t\t\t}\n\t\t\tif (config.format != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.format', config.format, options);\n\t\t\t}\n\t\t\tif (config.signingKey != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'user.signingkey', config.signingKey, options);\n\t\t\t}\n\t\t\tif (config.gpgProgram != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.program', config.gpgProgram, options);\n\t\t\t}\n\t\t\tif (config.sshProgram != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.ssh.program', config.sshProgram, options);\n\t\t\t}\n\t\t\tif (config.allowedSignersFile != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.ssh.allowedSignersFile', config.allowedSignersFile, options);\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tgetSigningConfigFlags(config: SigningConfig): string[] {\n\t\tconst flags: string[] = [];\n\n\t\tif (config.gpgProgram) {\n\t\t\tflags.push('-c', `gpg.program=${config.gpgProgram}`);\n\t\t}\n\t\tif (config.format && config.format !== 'gpg') {\n\t\t\tflags.push('-c', `gpg.format=${config.format}`);\n\t\t}\n\t\tif (config.sshProgram) {\n\t\t\tflags.push('-c', `gpg.ssh.program=${config.sshProgram}`);\n\t\t}\n\t\tif (config.allowedSignersFile) {\n\t\t\tflags.push('-c', `gpg.ssh.allowedSignersFile=${config.allowedSignersFile}`);\n\t\t}\n\n\t\treturn flags;\n\t}\n\n\tasync getRepositoryInfo(\n\t\tcwd: string,\n\t): Promise<\n\t\t| { repoPath: string; gitDir: string; commonGitDir: string | undefined; superprojectPath: string | undefined }\n\t\t| [safe: true, repoPath: string]\n\t\t| [safe: false]\n\t\t| []\n\t> {\n\t\tlet result;\n\n\t\tif (this.context.workspace?.isTrusted === false) {\n\t\t\ttry {\n\t\t\t\tawait fs.stat(joinPaths(cwd, 'HEAD'));\n\t\t\t\tresult = await this.git.run(\n\t\t\t\t\t{ cwd: cwd, errors: 'throw', configs: ['-C', cwd] },\n\t\t\t\t\t'rev-parse',\n\t\t\t\t\t'--show-cdup',\n\t\t\t\t);\n\t\t\t\tif (!result.stdout.trim()) {\n\t\t\t\t\tLogger.warn(`Skipping (untrusted); bare clone repository detected in '${cwd}'`);\n\t\t\t\t\treturn emptyArray as [];\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throws, we should be good to open the repo\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tresult = await this.git.run(\n\t\t\t\t{ cwd: cwd, errors: 'throw' },\n\t\t\t\t'rev-parse',\n\t\t\t\t'--show-toplevel',\n\t\t\t\t'--git-dir',\n\t\t\t\t'--git-common-dir',\n\t\t\t\t'--show-superproject-working-tree',\n\t\t\t);\n\t\t\tif (!result.stdout) return emptyArray as [];\n\n\t\t\t// Output is 3-4 lines: show-toplevel, git-dir, git-common-dir, [show-superproject-working-tree]\n\t\t\t// The 4th line is only present for submodules\n\t\t\tconst lines = result.stdout.split('\\n').map(r => r.trimStart());\n\t\t\tconst [repoPath, dotGitPath, commonDotGitPath, superprojectPath] = lines;\n\n\t\t\tif (!repoPath) return emptyArray as [];\n\n\t\t\tconst normalizedRepoPath = normalizePath(repoPath.replace(/[\\r|\\n]+$/, ''));\n\n\t\t\tlet gitDir = dotGitPath;\n\t\t\tif (gitDir && !isAbsolute(gitDir)) {\n\t\t\t\tgitDir = joinPaths(cwd, gitDir);\n\t\t\t}\n\t\t\tgitDir = normalizePath(gitDir);\n\n\t\t\tlet commonGitDir: string | undefined;\n\t\t\tif (commonDotGitPath) {\n\t\t\t\tcommonGitDir = commonDotGitPath;\n\t\t\t\tif (!isAbsolute(commonGitDir)) {\n\t\t\t\t\tcommonGitDir = joinPaths(cwd, commonGitDir);\n\t\t\t\t}\n\t\t\t\tcommonGitDir = normalizePath(commonGitDir);\n\t\t\t\tif (commonGitDir === gitDir) {\n\t\t\t\t\tcommonGitDir = undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trepoPath: normalizedRepoPath,\n\t\t\t\tgitDir: gitDir,\n\t\t\t\tcommonGitDir: commonGitDir,\n\t\t\t\tsuperprojectPath: superprojectPath?.trim() || undefined,\n\t\t\t};\n\t\t} catch (ex: any) {\n\t\t\tif (ex instanceof WorkspaceUntrustedError) return emptyArray as [];\n\n\t\t\tconst unsafeMatch =\n\t\t\t\t/(?:^fatal:\\s*detected dubious ownership in repository at '([^']+)'|unsafe repository \\('([^']+)' is owned by someone else\\))[\\s\\S]*(git config --global --add safe\\.directory [^\\n\\u2022]+)/m.exec(\n\t\t\t\t\tex.stderr,\n\t\t\t\t);\n\t\t\tif (unsafeMatch != null) {\n\t\t\t\tLogger.warn(\n\t\t\t\t\t`Skipping; unsafe repository detected in '${unsafeMatch[1] || unsafeMatch[2]}'; run '${\n\t\t\t\t\t\tunsafeMatch[3]\n\t\t\t\t\t}' to allow it`,\n\t\t\t\t);\n\t\t\t\treturn [false];\n\t\t\t}\n\n\t\t\tconst inDotGit = /this operation must be run in a work tree/.test(ex.stderr);\n\t\t\tif (inDotGit && this.context.workspace?.isTrusted !== false) {\n\t\t\t\tresult = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--is-bare-repository');\n\t\t\t\tif (result.stdout.trim() === 'true') {\n\t\t\t\t\tconst result = await this.revParseGitDir(cwd);\n\t\t\t\t\tconst repoPath = result?.commonPath ?? result?.path;\n\t\t\t\t\tif (repoPath?.length) return [true, repoPath];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inDotGit || ex.code === 'ENOENT') {\n\t\t\t\tlet exists = inDotGit ? false : await fsExists(cwd);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tdo {\n\t\t\t\t\t\tconst parent = dirname(cwd);\n\t\t\t\t\t\tif (parent === cwd || parent.length === 0) return emptyArray as [];\n\n\t\t\t\t\t\tcwd = parent;\n\t\t\t\t\t\texists = await fsExists(cwd);\n\t\t\t\t\t} while (!exists);\n\n\t\t\t\t\treturn this.getRepositoryInfo(cwd);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn emptyArray as [];\n\t\t}\n\t}\n\n\tprivate async revParseGitDir(cwd: string): Promise<{ path: string; commonPath?: string } | undefined> {\n\t\tconst result = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--git-dir', '--git-common-dir');\n\t\tif (!result.stdout) return undefined;\n\n\t\tlet [dotGitPath, commonDotGitPath] = result.stdout.split('\\n').map(r => r.trimStart());\n\n\t\tif (!isAbsolute(dotGitPath)) {\n\t\t\tdotGitPath = joinPaths(cwd, dotGitPath);\n\t\t}\n\t\tdotGitPath = normalizePath(dotGitPath);\n\n\t\tif (commonDotGitPath) {\n\t\t\tif (!isAbsolute(commonDotGitPath)) {\n\t\t\t\tcommonDotGitPath = joinPaths(cwd, commonDotGitPath);\n\t\t\t}\n\t\t\tcommonDotGitPath = normalizePath(commonDotGitPath);\n\n\t\t\treturn { path: dotGitPath, commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined };\n\t\t}\n\n\t\treturn { path: dotGitPath };\n\t}\n\n\t/**\n\t * Ensures the `.git/gk/` folder exists, but only if the parent `.git` directory already exists.\n\t * This prevents creating stray `.git` directories if the computed path is wrong.\n\t * Returns `true` if the folder was ensured, `false` if the parent `.git` does not exist.\n\t */\n\tprivate async ensureGkConfigFolder(\n\t\tgkConfigFolder: string,\n\t\tscope: ReturnType<typeof getScopedLogger>,\n\t): Promise<boolean> {\n\t\t// Verify the .git directory already exists — never create it from scratch\n\t\tconst dotGitDir = joinPaths(gkConfigFolder, '..');\n\t\ttry {\n\t\t\tawait fs.stat(dotGitDir);\n\t\t} catch {\n\t\t\tscope?.warn(`Skipping GK config write — expected git directory '${dotGitDir}' does not exist`);\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.mkdir(gkConfigFolder, { recursive: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigFolder}' directory`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate _migratedRepos = new Set<string>();\n\n\t/**\n\t * One-time migration of GK config entries from regular git config to `.git/gk/config`.\n\t * If `.git/gk/config` already exists, assumes migration is complete (or data is already there).\n\t * Preserves existing `.git/gk/config` values as source of truth (won't overwrite).\n\t * Removes migrated keys from regular git config to stop cluttering it.\n\t */\n\t@gate()\n\t@debug()\n\tprivate async migrateGkConfigFromGitConfig(repoPath: string): Promise<void> {\n\t\tif (this._migratedRepos.has(repoPath)) return;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\t// If .git/gk/config already exists, consider migration done — it was either\n\t\t// already migrated or values were written there directly via setGkConfig\n\t\ttry {\n\t\t\tawait fs.stat(gkConfigPath);\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// file doesn't exist, proceed with migration\n\t\t}\n\n\t\t// If .git/gk/config doesn't exist, create an empty file to prevent multiple migration attempts in future sessions\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;\n\n\t\ttry {\n\t\t\tawait fs.writeFile(gkConfigPath, new Uint8Array());\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigPath}' file`);\n\t\t}\n\n\t\t// Read legacy gk-* keys from regular git config\n\t\tlet migrateConfig: Map<string, string>;\n\t\ttry {\n\t\t\tmigrateConfig = await this.getConfigRegexCore(repoPath, '^branch\\\\..*\\\\.gk-', { runGitLocally: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, 'Failed to read legacy GK config entries');\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!migrateConfig.size) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tscope?.info(`Migrating ${String(migrateConfig.size)} GK config entries from git config to .git/gk/config`);\n\n\t\t// Copy legacy entries to .git/gk/config\n\t\tfor (const [key, value] of [...migrateConfig]) {\n\t\t\ttry {\n\t\t\t\tawait this.setConfigCore(repoPath, key, value, { file: gkConfigPath });\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to migrate key '${key}' to GK config`);\n\t\t\t\t// If we failed to migrate, delete it from the list so we won't try to remove it from git config later\n\t\t\t\tmigrateConfig.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Remove legacy keys from regular git config (use --unset-all defensively)\n\t\tfor (const [key] of migrateConfig) {\n\t\t\ttry {\n\t\t\t\tawait this.git.run(\n\t\t\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t\t\t'config',\n\t\t\t\t\t'--local',\n\t\t\t\t\t'--unset-all',\n\t\t\t\t\tkey,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to remove migrated key '${key}' from git config`);\n\t\t\t}\n\t\t}\n\n\t\t// Clear caches since we modified both config files\n\t\tthis.cache.clearCaches(repoPath, 'config', 'gkConfig');\n\n\t\tthis._migratedRepos.add(repoPath);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"config.js","names":["process_env","fs"],"sources":["../../../../git-cli/src/providers/config.ts"],"sourcesContent":["import { promises as fs } from 'fs';\nimport { hostname, userInfo } from 'os';\nimport { env as process_env } from 'process';\nimport type { Cache, GkConfigInvalidationTarget, GkReconcileOutcome } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { WorkspaceUntrustedError } from '@gitlens/git/errors.js';\nimport type { GitDir } from '@gitlens/git/models/repository.js';\nimport type { SigningConfig, SigningFormat, ValidationResult } from '@gitlens/git/models/signature.js';\nimport type { GitUser } from '@gitlens/git/models/user.js';\nimport type {\n\tDeprecatedGkConfigKeys,\n\tGitConfigKeys,\n\tGitConfigSubProvider,\n\tGitConfigType,\n\tGkConfigKeys,\n} from '@gitlens/git/providers/config.js';\nimport { gate } from '@gitlens/utils/decorators/gate.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport { first, some } from '@gitlens/utils/iterable.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { dirname, getBestPath, isAbsolute, joinPaths, normalizePath } from '@gitlens/utils/path.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport { fsExists } from '../exec/exec.js';\nimport type { Git } from '../exec/git.js';\n\nconst mappedAuthorRegex = /(.+)\\s<(.+)>/;\nconst emptyArray: readonly never[] = Object.freeze([]);\n\n/** Catch-all pattern matching every key in `.git/gk/config` for the one-shot bulk read. */\nconst gkConfigAllPattern = '.';\n\n/**\n * Canonicalizes a git config key for lookup against `--get-regexp` output, which git emits with the\n * section and variable-name lowercased and only the subsection case-preserved. Without this a\n * subsection-less key like `gk.defaultRemote` would miss the lowercased `gk.defaultremote` in the map.\n */\nexport function canonicalizeGitConfigKey(key: string): string {\n\tconst first = key.indexOf('.');\n\tif (first === -1) return key.toLowerCase();\n\n\tconst last = key.lastIndexOf('.');\n\t// `section.name` (no subsection) → fully lowercased; `section.subsection.name` → lowercase\n\t// section + name, preserve the case-sensitive subsection in the middle.\n\tif (first === last) return key.toLowerCase();\n\treturn `${key.slice(0, first).toLowerCase()}${key.slice(first, last)}${key.slice(last).toLowerCase()}`;\n}\n\n/**\n * Parses git config --get-regex output into a Map.\n * The output format is \"key value\" per line, where key and value are space-separated.\n *\n * A line with NO space at all is git's rendering of a bareword/valueless entry (`key`, no `=`) — git's\n * OWN config parser reads that as boolean TRUE (git-config(1): \"a variable defined without `= <value>`\n * is taken as true\"), confirmed against git's `format_config` (builtin/config.c): the key-delimiter is\n * appended unconditionally, then backed out ONLY when the stored value is NULL (the bareword case) —\n * an explicit empty value (`key =`) is a real, non-NULL empty string, so its line KEEPS the trailing\n * space and is already handled by the branch below, byte-distinct from a bareword line.\n *\n * By default a bareword line is skipped (the historical behavior, which reads as \"unset\" — wrong, but\n * safe); pass `includeValueless` to map it to the literal string `'true'` instead, so `parseGitBoolean`\n * and callers checking `.has()` for \"configured\" both read it correctly.\n */\nexport function parseConfigRegexOutput(\n\tdata: string | undefined,\n\toptions?: { includeValueless?: boolean },\n): Map<string, string> {\n\tconst configMap = new Map<string, string>();\n\tif (!data) return configMap;\n\n\tfor (const line of data.split('\\n')) {\n\t\tif (!line) continue;\n\n\t\tconst spaceIndex = line.indexOf(' ');\n\t\tif (spaceIndex === -1) {\n\t\t\tif (options?.includeValueless) {\n\t\t\t\tconfigMap.set(line, 'true');\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconfigMap.set(line.substring(0, spaceIndex), line.substring(spaceIndex + 1));\n\t}\n\n\treturn configMap;\n}\n\nexport function parseGitBoolean(value: string | undefined): boolean {\n\tif (value == null) return false;\n\n\tconst normalized = value.toLowerCase().trim();\n\treturn normalized === 'true' || normalized === 'yes' || normalized === 'on' || normalized === '1';\n}\n\n/** Whether a git config value spells boolean-false (`false`/`no`/`off`/`0`/empty), per git's maybe-bool parsing. */\nexport function isGitBooleanFalse(value: string): boolean {\n\tconst normalized = value.toLowerCase().trim();\n\treturn (\n\t\tnormalized === '' || normalized === 'false' || normalized === 'no' || normalized === 'off' || normalized === '0'\n\t);\n}\n\nexport class ConfigGitSubProvider implements GitConfigSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {\n\t\tthis.cache.setGkConfigReconciler((repoPath, priorSnapshot) => this.reconcileGkConfig(repoPath, priorSnapshot));\n\t}\n\n\t/**\n\t * In-flight gk-config reconciles keyed by commonPath. `pending` records that another `'gkConfig'`\n\t * event landed mid-pass, so one trailing pass runs after the current one settles. `paths` holds every\n\t * worktree path that reported the change, mapped to its close generation at that moment — the pass\n\t * reads through one that's still open and notifies all of them.\n\t */\n\tprivate readonly _gkConfigReconciles = new Map<string, { pending: boolean; paths: Map<string, number> }>();\n\n\t/**\n\t * Pending `setGkConfig` writes keyed by `(commonPath, key, value, skip-option)`. Concurrent\n\t * callers writing the same `(key, value)` pair share the in-flight Promise so only one\n\t * `git config --set` subprocess actually runs. Cleared on settle.\n\t */\n\tprivate readonly _pendingGkConfigWrites = new Map<string, Promise<void>>();\n\n\t@trace()\n\tgetConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfig(global ? undefined : repoPath, key, () =>\n\t\t\tthis.getConfigCore(repoPath, key, options),\n\t\t);\n\t}\n\n\tprivate async getConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean; type?: GitConfigType },\n\t): Promise<string | undefined> {\n\t\tconst args = ['config', '--get'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\tif (options?.type) {\n\t\t\targs.push(`--type=${options.type}`);\n\t\t}\n\t\targs.push(key);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t@trace()\n\tgetConfigRegex(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst global = options?.global || repoPath == null;\n\t\treturn this.cache.getConfigRegex(global ? undefined : repoPath, pattern, () =>\n\t\t\tthis.getConfigRegexCore(repoPath, pattern, options),\n\t\t);\n\t}\n\n\tprivate async getConfigRegexCore(\n\t\trepoPath: string | undefined,\n\t\tpattern: string,\n\t\toptions?: { file?: string; global?: boolean; runGitLocally?: boolean },\n\t): Promise<Map<string, string>> {\n\t\tconst args = ['config', '--get-regex'];\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else if (options?.global || repoPath == null) {\n\t\t\targs.push('--global');\n\t\t}\n\t\targs.push(pattern);\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath ?? '', errors: 'ignore', runLocally: options?.runGitLocally },\n\t\t\t...args,\n\t\t);\n\t\t// Deliberately NOT `.trim()`-ed: an explicit-empty value's LAST line ends in a meaningful trailing\n\t\t// space, and `.trim()`-ing the whole (possibly multi-line) buffer strips it — `parseConfigRegexOutput`\n\t\t// already skips the blank line a trailing newline produces, so no trim is needed.\n\t\treturn parseConfigRegexOutput(result.stdout);\n\t}\n\n\t@debug()\n\tasync setConfig(\n\t\trepoPath: string | undefined,\n\t\tkey: GitConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean },\n\t): Promise<void> {\n\t\tawait this.setConfigCore(repoPath, key, value, options);\n\n\t\t// A branch's upstream (`remote`/`merge`) is read back through the cached branch list, which a\n\t\t// `'config'` reset alone leaves serving the old tracking info.\n\t\tif (repoPath != null && !options?.file && /^branch\\..+\\.(?:remote|merge)$/i.test(key)) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t}\n\t}\n\n\tprivate async setConfigCore(\n\t\trepoPath: string | undefined,\n\t\tkey: string,\n\t\tvalue: string | undefined,\n\t\toptions?: { file?: string; global?: boolean },\n\t): Promise<void> {\n\t\tconst args: string[] = ['config'];\n\n\t\tif (options?.file) {\n\t\t\targs.push('-f', options.file);\n\t\t} else {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\targs.push(global ? '--global' : '--local');\n\t\t}\n\n\t\tif (value == null) {\n\t\t\targs.push('--unset', key);\n\t\t} else {\n\t\t\targs.push(key, value);\n\t\t}\n\n\t\tawait this.git.run({ cwd: repoPath ?? '', runLocally: true }, ...args);\n\n\t\t// Only invalidate cache when not using a custom file (custom files aren't cached)\n\t\tif (!options?.file) {\n\t\t\tconst global = options?.global || repoPath == null;\n\t\t\t// Invalidate the cached value for this key and clear all regex patterns for this scope\n\t\t\tthis.cache.deleteConfig(global ? undefined : repoPath, key);\n\t\t}\n\t}\n\n\t@gate()\n\t@debug()\n\tasync getCurrentUser(repoPath: string): Promise<GitUser | undefined> {\n\t\tif (!repoPath) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst cached = this.cache.currentUser.get(repoPath);\n\t\tif (cached != null) return cached;\n\t\t// If we found null, user data was not found - don't bother trying again\n\t\tif (cached === null) return undefined;\n\n\t\tconst user: GitUser = { name: undefined, email: undefined };\n\n\t\ttry {\n\t\t\tconst configMap = await this.getConfigRegex(repoPath, '^user\\\\.(name|email)$', {\n\t\t\t\trunGitLocally: true,\n\t\t\t});\n\t\t\tif (configMap.size) {\n\t\t\t\tuser.name = configMap.get('user.name');\n\t\t\t\tuser.email = configMap.get('user.email');\n\t\t\t} else {\n\t\t\t\tuser.name =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_NAME || process_env.GIT_COMMITTER_NAME || userInfo()?.username || undefined;\n\t\t\t\tif (!user.name) {\n\t\t\t\t\t// If we found no user data, mark it so we won't bother trying again\n\t\t\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tuser.email =\n\t\t\t\t\tprocess_env.GIT_AUTHOR_EMAIL ||\n\t\t\t\t\tprocess_env.GIT_COMMITTER_EMAIL ||\n\t\t\t\t\tprocess_env.EMAIL ||\n\t\t\t\t\t`${user.name}@${hostname()}`;\n\t\t\t}\n\n\t\t\tconst author = `${user.name} <${user.email}>`;\n\t\t\t// Check if there is a mailmap for the current user\n\t\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'check-mailmap', author);\n\n\t\t\tif (result.stdout && result.stdout !== author) {\n\t\t\t\tconst match = mappedAuthorRegex.exec(result.stdout);\n\t\t\t\tif (match != null) {\n\t\t\t\t\t[, user.name, user.email] = match;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.cache.currentUser.set(repoPath, user);\n\t\t\treturn user;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\n\t\t\t// Mark it so we won't bother trying again\n\t\t\tthis.cache.currentUser.set(repoPath, null);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@gate()\n\t@trace({ exit: r => `returned ${r}` })\n\tasync getDefaultWorktreePath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn getBestPath(normalizePath(joinPaths(basePath, '..')));\n\t}\n\n\t@gate()\n\t@trace({\n\t\texit: r =>\n\t\t\t`returned ${r.uri.toString(true)}, commonUri=${r.commonUri?.toString(true)}, parentUri=${r.parentUri?.toString(true)}`,\n\t})\n\tasync getGitDir(repoPath: string): Promise<GitDir> {\n\t\tconst cached = this.cache.gitDir.get(repoPath);\n\t\tif (cached != null) return cached;\n\n\t\tconst scope = getScopedLogger();\n\t\tconst repoInfo = await this.getRepositoryInfo(repoPath);\n\n\t\tlet gitDir: GitDir;\n\t\tif (!Array.isArray(repoInfo) && repoInfo != null) {\n\t\t\tgitDir = {\n\t\t\t\turi: fileUri(repoInfo.gitDir),\n\t\t\t\tcommonUri: repoInfo.commonGitDir ? fileUri(repoInfo.commonGitDir) : undefined,\n\t\t\t\tparentUri: repoInfo.superprojectPath ? fileUri(repoInfo.superprojectPath) : undefined,\n\t\t\t};\n\t\t} else {\n\t\t\tgitDir = {\n\t\t\t\turi: this.provider.getAbsoluteUri(joinPaths(repoPath, '.git'), repoPath),\n\t\t\t};\n\n\t\t\tconst gitDirPath = gitDir.uri.toString(true);\n\t\t\tscope?.warn(`rev-parse failed for '${repoPath}'; falling back to '${gitDirPath}'`);\n\t\t\tthis.context.hooks?.operations?.onGitDirResolveFailed?.(\n\t\t\t\trepoPath,\n\t\t\t\tgitDirPath,\n\t\t\t\t`rev_parse returned ${JSON.stringify(repoInfo)}`,\n\t\t\t);\n\t\t}\n\t\tthis.cache.gitDir.set(repoPath, gitDir);\n\n\t\treturn gitDir;\n\t}\n\n\t/**\n\t * Gets the path to the .git/gk/config file for storing GitKraken-specific metadata.\n\t * Uses commonPath for worktrees so all worktrees share the same data.\n\t */\n\tprivate async getGkConfigPath(repoPath: string): Promise<string | undefined> {\n\t\tconst gitDir = await this.getGitDir(repoPath);\n\t\tif (gitDir == null) return undefined;\n\n\t\t// Use commonUri (main .git dir) for worktrees, otherwise use uri\n\t\tconst basePath = (gitDir.commonUri ?? gitDir.uri).fsPath;\n\t\treturn joinPaths(basePath, 'gk', 'config');\n\t}\n\n\t@debug()\n\tasync getGkConfig(repoPath: string, key: GkConfigKeys | DeprecatedGkConfigKeys): Promise<string | undefined> {\n\t\t// Served in-memory from a single bulk read of the whole `.git/gk/config` (no gk read needs `--type`).\n\t\t// Canonicalize the key the way git does for `--get-regexp` output so subsection-less keys\n\t\t// (e.g. `gk.defaultRemote`) match, and coerce an empty value to undefined to match the old\n\t\t// `git config --get` (`.trim() || undefined`) contract.\n\t\treturn (await this.getGkConfigMap(repoPath)).get(canonicalizeGitConfigKey(key)) || undefined;\n\t}\n\n\t@debug()\n\tasync getGkConfigRegex(repoPath: string, pattern: string): Promise<Map<string, string>> {\n\t\tconst all = await this.getGkConfigMap(repoPath);\n\n\t\t// Filter the bulk map in-memory. The old path passed `pattern` to `git config --get-regex`\n\t\t// (errors ignored → empty result on a bad pattern); preserve that no-throw contract here\n\t\t// rather than letting an invalid-JS pattern reject the read.\n\t\tlet re: RegExp;\n\t\ttry {\n\t\t\tre = new RegExp(pattern);\n\t\t} catch {\n\t\t\treturn new Map();\n\t\t}\n\n\t\tconst result = new Map<string, string>();\n\t\tfor (const [k, v] of all) {\n\t\t\tif (re.test(k)) {\n\t\t\t\tresult.set(k, v);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Reads the entire `.git/gk/config` in one `git config --get-regexp` and caches the parsed map\n\t * per commonPath. Per-key (getGkConfig) and per-namespace (getGkConfigRegex) lookups are served\n\t * from this single cached read — so a branch-overview render that fans out across several gk\n\t * namespaces costs one `git config` call instead of one per namespace.\n\t */\n\tprivate async getGkConfigMap(repoPath: string): Promise<Map<string, string>> {\n\t\t// Migrate BEFORE populating the cache, not inside the factory: a first-time migration calls\n\t\t// clearCaches('gkConfig'), which would otherwise evict the in-flight gkConfigMap entry the\n\t\t// cache wrapper synchronously stored and force a redundant bulk re-read.\n\t\tawait this.migrateGkConfigFromGitConfig(this.cache.getCommonPath(repoPath));\n\t\treturn this.cache.getGkConfigMap(repoPath, async cacheable => {\n\t\t\tconst { map, failed } = await this.getGkConfigRegexCore(repoPath, gkConfigAllPattern);\n\t\t\t// This map has no TTL and everything gk-related reads through it, so a failed read cached as an\n\t\t\t// empty map would report every branch as having no stored base or merge target for the session —\n\t\t\t// and would make the section-cleanup pre-checks skip real work. Only cache a read that happened.\n\t\t\tif (failed) {\n\t\t\t\tcacheable.invalidate();\n\t\t\t}\n\t\t\treturn map;\n\t\t});\n\t}\n\n\tprivate async getGkConfigRegexCore(\n\t\trepoPath: string,\n\t\tpattern: string,\n\t): Promise<{ map: Map<string, string>; failed: boolean }> {\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\t// No `.git/gk/config` yet is a real answer, not a failure — a repo GitLens has never written to.\n\t\tif (!gkConfigPath) return { map: new Map(), failed: false };\n\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t'config',\n\t\t\t'--get-regex',\n\t\t\t'-f',\n\t\t\tgkConfigPath,\n\t\t\tpattern,\n\t\t);\n\t\t// `--get-regex` exits 1 when nothing matches, which IS an answer (an empty gk config).\n\t\tconst exited = result.completion.status === 'exited';\n\t\treturn {\n\t\t\t// Deliberately NOT `.trim()`-ed — see `getConfigRegexCore`'s comment: it strips the trailing\n\t\t\t// space that marks an explicit-empty value's LAST line, silently dropping it from the map.\n\t\t\tmap: parseConfigRegexOutput(result.stdout),\n\t\t\tfailed: !exited || (result.exitCode !== 0 && result.exitCode !== 1),\n\t\t};\n\t}\n\n\t@debug()\n\tasync setGkConfig(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },\n\t): Promise<void> {\n\t\t// Encode the skip-invalidation option in the dedup key: a self-write (with skip targets)\n\t\t// and a user-driven write of the same `(key, value)` (no skip targets) have different\n\t\t// cache-invalidation semantics, so sharing their Promise would silently let the user-driven\n\t\t// write skip invalidation. Sort the array so callers passing the same targets in different\n\t\t// orders share a single in-flight write.\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\tconst skipKey = options?.skipInvalidation?.length ? options.skipInvalidation.toSorted().join(',') : '';\n\t\tconst dedupKey = `${commonPath}\\0${key}\\0${value ?? ''}\\0${skipKey}`;\n\n\t\tconst pending = this._pendingGkConfigWrites.get(dedupKey);\n\t\tif (pending != null) return pending;\n\n\t\tconst promise = this.setGkConfigCore(repoPath, key, value, options);\n\t\tthis._pendingGkConfigWrites.set(dedupKey, promise);\n\t\t// Swallow the cleanup chain's rejection so a failed write doesn't surface as an unhandled\n\t\t// rejection separate from the caller's own handling of the returned promise.\n\t\tvoid promise.finally(() => this._pendingGkConfigWrites.delete(dedupKey)).catch(() => {});\n\t\treturn promise;\n\t}\n\n\tprivate async setGkConfigCore(\n\t\trepoPath: string,\n\t\tkey: GkConfigKeys | DeprecatedGkConfigKeys,\n\t\tvalue: string | undefined,\n\t\toptions?: { skipInvalidation?: readonly GkConfigInvalidationTarget[] },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) return;\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;\n\n\t\tawait this.setConfigCore(repoPath, key, value, { file: gkConfigPath });\n\n\t\t// Invalidate the bulk map (so the next read re-fetches) and the derived caches for this key's\n\t\t// ref. The `.git/gk/config` file-watcher also fires `'gkConfig'` shortly after, which reconciles\n\t\t// the same change (see `reconcileGkConfig`) — recording our own write here keeps that later\n\t\t// reconcile from re-diffing this key as an external change.\n\t\tthis.cache.deleteGkConfig(repoPath, key, options);\n\t\tthis.cache.recordGkConfigWrite(repoPath, key, value);\n\t}\n\n\t@debug()\n\tasync removeGkConfigBranchSection(repoPath: string, ref: string): Promise<void> {\n\t\tawait this.runGkConfigSectionCommand(repoPath, ref, '--remove-section', `branch.${ref}`);\n\t}\n\n\t@debug()\n\tasync renameGkConfigBranchSection(repoPath: string, oldRef: string, newRef: string): Promise<void> {\n\t\t// Clear the destination first, unconditionally. `--rename-section` onto a section that already\n\t\t// exists APPENDS rather than replaces, leaving duplicate keys — and git resolves a duplicated key\n\t\t// to its LAST value, so the orphan left by an earlier branch of this name would win over the\n\t\t// metadata being moved. Doing this outside `runGkConfigSectionCommand` also covers the case where\n\t\t// the source has no gk keys at all, where its pre-check would otherwise skip the whole call and\n\t\t// leave the orphan in place for the renamed branch to inherit.\n\t\tawait this.removeGkConfigBranchSection(repoPath, newRef);\n\t\tawait this.runGkConfigSectionCommand(\n\t\t\trepoPath,\n\t\t\toldRef,\n\t\t\t'--rename-section',\n\t\t\t`branch.${oldRef}`,\n\t\t\t`branch.${newRef}`,\n\t\t);\n\t}\n\n\t/**\n\t * Runs a section-level `git config` against `.git/gk/config` for `ref`'s section, skipping the\n\t * subprocess entirely when the bulk map (already cached on any warm path) shows that ref has no gk\n\t * keys — the overwhelmingly common case, since most branches never get gk metadata written at all.\n\t *\n\t * Never throws: callers invoke this as bookkeeping after a branch op has already succeeded, so a\n\t * failure here must not be reported as (or roll back) the branch/checkout/worktree operation itself.\n\t * The worst case is metadata that stays behind until the next op on that name.\n\t */\n\tprivate async runGkConfigSectionCommand(repoPath: string, ref: string, ...args: string[]): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\t\ttry {\n\t\t\tconst prefix = `branch.${ref}.`;\n\t\t\tconst map = await this.getGkConfigMap(repoPath);\n\t\t\t// The shortcut needs a map with CONTENT to be trustworthy. A failed bulk read resolves empty\n\t\t\t// (see `getGkConfigMap`) — the cache refuses it, but this caller still holds it — and an empty map\n\t\t\t// read as authoritative would skip the removal entirely, leaving a deleted branch's base,\n\t\t\t// disposition and issue links behind for the next branch reusing that name. When it's empty, run\n\t\t\t// the command: worst case is a no-op on a section that isn't there.\n\t\t\tif (map.size && !some(map.keys(), k => k.startsWith(prefix))) return;\n\n\t\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\t\tif (!gkConfigPath) return;\n\t\t\t// `getGkConfigPath` only joins a path, so check the file: with no `.git/gk/config` there is\n\t\t\t// genuinely nothing to remove, and running the command would fail with a fatal we'd then warn\n\t\t\t// about on every branch delete in a repo that has never had gk metadata.\n\t\t\tif (!(await fsExists(gkConfigPath))) return;\n\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, runLocally: true, errors: 'ignore' },\n\t\t\t\t'config',\n\t\t\t\t'-f',\n\t\t\t\tgkConfigPath,\n\t\t\t\t...args,\n\t\t\t);\n\t\t\t// `errors: 'ignore'` is deliberate — see the note above on why this must never throw — but that\n\t\t\t// also means the `catch` below never sees a failed write, so without this the metadata silently\n\t\t\t// stays behind (or, on a rename, stays under the old name) with nothing recorded anywhere.\n\t\t\t// Reported, not thrown: the branch op it follows has already succeeded.\n\t\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0) {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`Failed to update gk config section for '${ref}' in '${repoPath}': ${\n\t\t\t\t\t\tresult.completion.status === 'exited'\n\t\t\t\t\t\t\t? `git config exited ${result.exitCode}`\n\t\t\t\t\t\t\t: `${result.completion.status} · ${result.completion.error.message}`\n\t\t\t\t\t}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\t// A whole section moved or vanished, which the per-key `deleteGkConfig` cascade can't express.\n\t\t\t// Drop the bulk map so the next read hits disk; the derived caches for the affected ref are\n\t\t\t// evicted by the caller's own `deleteBaseBranchName` plus the `'branches'` cascade it fires.\n\t\t\tthis.cache.deleteGkConfigMap(repoPath);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to update gk config section for '${ref}' in '${repoPath}'`);\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles a watcher-observed `'gkConfig'` change — registered with the shared `Cache` as its\n\t * gk-config reconciler (see the constructor). Re-reads the bulk map (hard-evicted by\n\t * `Cache.handleGkConfigChanged`, so this hits disk) and diffs it against `priorSnapshot` to cascade\n\t * `baseBranchName`/`branchOverviews` only for refs whose merge-relevant keys actually changed.\n\t *\n\t * Serialized per commonPath: a reconcile is a read-then-diff against a single shared baseline, so\n\t * two overlapping passes would both diff against the same snapshot and the later change could fall\n\t * out entirely. An event arriving mid-pass instead queues one trailing pass, which re-reads and\n\t * diffs against the baseline the previous pass just established.\n\t *\n\t * `.git/gk/config` lives in the common dir, so the watcher delivers one event per worktree sharing it\n\t * (see `WatchGroup.onCommonEvent`). Siblings never run concurrently — the later ones fold into the\n\t * in-flight pass — but folding in arms the trailing pass, so a fan-out costs one extra read that\n\t * re-reads and diffs an unchanged file. However many siblings land while a pass is running, they\n\t * coalesce into that single trailing pass; the cost is the price of never dropping a real change.\n\t * Every reporting path is retained because `Repository` matches the follow-up change event on exact\n\t * path, and a sibling worktree that never receives one would sit on stale derived values.\n\t */\n\tprivate reconcileGkConfig(\n\t\trepoPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t): GkReconcileOutcome | Promise<GkReconcileOutcome> {\n\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\n\t\tconst inflight = this._gkConfigReconciles.get(commonPath);\n\t\tif (inflight != null) {\n\t\t\tinflight.paths.set(repoPath, this.cache.getCloseGeneration(repoPath));\n\t\t\tinflight.pending = true;\n\t\t\t// Handed to the in-flight pass's trailing loop, so it IS being reconciled — just not by this\n\t\t\t// call. Reporting a failure here would coarse-clear on every write in a burst.\n\t\t\treturn 'deferred';\n\t\t}\n\n\t\tconst entry = { pending: false, paths: new Map([[repoPath, this.cache.getCloseGeneration(repoPath)]]) };\n\t\tthis._gkConfigReconciles.set(commonPath, entry);\n\t\treturn this.runGkConfigReconcile(commonPath, priorSnapshot, entry);\n\t}\n\n\t/** Drops paths that closed since they reported the change; returns whichever remain. */\n\tprivate pruneClosedReconcilePaths(paths: Map<string, number>): Map<string, number> {\n\t\tfor (const [path, generation] of paths) {\n\t\t\tif (this.cache.getCloseGeneration(path) !== generation) {\n\t\t\t\tpaths.delete(path);\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\tprivate async runGkConfigReconcile(\n\t\tcommonPath: string,\n\t\tpriorSnapshot: ReadonlyMap<string, string> | undefined,\n\t\tentry: { pending: boolean; paths: Map<string, number> },\n\t): Promise<GkReconcileOutcome> {\n\t\tconst scope = getScopedLogger();\n\t\tlet snapshot = priorSnapshot;\n\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tentry.pending = false;\n\n\t\t\t\t// Read through a path that's still open — one worktree closing must not drop the event for\n\t\t\t\t// its still-open siblings, which is why this reads from the surviving set rather than from\n\t\t\t\t// whichever path happened to report first.\n\t\t\t\tconst readPath = first(this.pruneClosedReconcilePaths(entry.paths).keys());\n\t\t\t\t// Every path that reported the change has since closed — nothing left to reconcile, and\n\t\t\t\t// nothing left holding a stale derived value either.\n\t\t\t\tif (readPath == null) return 'reconciled';\n\n\t\t\t\t// A trailing pass must not reuse the entry the pass before it installed.\n\t\t\t\tthis.cache.deleteGkConfigMap(readPath);\n\t\t\t\tconst freshMap = await this.getGkConfigMap(readPath);\n\t\t\t\t// Paths may have closed while the re-read was in flight; if every one did, there's nothing\n\t\t\t\t// left to reconcile against.\n\t\t\t\tif (!this.pruneClosedReconcilePaths(entry.paths).size) return 'reconciled';\n\n\t\t\t\tif (this.cache.reconcileGkConfigMap(readPath, snapshot, freshMap)) {\n\t\t\t\t\t// Fire a follow-up change: a consumer that re-read on the original event may have done so\n\t\t\t\t\t// before this async cascade landed, so it would otherwise hold a stale derived value with\n\t\t\t\t\t// nothing left to prompt another re-read.\n\t\t\t\t\tfor (const path of entry.paths.keys()) {\n\t\t\t\t\t\tthis.context.hooks?.repository?.onChanged?.(path, ['gkConfig']);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// `reconcileGkConfigMap` advanced the stored baseline to what we just read; a trailing pass\n\t\t\t\t// diffs its own read against that.\n\t\t\t\tsnapshot = this.cache.getGkConfigMergeSnapshot(readPath);\n\t\t\t} while (entry.pending);\n\n\t\t\treturn 'reconciled';\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to reconcile gk config for '${commonPath}'`);\n\t\t\t// The caller falls back to the coarse clear — derived caches are otherwise left holding\n\t\t\t// pre-change values with nothing to correct them.\n\t\t\treturn 'failed';\n\t\t} finally {\n\t\t\tthis._gkConfigReconciles.delete(commonPath);\n\t\t}\n\t}\n\n\t@debug()\n\tasync getSigningConfig(repoPath: string): Promise<SigningConfig> {\n\t\t// Fetch all signing-related config in one call\n\t\tconst configMap = await this.getConfigRegex(\n\t\t\trepoPath,\n\t\t\t'^(commit\\\\.gpgsign|gpg\\\\.format|user\\\\.signingkey|gpg\\\\.program|gpg\\\\.ssh\\\\.program|gpg\\\\.ssh\\\\.allowedsignersfile)$',\n\t\t);\n\n\t\t// Extract values (keys are lowercase in git output)\n\t\tconst enabledRaw = configMap.get('commit.gpgsign');\n\t\tconst format = configMap.get('gpg.format');\n\t\tconst signingKey = configMap.get('user.signingkey');\n\t\tconst gpgProgram = configMap.get('gpg.program');\n\t\tconst sshProgram = configMap.get('gpg.ssh.program');\n\t\tconst allowedSignersFile = configMap.get('gpg.ssh.allowedsignersfile');\n\n\t\t// Check if git config has commit signing enabled, falling back to the host-level override\n\t\t// (e.g. VS Code's `git.enableCommitSigning`) when `commit.gpgsign` is unset/false. The host\n\t\t// override can only enable signing, never force it off.\n\t\tconst isEnabled = parseGitBoolean(enabledRaw) || this.context.config?.signing?.enabled === true;\n\n\t\treturn {\n\t\t\tenabled: isEnabled,\n\t\t\tformat: (format as SigningFormat) ?? 'gpg',\n\t\t\tsigningKey: signingKey,\n\t\t\tgpgProgram: gpgProgram,\n\t\t\tsshProgram: sshProgram,\n\t\t\tallowedSignersFile: allowedSignersFile,\n\t\t};\n\t}\n\n\t@debug()\n\tasync validateSigningSetup(repoPath: string): Promise<ValidationResult> {\n\t\tconst config = await this.getSigningConfig(repoPath);\n\n\t\tif (!config.signingKey) {\n\t\t\treturn { valid: false, error: 'No signing key configured' };\n\t\t}\n\n\t\t// Basic validation: just check that a signing key is configured\n\t\t// Git will handle the actual validation when signing commits\n\t\treturn { valid: true };\n\t}\n\n\t@debug()\n\tasync setSigningConfig(\n\t\trepoPath: string,\n\t\tconfig: Partial<SigningConfig>,\n\t\toptions?: { global?: boolean },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tif (config.enabled != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'commit.gpgsign', config.enabled ? 'true' : 'false', options);\n\t\t\t}\n\t\t\tif (config.format != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.format', config.format, options);\n\t\t\t}\n\t\t\tif (config.signingKey != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'user.signingkey', config.signingKey, options);\n\t\t\t}\n\t\t\tif (config.gpgProgram != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.program', config.gpgProgram, options);\n\t\t\t}\n\t\t\tif (config.sshProgram != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.ssh.program', config.sshProgram, options);\n\t\t\t}\n\t\t\tif (config.allowedSignersFile != null) {\n\t\t\t\tawait this.setConfig(repoPath, 'gpg.ssh.allowedSignersFile', config.allowedSignersFile, options);\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\tgetSigningConfigFlags(config: SigningConfig): string[] {\n\t\tconst flags: string[] = [];\n\n\t\tif (config.gpgProgram) {\n\t\t\tflags.push('-c', `gpg.program=${config.gpgProgram}`);\n\t\t}\n\t\tif (config.format && config.format !== 'gpg') {\n\t\t\tflags.push('-c', `gpg.format=${config.format}`);\n\t\t}\n\t\tif (config.sshProgram) {\n\t\t\tflags.push('-c', `gpg.ssh.program=${config.sshProgram}`);\n\t\t}\n\t\tif (config.allowedSignersFile) {\n\t\t\tflags.push('-c', `gpg.ssh.allowedSignersFile=${config.allowedSignersFile}`);\n\t\t}\n\n\t\treturn flags;\n\t}\n\n\tasync getRepositoryInfo(\n\t\tcwd: string,\n\t): Promise<\n\t\t| { repoPath: string; gitDir: string; commonGitDir: string | undefined; superprojectPath: string | undefined }\n\t\t| [safe: true, repoPath: string]\n\t\t| [safe: false]\n\t\t| []\n\t> {\n\t\tlet result;\n\n\t\tif (this.context.workspace?.isTrusted === false) {\n\t\t\ttry {\n\t\t\t\tawait fs.stat(joinPaths(cwd, 'HEAD'));\n\t\t\t\tresult = await this.git.run(\n\t\t\t\t\t{ cwd: cwd, errors: 'throw', configs: ['-C', cwd] },\n\t\t\t\t\t'rev-parse',\n\t\t\t\t\t'--show-cdup',\n\t\t\t\t);\n\t\t\t\tif (!result.stdout.trim()) {\n\t\t\t\t\tLogger.warn(`Skipping (untrusted); bare clone repository detected in '${cwd}'`);\n\t\t\t\t\treturn emptyArray as [];\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// If this throws, we should be good to open the repo\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tresult = await this.git.run(\n\t\t\t\t{ cwd: cwd, errors: 'throw' },\n\t\t\t\t'rev-parse',\n\t\t\t\t'--show-toplevel',\n\t\t\t\t'--git-dir',\n\t\t\t\t'--git-common-dir',\n\t\t\t\t'--show-superproject-working-tree',\n\t\t\t);\n\t\t\tif (!result.stdout) return emptyArray as [];\n\n\t\t\t// Output is 3-4 lines: show-toplevel, git-dir, git-common-dir, [show-superproject-working-tree]\n\t\t\t// The 4th line is only present for submodules\n\t\t\tconst lines = result.stdout.split('\\n').map(r => r.trimStart());\n\t\t\tconst [repoPath, dotGitPath, commonDotGitPath, superprojectPath] = lines;\n\n\t\t\tif (!repoPath) return emptyArray as [];\n\n\t\t\tconst normalizedRepoPath = normalizePath(repoPath.replace(/[\\r|\\n]+$/, ''));\n\n\t\t\tlet gitDir = dotGitPath;\n\t\t\tif (gitDir && !isAbsolute(gitDir)) {\n\t\t\t\tgitDir = joinPaths(cwd, gitDir);\n\t\t\t}\n\t\t\tgitDir = normalizePath(gitDir);\n\n\t\t\tlet commonGitDir: string | undefined;\n\t\t\tif (commonDotGitPath) {\n\t\t\t\tcommonGitDir = commonDotGitPath;\n\t\t\t\tif (!isAbsolute(commonGitDir)) {\n\t\t\t\t\tcommonGitDir = joinPaths(cwd, commonGitDir);\n\t\t\t\t}\n\t\t\t\tcommonGitDir = normalizePath(commonGitDir);\n\t\t\t\tif (commonGitDir === gitDir) {\n\t\t\t\t\tcommonGitDir = undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\trepoPath: normalizedRepoPath,\n\t\t\t\tgitDir: gitDir,\n\t\t\t\tcommonGitDir: commonGitDir,\n\t\t\t\tsuperprojectPath: superprojectPath?.trim() || undefined,\n\t\t\t};\n\t\t} catch (ex: any) {\n\t\t\tif (ex instanceof WorkspaceUntrustedError) return emptyArray as [];\n\n\t\t\tconst unsafeMatch =\n\t\t\t\t/(?:^fatal:\\s*detected dubious ownership in repository at '([^']+)'|unsafe repository \\('([^']+)' is owned by someone else\\))[\\s\\S]*(git config --global --add safe\\.directory [^\\n\\u2022]+)/m.exec(\n\t\t\t\t\tex.stderr,\n\t\t\t\t);\n\t\t\tif (unsafeMatch != null) {\n\t\t\t\tLogger.warn(\n\t\t\t\t\t`Skipping; unsafe repository detected in '${unsafeMatch[1] || unsafeMatch[2]}'; run '${\n\t\t\t\t\t\tunsafeMatch[3]\n\t\t\t\t\t}' to allow it`,\n\t\t\t\t);\n\t\t\t\treturn [false];\n\t\t\t}\n\n\t\t\tconst inDotGit = /this operation must be run in a work tree/.test(ex.stderr);\n\t\t\tif (inDotGit && this.context.workspace?.isTrusted !== false) {\n\t\t\t\tresult = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--is-bare-repository');\n\t\t\t\tif (result.stdout.trim() === 'true') {\n\t\t\t\t\tconst result = await this.revParseGitDir(cwd);\n\t\t\t\t\tconst repoPath = result?.commonPath ?? result?.path;\n\t\t\t\t\tif (repoPath?.length) return [true, repoPath];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (inDotGit || ex.code === 'ENOENT') {\n\t\t\t\tlet exists = inDotGit ? false : await fsExists(cwd);\n\t\t\t\tif (!exists) {\n\t\t\t\t\tdo {\n\t\t\t\t\t\tconst parent = dirname(cwd);\n\t\t\t\t\t\tif (parent === cwd || parent.length === 0) return emptyArray as [];\n\n\t\t\t\t\t\tcwd = parent;\n\t\t\t\t\t\texists = await fsExists(cwd);\n\t\t\t\t\t} while (!exists);\n\n\t\t\t\t\treturn this.getRepositoryInfo(cwd);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn emptyArray as [];\n\t\t}\n\t}\n\n\tprivate async revParseGitDir(cwd: string): Promise<{ path: string; commonPath?: string } | undefined> {\n\t\tconst result = await this.git.run({ cwd: cwd, errors: 'ignore' }, 'rev-parse', '--git-dir', '--git-common-dir');\n\t\tif (!result.stdout) return undefined;\n\n\t\tlet [dotGitPath, commonDotGitPath] = result.stdout.split('\\n').map(r => r.trimStart());\n\n\t\tif (!isAbsolute(dotGitPath)) {\n\t\t\tdotGitPath = joinPaths(cwd, dotGitPath);\n\t\t}\n\t\tdotGitPath = normalizePath(dotGitPath);\n\n\t\tif (commonDotGitPath) {\n\t\t\tif (!isAbsolute(commonDotGitPath)) {\n\t\t\t\tcommonDotGitPath = joinPaths(cwd, commonDotGitPath);\n\t\t\t}\n\t\t\tcommonDotGitPath = normalizePath(commonDotGitPath);\n\n\t\t\treturn { path: dotGitPath, commonPath: commonDotGitPath !== dotGitPath ? commonDotGitPath : undefined };\n\t\t}\n\n\t\treturn { path: dotGitPath };\n\t}\n\n\t/**\n\t * Ensures the `.git/gk/` folder exists, but only if the parent `.git` directory already exists.\n\t * This prevents creating stray `.git` directories if the computed path is wrong.\n\t * Returns `true` if the folder was ensured, `false` if the parent `.git` does not exist.\n\t */\n\tprivate async ensureGkConfigFolder(\n\t\tgkConfigFolder: string,\n\t\tscope: ReturnType<typeof getScopedLogger>,\n\t): Promise<boolean> {\n\t\t// Verify the .git directory already exists — never create it from scratch\n\t\tconst dotGitDir = joinPaths(gkConfigFolder, '..');\n\t\ttry {\n\t\t\tawait fs.stat(dotGitDir);\n\t\t} catch {\n\t\t\tscope?.warn(`Skipping GK config write — expected git directory '${dotGitDir}' does not exist`);\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\tawait fs.mkdir(gkConfigFolder, { recursive: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigFolder}' directory`);\n\t\t\treturn false;\n\t\t}\n\n\t\treturn true;\n\t}\n\n\tprivate _migratedRepos = new Set<string>();\n\n\t/**\n\t * One-time migration of GK config entries from regular git config to `.git/gk/config`.\n\t * If `.git/gk/config` already exists, assumes migration is complete (or data is already there).\n\t * Preserves existing `.git/gk/config` values as source of truth (won't overwrite).\n\t * Removes migrated keys from regular git config to stop cluttering it.\n\t */\n\t@gate()\n\t@debug()\n\tprivate async migrateGkConfigFromGitConfig(repoPath: string): Promise<void> {\n\t\tif (this._migratedRepos.has(repoPath)) return;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst gkConfigPath = await this.getGkConfigPath(repoPath);\n\t\tif (!gkConfigPath) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tconst gkConfigFolder = joinPaths(gkConfigPath, '..');\n\n\t\t// If .git/gk/config already exists, consider migration done — it was either\n\t\t// already migrated or values were written there directly via setGkConfig\n\t\ttry {\n\t\t\tawait fs.stat(gkConfigPath);\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t} catch {\n\t\t\t// file doesn't exist, proceed with migration\n\t\t}\n\n\t\t// If .git/gk/config doesn't exist, create an empty file to prevent multiple migration attempts in future sessions\n\t\tif (!(await this.ensureGkConfigFolder(gkConfigFolder, scope))) return;\n\n\t\ttry {\n\t\t\tawait fs.writeFile(gkConfigPath, new Uint8Array());\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, `Failed to create '${gkConfigPath}' file`);\n\t\t}\n\n\t\t// Read legacy gk-* keys from regular git config\n\t\tlet migrateConfig: Map<string, string>;\n\t\ttry {\n\t\t\tmigrateConfig = await this.getConfigRegexCore(repoPath, '^branch\\\\..*\\\\.gk-', { runGitLocally: true });\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex, 'Failed to read legacy GK config entries');\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!migrateConfig.size) {\n\t\t\tthis._migratedRepos.add(repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\tscope?.info(`Migrating ${String(migrateConfig.size)} GK config entries from git config to .git/gk/config`);\n\n\t\t// Copy legacy entries to .git/gk/config\n\t\tfor (const [key, value] of [...migrateConfig]) {\n\t\t\ttry {\n\t\t\t\tawait this.setConfigCore(repoPath, key, value, { file: gkConfigPath });\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to migrate key '${key}' to GK config`);\n\t\t\t\t// If we failed to migrate, delete it from the list so we won't try to remove it from git config later\n\t\t\t\tmigrateConfig.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Remove legacy keys from regular git config (use --unset-all defensively)\n\t\tfor (const [key] of migrateConfig) {\n\t\t\ttry {\n\t\t\t\tawait this.git.run(\n\t\t\t\t\t{ cwd: repoPath, errors: 'ignore', runLocally: true },\n\t\t\t\t\t'config',\n\t\t\t\t\t'--local',\n\t\t\t\t\t'--unset-all',\n\t\t\t\t\tkey,\n\t\t\t\t);\n\t\t\t} catch (ex) {\n\t\t\t\tscope?.error(ex, `Failed to remove migrated key '${key}' from git config`);\n\t\t\t}\n\t\t}\n\n\t\t// Clear caches since we modified both config files\n\t\tthis.cache.clearCaches(repoPath, 'config', 'gkConfig');\n\n\t\tthis._migratedRepos.add(repoPath);\n\t}\n}\n"],"mappings":"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"}
@@ -3,7 +3,7 @@ import { getScopedLogger } from "../../utils/logger.scoped.js";
3
3
  import { debug } from "../../utils/decorators/log.js";
4
4
  import { fromUri, toFsPath } from "../../utils/uri.js";
5
5
  import { normalizePath, splitPath } from "../../utils/path.js";
6
- import { rootSha, uncommittedStaged } from "../../git/models/revision.js";
6
+ import { uncommittedStaged } from "../../git/models/revision.js";
7
7
  import { getRevisionRangeParts, isRevisionRange, isUncommitted, isUncommittedStaged } from "../../git/utils/revision.utils.js";
8
8
  import __decorate from "../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
9
9
  import { GitError, GitErrors, gitConfigsDiff, gitConfigsLog } from "../exec/git.js";
@@ -440,7 +440,7 @@ var DiffGitSubProvider = class {
440
440
  if (options?.renames) params.push(`-M${options.similarityThreshold == null ? "" : `${options.similarityThreshold}%`}`);
441
441
  if (options?.filters != null && options.filters.length !== 0) params.push(`--diff-filter=${options.filters.join("")}`);
442
442
  if (ref1) {
443
- if (ref1.endsWith("^3^")) ref1 = rootSha;
443
+ if (ref1.endsWith("^3^")) ref1 = await this.provider.revision.getEmptyTreeSha(repoPath);
444
444
  params.push(isUncommittedStaged(ref1) ? "--staged" : ref1);
445
445
  }
446
446
  if (ref2) params.push(isUncommittedStaged(ref2) ? "--staged" : ref2);
@@ -454,7 +454,7 @@ var DiffGitSubProvider = class {
454
454
  const match = GitErrors.badRevision.exec(ex.message);
455
455
  if (match !== null) {
456
456
  const [, ref] = match;
457
- if (ref === ref1 && ref?.endsWith("^")) return this.diff(repoPath, fileName, rootSha, ref2, options);
457
+ if (ref === ref1 && ref?.endsWith("^")) return this.diff(repoPath, fileName, await this.provider.revision.getEmptyTreeSha(repoPath), ref2, options);
458
458
  }
459
459
  throw ex;
460
460
  }
@@ -482,7 +482,7 @@ var DiffGitSubProvider = class {
482
482
  const match = GitErrors.badRevision.exec(ex.message);
483
483
  if (match !== null) {
484
484
  const [, matchedRef] = match;
485
- if (matchedRef === ref && matchedRef?.endsWith("^")) return this.diffContents(repoPath, fileName, rootSha, contents, options);
485
+ if (matchedRef === ref && matchedRef?.endsWith("^")) return this.diffContents(repoPath, fileName, await this.provider.revision.getEmptyTreeSha(repoPath), contents, options);
486
486
  }
487
487
  throw ex;
488
488
  }
@@ -1 +1 @@
1
- {"version":3,"file":"diff.js","names":[],"sources":["../../../../git-cli/src/providers/diff.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport type {\n\tGitDiff,\n\tGitDiffFiles,\n\tGitDiffFilter,\n\tGitDiffShortStat,\n\tParsedGitDiff,\n\tParsedGitDiffHunks,\n} from '@gitlens/git/models/diff.js';\nimport type { GitFile } from '@gitlens/git/models/file.js';\nimport type { GitRevisionRange, GitRevisionRangeNotation } from '@gitlens/git/models/revision.js';\nimport { deletedOrMissing, rootSha, uncommitted, uncommittedStaged } from '@gitlens/git/models/revision.js';\nimport {\n\tparseGitApplyFiles,\n\tparseGitDiff,\n\tparseGitDiffNumStatFiles,\n\tparseGitDiffShortStat,\n\tparseGitFileDiff,\n} from '@gitlens/git/parsers/diffParser.js';\nimport type {\n\tGitDiffSubProvider,\n\tNextComparisonUrisResult,\n\tPreviousComparisonUrisResult,\n\tPreviousRangeComparisonUrisResult,\n} from '@gitlens/git/providers/diff.js';\nimport type { DisposableTemporaryGitIndex } from '@gitlens/git/providers/staging.js';\nimport type { DiffRange, RevisionUri } from '@gitlens/git/providers/types.js';\nimport {\n\tgetRevisionRangeParts,\n\tisRevisionRange,\n\tisUncommitted,\n\tisUncommittedStaged,\n} from '@gitlens/git/utils/revision.utils.js';\nimport { encodeGitLensRevisionUriAuthority } from '@gitlens/git/utils/uriAuthority.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport { fnv1aHash } from '@gitlens/utils/hash.js';\nimport { first } from '@gitlens/utils/iterable.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { normalizePath, splitPath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { fromUri, toFsPath } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport type { GitResult } from '../exec/exec.types.js';\nimport type { Git } from '../exec/git.js';\nimport { gitConfigsDiff, gitConfigsLog, GitError, GitErrors } from '../exec/git.js';\nimport type { LogParsedFile } from '../parsers/logParser.js';\nimport { getShaAndFileRangeLogParser, getShaAndFileSummaryLogParser } from '../parsers/logParser.js';\n\nexport class DiffGitSubProvider implements GitDiffSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {}\n\n\t@debug()\n\tasync getChangedFilesCount(\n\t\trepoPath: string,\n\t\tto?: string,\n\t\tfrom?: string,\n\t\toptions?: { uris?: (string | Uri)[]; includeUntracked?: boolean },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<GitDiffShortStat | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst args: string[] = [];\n\t\tif (to != null) {\n\t\t\t// Handle revision ranges specially if there is no `from`, otherwise `prepareToFromDiffArgs` will duplicate the range\n\t\t\tif (isRevisionRange(to) && from == null) {\n\t\t\t\targs.push(to);\n\t\t\t} else {\n\t\t\t\tprepareToFromDiffArgs(to, from, args);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsDiff },\n\t\t\t\t'diff',\n\t\t\t\t'--shortstat',\n\t\t\t\t'--no-ext-diff',\n\t\t\t\t...args,\n\t\t\t\t'--',\n\t\t\t\t...(options?.uris?.map(p => this.provider.getRelativePath(p, repoPath)) ?? []),\n\t\t\t);\n\n\t\t\tconst stat = result.stdout ? parseGitDiffShortStat(result.stdout) : undefined;\n\n\t\t\t// Merge in untracked files when the comparison involves the working tree.\n\t\t\t// Skip when a pathspec filter is active — mixing a path-filtered diff count with an\n\t\t\t// unfiltered untracked count would silently produce wrong totals.\n\t\t\tif (options?.includeUntracked && !options.uris?.length && isWorkingTreeComparison(to, from)) {\n\t\t\t\tconst untracked = (await this.provider.status?.getUntrackedFiles?.(repoPath)) ?? [];\n\t\t\t\tif (untracked.length) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfiles: (stat?.files ?? 0) + untracked.length,\n\t\t\t\t\t\tadditions: stat?.additions ?? 0,\n\t\t\t\t\t\tdeletions: stat?.deletions ?? 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn stat;\n\t\t} catch (ex) {\n\t\t\tconst msg: string = ex?.toString() ?? '';\n\t\t\tif (GitErrors.noMergeBase.test(msg) || GitErrors.badRevision.test(msg)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getDiff(\n\t\trepoPath: string,\n\t\tto: string,\n\t\tfrom?: string,\n\t\toptions?: {\n\t\t\tcontext?: number;\n\t\t\tindex?: DisposableTemporaryGitIndex;\n\t\t\tnotation?: GitRevisionRangeNotation;\n\t\t\turis?: (string | Uri)[];\n\t\t},\n\t\t_cancellation?: AbortSignal,\n\t): Promise<GitDiff | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst args = [`-U${options?.context ?? 3}`];\n\n\t\tif (to != null && isRevisionRange(to)) {\n\t\t\tconst parts = getRevisionRangeParts(to);\n\t\t\tif (parts != null) {\n\t\t\t\tto = parts.right ?? '';\n\t\t\t\tfrom = parts.left;\n\t\t\t\toptions = { ...options, notation: parts.notation };\n\t\t\t}\n\t\t}\n\n\t\tfrom = prepareToFromDiffArgs(to, from, args, options?.notation);\n\n\t\tlet paths: Set<string> | undefined;\n\t\tif (options?.uris) {\n\t\t\tpaths = new Set<string>(options.uris.map(p => this.provider.getRelativePath(p, repoPath)));\n\t\t\targs.push('--', ...paths);\n\t\t}\n\n\t\tlet result;\n\t\ttry {\n\t\t\tresult = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsDiff, errors: 'throw', env: options?.index?.env },\n\t\t\t\t'diff',\n\t\t\t\t...args,\n\t\t\t\targs.includes('--') ? undefined : '--',\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst diff: GitDiff = { contents: result.stdout, from: from, to: to, notation: options?.notation };\n\t\treturn diff;\n\t}\n\n\t@debug()\n\tasync getParsedDiff(\n\t\trepoPath: string,\n\t\tto: string,\n\t\tfrom?: string,\n\t\toptions?: {\n\t\t\tcontext?: number;\n\t\t\tnotation?: GitRevisionRangeNotation;\n\t\t\turis?: (string | Uri)[];\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ParsedGitDiff | undefined> {\n\t\tconst diff = await this.getDiff(repoPath, to, from, options, cancellation);\n\t\tif (!diff?.contents) return undefined;\n\n\t\treturn parseGitDiff(diff.contents);\n\t}\n\n\t@debug({ args: repoPath => ({ repoPath: repoPath }) })\n\tasync getDiffFiles(\n\t\trepoPath: string,\n\t\tcontents: string,\n\t\t_cancellation?: AbortSignal,\n\t): Promise<GitDiffFiles | undefined> {\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, configs: gitConfigsLog, stdin: contents },\n\t\t\t'apply',\n\t\t\t'--numstat',\n\t\t\t'--summary',\n\t\t\t'-z',\n\t\t\t'-',\n\t\t);\n\t\tif (!result.stdout) return undefined;\n\n\t\tconst files = parseGitApplyFiles(result.stdout, repoPath);\n\t\treturn {\n\t\t\tfiles: files,\n\t\t};\n\t}\n\n\t@debug()\n\tasync getDiffStatus(\n\t\trepoPath: string,\n\t\tref1OrRange: string | GitRevisionRange,\n\t\tref2?: string,\n\t\toptions?: {\n\t\t\tfilters?: GitDiffFilter[];\n\t\t\tincludeUntracked?: boolean;\n\t\t\tpath?: string;\n\t\t\trenameLimit?: number;\n\t\t\tsimilarityThreshold?: number;\n\t\t},\n\t): Promise<GitFile[] | undefined> {\n\t\ttry {\n\t\t\tconst similarityThreshold =\n\t\t\t\toptions?.similarityThreshold ?? this.context.config?.commits.similarityThreshold;\n\t\t\tconst configs =\n\t\t\t\toptions?.renameLimit != null\n\t\t\t\t\t? [...gitConfigsDiff, '-c', `diff.renameLimit=${options.renameLimit}`]\n\t\t\t\t\t: gitConfigsDiff;\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: configs },\n\t\t\t\t'diff',\n\t\t\t\t'--numstat',\n\t\t\t\t'--summary',\n\t\t\t\t`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`,\n\t\t\t\t'--no-ext-diff',\n\t\t\t\t'-z',\n\t\t\t\toptions?.filters?.length ? `--diff-filter=${options.filters.join('')}` : undefined,\n\t\t\t\tref1OrRange ? ref1OrRange : undefined,\n\t\t\t\tref2 ? ref2 : undefined,\n\t\t\t\t'--',\n\t\t\t\toptions?.path ? options.path : undefined,\n\t\t\t);\n\n\t\t\tconst files = result.stdout ? (parseGitDiffNumStatFiles(result.stdout, repoPath) ?? []) : [];\n\n\t\t\t// Merge in untracked files when the comparison involves the working tree.\n\t\t\t// Skip when `filters` excludes additions (untracked entries are semantically 'A'/added),\n\t\t\t// or when `path` is a non-exact pathspec we can't reliably match against the untracked list.\n\t\t\tif (\n\t\t\t\toptions?.includeUntracked &&\n\t\t\t\tref2 == null &&\n\t\t\t\ttypeof ref1OrRange === 'string' &&\n\t\t\t\t!isRevisionRange(ref1OrRange) &&\n\t\t\t\t!isUncommittedStaged(ref1OrRange) &&\n\t\t\t\tfiltersAllowUntracked(options.filters) &&\n\t\t\t\t!isNonExactPathspec(options.path)\n\t\t\t) {\n\t\t\t\tconst untracked = (await this.provider.status?.getUntrackedFiles?.(repoPath)) ?? [];\n\t\t\t\tif (untracked.length) {\n\t\t\t\t\tconst seen = new Set(files.map(f => f.path));\n\t\t\t\t\tfor (const file of untracked) {\n\t\t\t\t\t\tif (seen.has(file.path)) continue;\n\t\t\t\t\t\tif (options.path && options.path !== file.path) continue;\n\n\t\t\t\t\t\tfiles.push(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn files.length ? files : undefined;\n\t\t} catch (_ex) {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getDiffTool(repoPath?: string): Promise<string | undefined> {\n\t\treturn (\n\t\t\t(await this.provider.config.getConfig?.(repoPath, 'diff.guitool', { runGitLocally: true })) ??\n\t\t\tthis.provider.config.getConfig?.(repoPath, 'diff.tool', { runGitLocally: true })\n\t\t);\n\t}\n\n\t@debug()\n\tasync getNextComparisonUris(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\trev: string | undefined,\n\t\tskip: number = 0,\n\t\toptions?: { ordering?: 'date' | 'author-date' | 'topo' | null },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<NextComparisonUrisResult | undefined> {\n\t\t// If we have no revision there is no next commit\n\t\tif (!rev) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tlet relativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\n\t\tif (isUncommittedStaged(rev)) {\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, relativePath, rev),\n\t\t\t\tnext: createRevisionUri(this.provider, repoPath, relativePath, undefined),\n\t\t\t};\n\t\t} else if (isUncommitted(rev)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parser = getShaAndFileSummaryLogParser();\n\t\t\tconst args = ['log', ...parser.arguments];\n\n\t\t\tconst ordering = options?.ordering ?? this.context.config?.commits.ordering;\n\t\t\tif (ordering) {\n\t\t\t\targs.push(`--${ordering}-order`);\n\t\t\t}\n\n\t\t\targs.push(\n\t\t\t\t// Use reverse to get the next commits (oldest to newest instead of newest to oldest)\n\t\t\t\t'--reverse',\n\t\t\t);\n\n\t\t\tif (rev !== deletedOrMissing) {\n\t\t\t\targs.push(\n\t\t\t\t\t// Ancestry path to ensure we only follow the path from the given revision to HEAD\n\t\t\t\t\t'--ancestry-path',\n\t\t\t\t\t// Range from the given revision to HEAD\n\t\t\t\t\t`${rev}..HEAD`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Follow file history and specify the file path\n\t\t\targs.push('--follow', '--', relativePath);\n\n\t\t\t// Unbounded --follow walk over file history can be slow on long histories — queue as background\n\t\t\t// so it doesn't compete with interactive work.\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsLog, priority: 'background' },\n\t\t\t\t...args,\n\t\t\t);\n\n\t\t\tlet currentSha;\n\t\t\tlet currentPath;\n\n\t\t\tif (skip === 0) {\n\t\t\t\tcurrentSha = rev;\n\t\t\t\tcurrentPath = relativePath;\n\t\t\t} else {\n\t\t\t\tskip--;\n\t\t\t}\n\n\t\t\tlet nextSha;\n\t\t\tlet nextPath;\n\n\t\t\tlet file;\n\t\t\tfor (const commit of parser.parse(result.stdout)) {\n\t\t\t\tconst path = relativePath;\n\t\t\t\tfile = commit.files.find(f => f.path === path || f.originalPath === path);\n\t\t\t\t// Keep track of the file changing paths\n\t\t\t\tif (file?.path && file.path !== relativePath) {\n\t\t\t\t\trelativePath = file.path;\n\t\t\t\t}\n\n\t\t\t\tif (skip > 0) {\n\t\t\t\t\tskip--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (currentSha == null) {\n\t\t\t\t\tcurrentSha = commit.sha;\n\t\t\t\t\tcurrentPath = file?.path ?? relativePath;\n\t\t\t\t} else if (nextSha == null) {\n\t\t\t\t\t// if (commit.sha === rev) continue;\n\n\t\t\t\t\tnextSha = commit.sha;\n\t\t\t\t\tnextPath = file?.path ?? relativePath;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentSha == null || currentPath == null) {\n\t\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, relativePath);\n\t\t\t\tif (status != null) {\n\t\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\t\tcurrentSha = uncommittedStaged;\n\t\t\t\t\t\tcurrentPath = status.originalPath ?? status.path;\n\t\t\t\t\t\tnextSha = '';\n\t\t\t\t\t\tnextPath = status.path;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextSha == null || nextPath == null) {\n\t\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, relativePath);\n\t\t\t\tif (status != null) {\n\t\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\t\tnextSha = uncommittedStaged;\n\t\t\t\t\t\tnextPath = status.originalPath ?? status.path;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextSha = '';\n\t\t\t\t\t\tnextPath = status.path;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tnextSha = '';\n\t\t\t\t\tnextPath = relativePath;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha || undefined),\n\t\t\t\tnext: createRevisionUri(\n\t\t\t\t\tthis.provider,\n\t\t\t\t\trepoPath,\n\t\t\t\t\tnextPath ?? currentPath,\n\t\t\t\t\t(nextSha ?? deletedOrMissing) || undefined,\n\t\t\t\t),\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getPreviousComparisonUris(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\trev: string | undefined,\n\t\tskip: number = 0,\n\t\tunsaved?: boolean,\n\t\toptions?: { ordering?: 'date' | 'author-date' | 'topo' | null },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<PreviousComparisonUrisResult | undefined> {\n\t\tif (rev === deletedOrMissing) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tif (rev === uncommitted) {\n\t\t\trev = undefined;\n\t\t}\n\n\t\t/* Rules:\n\t\t * Starting from working tree (rev is empty or undefined):\n\t\t * 1. Check file status:\n\t\t * 1.1. If file has both staged and working (or unsaved) changes:\n\t\t * - If skip=0: diff working with staged\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.2. If file has only staged changes:\n\t\t * - If skip=0: diff working with HEAD\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.3. If file has only working changes:\n\t\t * - If skip=0: diff working with HEAD\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.4. If file has no changes (or no status found):\n\t\t * - If skip=0: diff working with HEAD~1\n\t\t * - If skip>0: skip through revs starting at HEAD~1\n\t\t *\n\t\t * Starting from staged (rev is uncommittedStaged):\n\t\t * 1. Check file status:\n\t\t * 1.1. If status exists:\n\t\t * - If skip=0: diff staged with HEAD\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.2. If status doesn't exist:\n\t\t * - If skip=0: diff HEAD with HEAD~1\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * Starting from a commit (rev is a SHA):\n\t\t * - If skip=0: diff SHA with SHA^\n\t\t * - If skip>0: skip through revs starting at SHA\n\t\t */\n\n\t\tlet relativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\t\tlet skipPrev = 0;\n\n\t\tlet revs: [string, string][] = [];\n\t\tif (!rev) {\n\t\t\trevs = [['', relativePath]];\n\n\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, pathOrUri);\n\t\t\tif (status != null) {\n\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\trevs.push([uncommittedStaged, status.originalPath ?? status.path]);\n\t\t\t\t}\n\n\t\t\t\trevs.push(['HEAD', status.originalPath ?? status.path]);\n\t\t\t\tif (status.workingTreeStatus == null && !unsaved) {\n\t\t\t\t\tif (skip === 0 || status.indexStatus == null) {\n\t\t\t\t\t\t// Skip over the HEAD commit to get a diff, because working and HEAD are the same\n\t\t\t\t\t\tskipPrev++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trevs.push(['HEAD', relativePath]);\n\t\t\t\tif (!unsaved) {\n\t\t\t\t\t// Skip over the HEAD commit to get a diff, because working and HEAD are the same\n\t\t\t\t\tskipPrev++;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (isUncommittedStaged(rev)) {\n\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, pathOrUri);\n\t\t\tif (status != null) {\n\t\t\t\trevs = [\n\t\t\t\t\t[uncommittedStaged, status.originalPath ?? status.path],\n\t\t\t\t\t['HEAD', status.originalPath ?? status.path],\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t// Even though we supposedly started at staged, there is no staged version\n\t\t\t\trevs = [['HEAD', relativePath]];\n\t\t\t\tif (skip > 0) {\n\t\t\t\t\tskip--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet currentSha;\n\t\tlet currentPath;\n\n\t\tif (revs.length) {\n\t\t\tskip++;\n\t\t\twhile (revs.length && skip > 0) {\n\t\t\t\tskip--;\n\t\t\t\t[rev, relativePath] = revs.shift()!;\n\t\t\t}\n\n\t\t\tif (rev !== 'HEAD') {\n\t\t\t\tcurrentSha = rev;\n\t\t\t\tcurrentPath = relativePath;\n\n\t\t\t\tskipPrev++;\n\t\t\t\twhile (revs.length && skipPrev > 0) {\n\t\t\t\t\tskipPrev--;\n\t\t\t\t\t[rev, relativePath] = revs.shift()!;\n\t\t\t\t}\n\n\t\t\t\tif (skipPrev > 0) {\n\t\t\t\t\tskip += skipPrev;\n\t\t\t\t}\n\n\t\t\t\tif (rev !== 'HEAD' && skip === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha),\n\t\t\t\t\t\tprevious: createRevisionUri(this.provider, repoPath, relativePath, rev),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst parser = getShaAndFileSummaryLogParser();\n\t\t\tconst args = ['log', ...parser.arguments, `-n${skip + 2}`]; // Don't use --skip as it doesn't work with --follow\n\n\t\t\tconst ordering = options?.ordering ?? this.context.config?.commits.ordering;\n\t\t\tif (ordering) {\n\t\t\t\targs.push(`--${ordering}-order`);\n\t\t\t}\n\n\t\t\targs.push('--follow', rev!, '--', relativePath);\n\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{\n\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\tconfigs: gitConfigsLog,\n\t\t\t\t\tcaching: { cache: this.cache.gitResults, options: { accessTTL: 5 * 60 * 1000 } },\n\t\t\t\t},\n\t\t\t\t...args,\n\t\t\t);\n\n\t\t\tlet previousSha;\n\t\t\tlet previousPath;\n\t\t\tlet file;\n\n\t\t\tfor (const commit of parser.parse(result.stdout)) {\n\t\t\t\tconst path = relativePath;\n\t\t\t\tfile = commit.files.find(f => f.path === path || f.originalPath === path);\n\t\t\t\t// Keep track of the file changing paths\n\t\t\t\tif (file?.originalPath && file.originalPath !== relativePath) {\n\t\t\t\t\trelativePath = file.originalPath;\n\t\t\t\t}\n\n\t\t\t\tif (skip > 0) {\n\t\t\t\t\tskip--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (currentSha == null) {\n\t\t\t\t\tcurrentSha = commit.sha;\n\t\t\t\t\tcurrentPath = file?.path ?? relativePath;\n\t\t\t\t} else if (previousSha == null) {\n\t\t\t\t\tif (commit.sha === rev) continue;\n\n\t\t\t\t\tpreviousSha = commit.sha;\n\t\t\t\t\tpreviousPath = file?.path ?? relativePath;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentSha == null || currentPath == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha || undefined),\n\t\t\t\tprevious: createRevisionUri(\n\t\t\t\t\tthis.provider,\n\t\t\t\t\trepoPath,\n\t\t\t\t\tpreviousPath ?? currentPath,\n\t\t\t\t\tpreviousSha ?? deletedOrMissing,\n\t\t\t\t),\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getPreviousComparisonUrisForRange(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\trev: string | undefined,\n\t\trange: DiffRange,\n\t\toptions?: { ordering?: 'date' | 'author-date' | 'topo' | null; skipFirstRev?: boolean },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<PreviousRangeComparisonUrisResult | undefined> {\n\t\tif (rev === deletedOrMissing) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tif (rev === uncommitted) {\n\t\t\trev = undefined;\n\t\t}\n\n\t\tlet currentSha;\n\t\tlet currentPath;\n\t\tlet relativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\t\tconst skipFirstRev = options?.skipFirstRev ?? true;\n\n\t\t// If we are at the working tree (i.e. no ref), we need to dig deeper to figure out where to go\n\t\tif (!rev) {\n\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, pathOrUri);\n\t\t\tif (status != null) {\n\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\tif (status.workingTreeStatus != null && !skipFirstRev) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, relativePath, undefined),\n\t\t\t\t\t\t\tprevious: createRevisionUri(this.provider, repoPath, relativePath, uncommittedStaged),\n\t\t\t\t\t\t\trange: range,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentSha = uncommittedStaged;\n\t\t\t\t\tcurrentPath = status.originalPath ?? status.path;\n\t\t\t\t\tif (status.originalPath != null) {\n\t\t\t\t\t\trelativePath = status.originalPath;\n\t\t\t\t\t}\n\t\t\t\t\trev = uncommittedStaged;\n\t\t\t\t} else if (status.workingTreeStatus != null && !skipFirstRev) {\n\t\t\t\t\tcurrentSha = '';\n\t\t\t\t\tcurrentPath = relativePath;\n\t\t\t\t\trev = '';\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (!skipFirstRev) {\n\t\t\tcurrentSha = rev;\n\t\t\tcurrentPath = relativePath;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parser = getShaAndFileRangeLogParser();\n\t\t\tconst args = ['log', ...parser.arguments, '-n2']; // Don't use --skip as it doesn't work with --follow\n\n\t\t\tconst ordering = options?.ordering ?? this.context.config?.commits.ordering;\n\t\t\tif (ordering) {\n\t\t\t\targs.push(`--${ordering}-order`);\n\t\t\t}\n\n\t\t\tif (rev && !isUncommittedStaged(rev)) {\n\t\t\t\targs.push(rev);\n\t\t\t}\n\n\t\t\targs.push(`-L${range.startLine},${range.endLine}:${relativePath}`);\n\n\t\t\tlet result: GitResult;\n\t\t\ttry {\n\t\t\t\tresult = await this.git.run({ cwd: repoPath, configs: gitConfigsLog }, ...args);\n\t\t\t} catch (ex) {\n\t\t\t\tif (rev && !isUncommittedStaged(rev)) throw ex;\n\n\t\t\t\t// If the line count is invalid reset to a valid range\n\t\t\t\tconst match = GitErrors.invalidLineCount.exec(ex?.toString() ?? '');\n\t\t\t\tif (match == null) throw ex;\n\n\t\t\t\tconst line = parseInt(match[1], 10);\n\t\t\t\tif (isNaN(line)) throw ex;\n\n\t\t\t\tconst index = args.findIndex(a => a.startsWith('-L'));\n\t\t\t\tif (index === -1) throw ex;\n\n\t\t\t\trange = {\n\t\t\t\t\tstartLine: Math.min(range.startLine, line),\n\t\t\t\t\tstartCharacter: range.startCharacter,\n\t\t\t\t\tendLine: Math.min(range.endLine, line),\n\t\t\t\t\tendCharacter: range.endCharacter,\n\t\t\t\t\tactive: range.active,\n\t\t\t\t};\n\n\t\t\t\targs.splice(index, 1, `-L${range.startLine},${range.endLine}:${relativePath}`);\n\t\t\t\tresult = await this.git.run({ cwd: repoPath, configs: gitConfigsLog }, ...args);\n\t\t\t}\n\n\t\t\tlet currentRange;\n\t\t\tlet previousSha;\n\t\t\tlet previousPath;\n\t\t\tlet file;\n\n\t\t\tfor (const commit of parser.parse(result.stdout)) {\n\t\t\t\tconst path = relativePath;\n\t\t\t\tfile = commit.files.find(f => f.path === path || f.originalPath === path);\n\t\t\t\t// If we couldn't find the file, but there is only one file in the commit, use that one and assume the filename changed\n\t\t\t\tif (file == null && commit.files.length === 1) {\n\t\t\t\t\tfile = commit.files[0];\n\t\t\t\t\trelativePath = file.path;\n\t\t\t\t}\n\t\t\t\t// Keep track of the file changing paths\n\t\t\t\tif (file?.originalPath && file.originalPath !== relativePath) {\n\t\t\t\t\trelativePath = file.originalPath;\n\t\t\t\t}\n\n\t\t\t\tif (currentSha == null) {\n\t\t\t\t\tcurrentSha = commit.sha;\n\t\t\t\t\tcurrentPath = file?.path ?? relativePath;\n\t\t\t\t\tcurrentRange =\n\t\t\t\t\t\tfile?.range != null ? { ...file.range, startCharacter: 1, endCharacter: 1 } : undefined;\n\t\t\t\t} else if (previousSha == null) {\n\t\t\t\t\tif (commit.sha === rev) continue;\n\n\t\t\t\t\tpreviousSha = commit.sha;\n\t\t\t\t\tpreviousPath = file?.path ?? relativePath;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentSha == null || currentPath == null) return undefined;\n\n\t\t\t// If we have no previous SHA but have a real current SHA, resolve the parent commit.\n\t\t\t// This handles the case where a line is newly added — the line has no prior history,\n\t\t\t// but the file likely existed at the parent commit\n\t\t\tif (previousSha == null && currentSha) {\n\t\t\t\tpreviousSha =\n\t\t\t\t\t(await this.provider.refs.validateReference(repoPath, `${currentSha}^`)) ?? deletedOrMissing;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha || undefined),\n\t\t\t\tprevious: createRevisionUri(\n\t\t\t\t\tthis.provider,\n\t\t\t\t\trepoPath,\n\t\t\t\t\tpreviousPath ?? currentPath,\n\t\t\t\t\tpreviousSha ?? deletedOrMissing,\n\t\t\t\t),\n\t\t\t\trange: currentRange ?? range,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/** Returns raw diff output for a file between two revisions. */\n\tasync diff(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref1?: string,\n\t\tref2?: string,\n\t\toptions?: {\n\t\t\tencoding?: string;\n\t\t\tfilters?: GitDiffFilter[];\n\t\t\tlinesOfContext?: number;\n\t\t\trenames?: boolean;\n\t\t\tsimilarityThreshold?: number | null;\n\t\t},\n\t): Promise<GitResult> {\n\t\tconst params = ['diff', '--no-ext-diff', '--minimal'];\n\n\t\tif (options?.linesOfContext != null) {\n\t\t\tparams.push(`-U${options.linesOfContext}`);\n\t\t}\n\n\t\tif (options?.renames) {\n\t\t\tparams.push(`-M${options.similarityThreshold == null ? '' : `${options.similarityThreshold}%`}`);\n\t\t}\n\n\t\tif (options?.filters != null && options.filters.length !== 0) {\n\t\t\tparams.push(`--diff-filter=${options.filters.join('')}`);\n\t\t}\n\n\t\tif (ref1) {\n\t\t\t// <sha>^3 signals an untracked file in a stash and if we are trying to find its parent, use the root sha\n\t\t\tif (ref1.endsWith('^3^')) {\n\t\t\t\tref1 = rootSha;\n\t\t\t}\n\t\t\tparams.push(isUncommittedStaged(ref1) ? '--staged' : ref1);\n\t\t}\n\t\tif (ref2) {\n\t\t\tparams.push(isUncommittedStaged(ref2) ? '--staged' : ref2);\n\t\t}\n\n\t\ttry {\n\t\t\treturn await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsDiff, encoding: options?.encoding },\n\t\t\t\t...params,\n\t\t\t\t'--',\n\t\t\t\tfileName,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tconst match = GitErrors.badRevision.exec(ex.message);\n\t\t\tif (match !== null) {\n\t\t\t\tconst [, ref] = match;\n\n\t\t\t\t// If the bad ref is trying to find a parent ref, assume we hit to the last commit, so try again using the root sha\n\t\t\t\tif (ref === ref1 && ref?.endsWith('^')) {\n\t\t\t\t\treturn this.diff(repoPath, fileName, rootSha, ref2, options);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/** Returns raw diff output comparing a file's revision with given contents via stdin. */\n\tasync diffContents(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref: string,\n\t\tcontents: string,\n\t\toptions?: { encoding?: string; filters?: GitDiffFilter[]; similarityThreshold?: number | null },\n\t): Promise<string> {\n\t\tconst params = [\n\t\t\t'diff',\n\t\t\t`-M${options?.similarityThreshold == null ? '' : `${options.similarityThreshold}%`}`,\n\t\t\t'--no-ext-diff',\n\t\t\t'-U0',\n\t\t\t'--minimal',\n\t\t];\n\n\t\tif (options?.filters != null && options.filters.length !== 0) {\n\t\t\tparams.push(`--diff-filter=${options.filters.join('')}`);\n\t\t}\n\n\t\tparams.push('--no-index');\n\n\t\ttry {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{\n\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\tconfigs: gitConfigsDiff,\n\t\t\t\t\tencoding: options?.encoding,\n\t\t\t\t\tstdin: contents,\n\t\t\t\t},\n\t\t\t\t...params,\n\t\t\t\t'--',\n\t\t\t\tfileName,\n\t\t\t\t// Pipe the contents to stdin\n\t\t\t\t'-',\n\t\t\t);\n\t\t\treturn result.stdout;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof GitError && ex.stdout) {\n\t\t\t\treturn ex.stdout;\n\t\t\t}\n\n\t\t\tconst match = GitErrors.badRevision.exec(ex.message);\n\t\t\tif (match !== null) {\n\t\t\t\tconst [, matchedRef] = match;\n\n\t\t\t\t// If the bad ref is trying to find a parent ref, assume we hit to the last commit, so try again using the root sha\n\t\t\t\tif (matchedRef === ref && matchedRef?.endsWith('^')) {\n\t\t\t\t\treturn this.diffContents(repoPath, fileName, rootSha, contents, options);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/** Returns parsed diff hunks for a file between two revisions. */\n\t@debug()\n\tgetDiffForFile(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref1: string | undefined,\n\t\tref2?: string,\n\t\toptions?: { encoding?: string },\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\tconst cacheKey = `${normalizePath(fileName)}:${ref1 ?? ''}:${ref2 ?? ''}`;\n\t\treturn this.cache.diff.getOrCreate(\n\t\t\trepoPath,\n\t\t\tcacheKey,\n\t\t\t() => this.getDiffForFileCore(repoPath, fileName, ref1, ref2, options),\n\t\t\t{ errorTTL: 1000 * 60 },\n\t\t);\n\t}\n\n\tprivate async getDiffForFileCore(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref1: string | undefined,\n\t\tref2: string | undefined,\n\t\toptions: { encoding?: string } | undefined,\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\t// Include renames (R) and copies (C) so content diffs on renamed files still surface;\n\t\t// --diff-filter runs after `-M` rename detection, so 'M' alone would drop valid rename diffs.\n\t\tconst result = await this.diff(repoPath, fileName, ref1, ref2, {\n\t\t\tencoding: options?.encoding,\n\t\t\tfilters: ['M', 'R', 'C'],\n\t\t\tlinesOfContext: 0,\n\t\t\trenames: true,\n\t\t\tsimilarityThreshold: this.context.config?.commits.similarityThreshold,\n\t\t});\n\t\treturn parseGitFileDiff(result.stdout);\n\t}\n\n\t/** Returns parsed diff hunks comparing a file's revision with given contents via stdin. */\n\t@debug()\n\tgetDiffForFileContents(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref: string,\n\t\tcontents: string,\n\t\toptions?: { encoding?: string },\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\tconst cacheKey = `${normalizePath(fileName)}:${ref}:~${fnv1aHash(contents)}`;\n\t\treturn this.cache.diff.getOrCreate(\n\t\t\trepoPath,\n\t\t\tcacheKey,\n\t\t\t() => this.getDiffForFileContentsCore(repoPath, fileName, ref, contents, options),\n\t\t\t{ errorTTL: 1000 * 60 },\n\t\t);\n\t}\n\n\tprivate async getDiffForFileContentsCore(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref: string,\n\t\tcontents: string,\n\t\toptions: { encoding?: string } | undefined,\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\tconst data = await this.diffContents(repoPath, fileName, ref, contents, {\n\t\t\tencoding: options?.encoding,\n\t\t\tfilters: ['M', 'R', 'C'],\n\t\t\tsimilarityThreshold: this.context.config?.commits.similarityThreshold,\n\t\t});\n\t\treturn parseGitFileDiff(data);\n\t}\n\n\t/** Finds the most recent commit where the given path was copied, renamed, or deleted. */\n\t@debug()\n\tasync findPathStatusChanged(\n\t\trepoPath: string,\n\t\tpathspec: string,\n\t\trev: string | undefined,\n\t\toptions?: { filters?: GitDiffFilter[]; ordering?: 'date' | 'author-date' | 'topo' | null },\n\t): Promise<{ sha: string; file: LogParsedFile | undefined } | undefined> {\n\t\treturn findPathStatusChanged(this.git, repoPath, pathspec, rev, options);\n\t}\n\n\t@debug()\n\tasync openDiffTool(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\toptions?: { ref1?: string; ref2?: string; staged?: boolean; tool?: string },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\t\tconst [relativePath, root] = splitPath(toFsPath(pathOrUri), repoPath);\n\n\t\ttry {\n\t\t\tlet tool = options?.tool;\n\t\t\tif (!tool) {\n\t\t\t\tconst scope = getScopedLogger();\n\n\t\t\t\ttool = await this.getDiffTool(root);\n\t\t\t\tif (tool == null) throw new Error('No diff tool found');\n\n\t\t\t\tscope?.debug(`Using tool=${tool}`);\n\t\t\t}\n\n\t\t\tawait this.git.run(\n\t\t\t\t{ cwd: root },\n\t\t\t\t'difftool',\n\t\t\t\t'--no-prompt',\n\t\t\t\t`--tool=${tool}`,\n\t\t\t\toptions?.staged ? '--staged' : undefined,\n\t\t\t\toptions?.ref1 ? options.ref1 : undefined,\n\t\t\t\toptions?.ref2 ? options.ref2 : undefined,\n\t\t\t\t'--',\n\t\t\t\trelativePath,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tconst msg: string = ex?.toString() ?? '';\n\t\t\tif ((ex instanceof Error && ex.message === 'No diff tool found') || /Unknown .+? tool/.test(msg)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tl10n.t(\n\t\t\t\t\t\t'Unable to open changes because the specified diff tool cannot be found or no Git diff tool is configured',\n\t\t\t\t\t),\n\t\t\t\t\t{ cause: ex },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync openDirectoryCompare(repoPath: string, ref1: string, ref2?: string, tool?: string): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tif (!tool) {\n\t\t\t\tconst scope = getScopedLogger();\n\n\t\t\t\ttool = await this.getDiffTool(repoPath);\n\t\t\t\tif (tool == null) throw new Error('No diff tool found');\n\n\t\t\t\tscope?.debug(`Using tool=${tool}`);\n\t\t\t}\n\n\t\t\tawait this.git.run({ cwd: repoPath }, 'difftool', '--dir-diff', `--tool=${tool}`, ref1, ref2);\n\t\t} catch (ex) {\n\t\t\tconst msg: string = ex?.toString() ?? '';\n\t\t\tif ((ex instanceof Error && ex.message === 'No diff tool found') || /Unknown .+? tool/.test(msg)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tl10n.t(\n\t\t\t\t\t\t'Unable to open directory compare because the specified diff tool cannot be found or no Git diff tool is configured',\n\t\t\t\t\t),\n\t\t\t\t\t{ cause: ex },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n}\n// Returns true when no --diff-filter is active or when it includes additions ('A' or '*').\n// Untracked files are semantically \"added\" — if a caller restricts filters to e.g. ['M'],\n// honor that and omit untracked.\nfunction filtersAllowUntracked(filters: GitDiffFilter[] | undefined): boolean {\n\tif (!filters?.length) return true;\n\treturn filters.includes('A') || filters.includes('*');\n}\n\n// Returns true when the path is a git pathspec we can't match with simple equality\n// (directory prefix, glob, or magic pathspec). In that case, skip the untracked merge\n// rather than risk excluding legitimate matches or including wrong ones.\nfunction isNonExactPathspec(path: string | undefined): boolean {\n\tif (!path) return false;\n\treturn path.endsWith('/') || /[*?[\\]]|^:/.test(path);\n}\n\n// Returns true when the `git diff` invocation shape includes the working tree\n// (i.e. `git diff`, `git diff HEAD`, or `git diff <ref>` with no `from`), and\n// therefore untracked files are a meaningful addition to the result.\nfunction isWorkingTreeComparison(to: string | undefined, from: string | undefined): boolean {\n\tif (to == null) return true;\n\tif (isRevisionRange(to) || isUncommittedStaged(to)) return false;\n\n\t// `to === uncommitted` always targets the working tree (`prepareToFromDiffArgs` sets\n\t// `from = 'HEAD'` when omitted, or diffs against the caller-supplied `from`).\n\tif (isUncommitted(to)) return true;\n\n\t// `git diff <from>` — `to === ''` means the caller explicitly asked for working tree vs `from`\n\tif (to === '') return from == null || !isUncommittedStaged(from);\n\n\t// `getChangedFilesCount` uses `prepareToFromDiffArgs`, which translates `to` (with no `from`)\n\t// to `${to}^ ${to}` UNLESS `to` is HEAD or empty.\n\tif (to.toUpperCase() === 'HEAD' && from == null) return true;\n\treturn false;\n}\n\nfunction prepareToFromDiffArgs(\n\tto: string,\n\tfrom: string | undefined,\n\targs: string[],\n\tnotation?: GitRevisionRangeNotation,\n): string {\n\tif (to === uncommitted) {\n\t\tif (from != null) {\n\t\t\targs.push(from);\n\t\t} else {\n\t\t\t// Get only unstaged changes\n\t\t\tfrom = 'HEAD';\n\t\t}\n\t} else if (to === uncommittedStaged) {\n\t\targs.push('--staged');\n\t\tif (from != null) {\n\t\t\targs.push(from);\n\t\t} else {\n\t\t\t// Get only staged changes\n\t\t\tfrom = 'HEAD';\n\t\t}\n\t} else if (from == null) {\n\t\tif (to === '' || to.toUpperCase() === 'HEAD') {\n\t\t\tfrom = 'HEAD';\n\t\t\targs.push(from);\n\t\t} else {\n\t\t\tfrom = `${to}^`;\n\t\t\targs.push(from, to);\n\t\t}\n\t} else if (to === '') {\n\t\targs.push(from);\n\t} else if (notation != null) {\n\t\targs.push(`${from}${notation}${to}`);\n\t} else {\n\t\targs.push(from, to);\n\t}\n\treturn from;\n}\n\nfunction createRevisionUri(\n\tprovider: CliGitProviderInternal,\n\trepoPath: string,\n\trelativePath: string,\n\tsha: string | undefined,\n): RevisionUri {\n\treturn {\n\t\turi: sha\n\t\t\t? fromUri({\n\t\t\t\t\tscheme: 'gitlens',\n\t\t\t\t\tauthority: encodeGitLensRevisionUriAuthority({ ref: sha, repoPath: repoPath }),\n\t\t\t\t\tpath: provider.getAbsoluteUri(relativePath, repoPath).path,\n\t\t\t\t})\n\t\t\t: provider.getAbsoluteUri(relativePath, repoPath),\n\t\tpath: relativePath,\n\t\tsha: sha,\n\t\trepoPath: repoPath,\n\t};\n}\n\nexport async function findPathStatusChanged(\n\tgit: Git,\n\trepoPath: string,\n\tpathspec: string,\n\trev: string | undefined,\n\toptions?: { filters?: GitDiffFilter[]; ordering?: 'date' | 'author-date' | 'topo' | null },\n): Promise<{ sha: string; file: LogParsedFile | undefined } | undefined> {\n\tconst parser = getShaAndFileSummaryLogParser();\n\n\tconst ordering = options?.ordering;\n\n\tconst result = await git.run(\n\t\t{ cwd: repoPath, configs: gitConfigsLog },\n\t\t'log',\n\t\t...parser.arguments,\n\t\tordering ? `--${ordering}-order` : undefined,\n\t\t'--no-walk',\n\t\t`--diff-filter=${options?.filters?.length ? options.filters.join('') : 'RCD'}`,\n\t\trev,\n\t\t// Note: --no-walk constrains to the exact commit (no ancestor search). Pathspec is omitted\n\t\t// because git suppresses rename detection with pathspecs (shows D instead of R, losing the destination path).\n\t\t'--',\n\t);\n\n\tconst commit = first(parser.parse(result.stdout));\n\tif (commit == null) return undefined;\n\n\tconst file = commit.files.find(f => f.path === pathspec || f.originalPath === pathspec);\n\treturn { sha: commit.sha, file: file };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"diff.js","names":[],"sources":["../../../../git-cli/src/providers/diff.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport type {\n\tGitDiff,\n\tGitDiffFiles,\n\tGitDiffFilter,\n\tGitDiffShortStat,\n\tParsedGitDiff,\n\tParsedGitDiffHunks,\n} from '@gitlens/git/models/diff.js';\nimport type { GitFile } from '@gitlens/git/models/file.js';\nimport type { GitRevisionRange, GitRevisionRangeNotation } from '@gitlens/git/models/revision.js';\nimport { deletedOrMissing, uncommitted, uncommittedStaged } from '@gitlens/git/models/revision.js';\nimport {\n\tparseGitApplyFiles,\n\tparseGitDiff,\n\tparseGitDiffNumStatFiles,\n\tparseGitDiffShortStat,\n\tparseGitFileDiff,\n} from '@gitlens/git/parsers/diffParser.js';\nimport type {\n\tGitDiffSubProvider,\n\tNextComparisonUrisResult,\n\tPreviousComparisonUrisResult,\n\tPreviousRangeComparisonUrisResult,\n} from '@gitlens/git/providers/diff.js';\nimport type { DisposableTemporaryGitIndex } from '@gitlens/git/providers/staging.js';\nimport type { DiffRange, RevisionUri } from '@gitlens/git/providers/types.js';\nimport {\n\tgetRevisionRangeParts,\n\tisRevisionRange,\n\tisUncommitted,\n\tisUncommittedStaged,\n} from '@gitlens/git/utils/revision.utils.js';\nimport { encodeGitLensRevisionUriAuthority } from '@gitlens/git/utils/uriAuthority.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport { fnv1aHash } from '@gitlens/utils/hash.js';\nimport { first } from '@gitlens/utils/iterable.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { normalizePath, splitPath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { fromUri, toFsPath } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport type { GitResult } from '../exec/exec.types.js';\nimport type { Git } from '../exec/git.js';\nimport { gitConfigsDiff, gitConfigsLog, GitError, GitErrors } from '../exec/git.js';\nimport type { LogParsedFile } from '../parsers/logParser.js';\nimport { getShaAndFileRangeLogParser, getShaAndFileSummaryLogParser } from '../parsers/logParser.js';\n\nexport class DiffGitSubProvider implements GitDiffSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {}\n\n\t@debug()\n\tasync getChangedFilesCount(\n\t\trepoPath: string,\n\t\tto?: string,\n\t\tfrom?: string,\n\t\toptions?: { uris?: (string | Uri)[]; includeUntracked?: boolean },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<GitDiffShortStat | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst args: string[] = [];\n\t\tif (to != null) {\n\t\t\t// Handle revision ranges specially if there is no `from`, otherwise `prepareToFromDiffArgs` will duplicate the range\n\t\t\tif (isRevisionRange(to) && from == null) {\n\t\t\t\targs.push(to);\n\t\t\t} else {\n\t\t\t\tprepareToFromDiffArgs(to, from, args);\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsDiff },\n\t\t\t\t'diff',\n\t\t\t\t'--shortstat',\n\t\t\t\t'--no-ext-diff',\n\t\t\t\t...args,\n\t\t\t\t'--',\n\t\t\t\t...(options?.uris?.map(p => this.provider.getRelativePath(p, repoPath)) ?? []),\n\t\t\t);\n\n\t\t\tconst stat = result.stdout ? parseGitDiffShortStat(result.stdout) : undefined;\n\n\t\t\t// Merge in untracked files when the comparison involves the working tree.\n\t\t\t// Skip when a pathspec filter is active — mixing a path-filtered diff count with an\n\t\t\t// unfiltered untracked count would silently produce wrong totals.\n\t\t\tif (options?.includeUntracked && !options.uris?.length && isWorkingTreeComparison(to, from)) {\n\t\t\t\tconst untracked = (await this.provider.status?.getUntrackedFiles?.(repoPath)) ?? [];\n\t\t\t\tif (untracked.length) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tfiles: (stat?.files ?? 0) + untracked.length,\n\t\t\t\t\t\tadditions: stat?.additions ?? 0,\n\t\t\t\t\t\tdeletions: stat?.deletions ?? 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn stat;\n\t\t} catch (ex) {\n\t\t\tconst msg: string = ex?.toString() ?? '';\n\t\t\tif (GitErrors.noMergeBase.test(msg) || GitErrors.badRevision.test(msg)) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getDiff(\n\t\trepoPath: string,\n\t\tto: string,\n\t\tfrom?: string,\n\t\toptions?: {\n\t\t\tcontext?: number;\n\t\t\tindex?: DisposableTemporaryGitIndex;\n\t\t\tnotation?: GitRevisionRangeNotation;\n\t\t\turis?: (string | Uri)[];\n\t\t},\n\t\t_cancellation?: AbortSignal,\n\t): Promise<GitDiff | undefined> {\n\t\tconst scope = getScopedLogger();\n\t\tconst args = [`-U${options?.context ?? 3}`];\n\n\t\tif (to != null && isRevisionRange(to)) {\n\t\t\tconst parts = getRevisionRangeParts(to);\n\t\t\tif (parts != null) {\n\t\t\t\tto = parts.right ?? '';\n\t\t\t\tfrom = parts.left;\n\t\t\t\toptions = { ...options, notation: parts.notation };\n\t\t\t}\n\t\t}\n\n\t\tfrom = prepareToFromDiffArgs(to, from, args, options?.notation);\n\n\t\tlet paths: Set<string> | undefined;\n\t\tif (options?.uris) {\n\t\t\tpaths = new Set<string>(options.uris.map(p => this.provider.getRelativePath(p, repoPath)));\n\t\t\targs.push('--', ...paths);\n\t\t}\n\n\t\tlet result;\n\t\ttry {\n\t\t\tresult = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsDiff, errors: 'throw', env: options?.index?.env },\n\t\t\t\t'diff',\n\t\t\t\t...args,\n\t\t\t\targs.includes('--') ? undefined : '--',\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\treturn undefined;\n\t\t}\n\n\t\tconst diff: GitDiff = { contents: result.stdout, from: from, to: to, notation: options?.notation };\n\t\treturn diff;\n\t}\n\n\t@debug()\n\tasync getParsedDiff(\n\t\trepoPath: string,\n\t\tto: string,\n\t\tfrom?: string,\n\t\toptions?: {\n\t\t\tcontext?: number;\n\t\t\tnotation?: GitRevisionRangeNotation;\n\t\t\turis?: (string | Uri)[];\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<ParsedGitDiff | undefined> {\n\t\tconst diff = await this.getDiff(repoPath, to, from, options, cancellation);\n\t\tif (!diff?.contents) return undefined;\n\n\t\treturn parseGitDiff(diff.contents);\n\t}\n\n\t@debug({ args: repoPath => ({ repoPath: repoPath }) })\n\tasync getDiffFiles(\n\t\trepoPath: string,\n\t\tcontents: string,\n\t\t_cancellation?: AbortSignal,\n\t): Promise<GitDiffFiles | undefined> {\n\t\tconst result = await this.git.run(\n\t\t\t{ cwd: repoPath, configs: gitConfigsLog, stdin: contents },\n\t\t\t'apply',\n\t\t\t'--numstat',\n\t\t\t'--summary',\n\t\t\t'-z',\n\t\t\t'-',\n\t\t);\n\t\tif (!result.stdout) return undefined;\n\n\t\tconst files = parseGitApplyFiles(result.stdout, repoPath);\n\t\treturn {\n\t\t\tfiles: files,\n\t\t};\n\t}\n\n\t@debug()\n\tasync getDiffStatus(\n\t\trepoPath: string,\n\t\tref1OrRange: string | GitRevisionRange,\n\t\tref2?: string,\n\t\toptions?: {\n\t\t\tfilters?: GitDiffFilter[];\n\t\t\tincludeUntracked?: boolean;\n\t\t\tpath?: string;\n\t\t\trenameLimit?: number;\n\t\t\tsimilarityThreshold?: number;\n\t\t},\n\t): Promise<GitFile[] | undefined> {\n\t\ttry {\n\t\t\tconst similarityThreshold =\n\t\t\t\toptions?.similarityThreshold ?? this.context.config?.commits.similarityThreshold;\n\t\t\tconst configs =\n\t\t\t\toptions?.renameLimit != null\n\t\t\t\t\t? [...gitConfigsDiff, '-c', `diff.renameLimit=${options.renameLimit}`]\n\t\t\t\t\t: gitConfigsDiff;\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: configs },\n\t\t\t\t'diff',\n\t\t\t\t'--numstat',\n\t\t\t\t'--summary',\n\t\t\t\t`-M${similarityThreshold == null ? '' : `${similarityThreshold}%`}`,\n\t\t\t\t'--no-ext-diff',\n\t\t\t\t'-z',\n\t\t\t\toptions?.filters?.length ? `--diff-filter=${options.filters.join('')}` : undefined,\n\t\t\t\tref1OrRange ? ref1OrRange : undefined,\n\t\t\t\tref2 ? ref2 : undefined,\n\t\t\t\t'--',\n\t\t\t\toptions?.path ? options.path : undefined,\n\t\t\t);\n\n\t\t\tconst files = result.stdout ? (parseGitDiffNumStatFiles(result.stdout, repoPath) ?? []) : [];\n\n\t\t\t// Merge in untracked files when the comparison involves the working tree.\n\t\t\t// Skip when `filters` excludes additions (untracked entries are semantically 'A'/added),\n\t\t\t// or when `path` is a non-exact pathspec we can't reliably match against the untracked list.\n\t\t\tif (\n\t\t\t\toptions?.includeUntracked &&\n\t\t\t\tref2 == null &&\n\t\t\t\ttypeof ref1OrRange === 'string' &&\n\t\t\t\t!isRevisionRange(ref1OrRange) &&\n\t\t\t\t!isUncommittedStaged(ref1OrRange) &&\n\t\t\t\tfiltersAllowUntracked(options.filters) &&\n\t\t\t\t!isNonExactPathspec(options.path)\n\t\t\t) {\n\t\t\t\tconst untracked = (await this.provider.status?.getUntrackedFiles?.(repoPath)) ?? [];\n\t\t\t\tif (untracked.length) {\n\t\t\t\t\tconst seen = new Set(files.map(f => f.path));\n\t\t\t\t\tfor (const file of untracked) {\n\t\t\t\t\t\tif (seen.has(file.path)) continue;\n\t\t\t\t\t\tif (options.path && options.path !== file.path) continue;\n\n\t\t\t\t\t\tfiles.push(file);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn files.length ? files : undefined;\n\t\t} catch (_ex) {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getDiffTool(repoPath?: string): Promise<string | undefined> {\n\t\treturn (\n\t\t\t(await this.provider.config.getConfig?.(repoPath, 'diff.guitool', { runGitLocally: true })) ??\n\t\t\tthis.provider.config.getConfig?.(repoPath, 'diff.tool', { runGitLocally: true })\n\t\t);\n\t}\n\n\t@debug()\n\tasync getNextComparisonUris(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\trev: string | undefined,\n\t\tskip: number = 0,\n\t\toptions?: { ordering?: 'date' | 'author-date' | 'topo' | null },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<NextComparisonUrisResult | undefined> {\n\t\t// If we have no revision there is no next commit\n\t\tif (!rev) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tlet relativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\n\t\tif (isUncommittedStaged(rev)) {\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, relativePath, rev),\n\t\t\t\tnext: createRevisionUri(this.provider, repoPath, relativePath, undefined),\n\t\t\t};\n\t\t} else if (isUncommitted(rev)) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parser = getShaAndFileSummaryLogParser();\n\t\t\tconst args = ['log', ...parser.arguments];\n\n\t\t\tconst ordering = options?.ordering ?? this.context.config?.commits.ordering;\n\t\t\tif (ordering) {\n\t\t\t\targs.push(`--${ordering}-order`);\n\t\t\t}\n\n\t\t\targs.push(\n\t\t\t\t// Use reverse to get the next commits (oldest to newest instead of newest to oldest)\n\t\t\t\t'--reverse',\n\t\t\t);\n\n\t\t\tif (rev !== deletedOrMissing) {\n\t\t\t\targs.push(\n\t\t\t\t\t// Ancestry path to ensure we only follow the path from the given revision to HEAD\n\t\t\t\t\t'--ancestry-path',\n\t\t\t\t\t// Range from the given revision to HEAD\n\t\t\t\t\t`${rev}..HEAD`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Follow file history and specify the file path\n\t\t\targs.push('--follow', '--', relativePath);\n\n\t\t\t// Unbounded --follow walk over file history can be slow on long histories — queue as background\n\t\t\t// so it doesn't compete with interactive work.\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsLog, priority: 'background' },\n\t\t\t\t...args,\n\t\t\t);\n\n\t\t\tlet currentSha;\n\t\t\tlet currentPath;\n\n\t\t\tif (skip === 0) {\n\t\t\t\tcurrentSha = rev;\n\t\t\t\tcurrentPath = relativePath;\n\t\t\t} else {\n\t\t\t\tskip--;\n\t\t\t}\n\n\t\t\tlet nextSha;\n\t\t\tlet nextPath;\n\n\t\t\tlet file;\n\t\t\tfor (const commit of parser.parse(result.stdout)) {\n\t\t\t\tconst path = relativePath;\n\t\t\t\tfile = commit.files.find(f => f.path === path || f.originalPath === path);\n\t\t\t\t// Keep track of the file changing paths\n\t\t\t\tif (file?.path && file.path !== relativePath) {\n\t\t\t\t\trelativePath = file.path;\n\t\t\t\t}\n\n\t\t\t\tif (skip > 0) {\n\t\t\t\t\tskip--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (currentSha == null) {\n\t\t\t\t\tcurrentSha = commit.sha;\n\t\t\t\t\tcurrentPath = file?.path ?? relativePath;\n\t\t\t\t} else if (nextSha == null) {\n\t\t\t\t\t// if (commit.sha === rev) continue;\n\n\t\t\t\t\tnextSha = commit.sha;\n\t\t\t\t\tnextPath = file?.path ?? relativePath;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentSha == null || currentPath == null) {\n\t\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, relativePath);\n\t\t\t\tif (status != null) {\n\t\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\t\tcurrentSha = uncommittedStaged;\n\t\t\t\t\t\tcurrentPath = status.originalPath ?? status.path;\n\t\t\t\t\t\tnextSha = '';\n\t\t\t\t\t\tnextPath = status.path;\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn undefined;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nextSha == null || nextPath == null) {\n\t\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, relativePath);\n\t\t\t\tif (status != null) {\n\t\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\t\tnextSha = uncommittedStaged;\n\t\t\t\t\t\tnextPath = status.originalPath ?? status.path;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tnextSha = '';\n\t\t\t\t\t\tnextPath = status.path;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tnextSha = '';\n\t\t\t\t\tnextPath = relativePath;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha || undefined),\n\t\t\t\tnext: createRevisionUri(\n\t\t\t\t\tthis.provider,\n\t\t\t\t\trepoPath,\n\t\t\t\t\tnextPath ?? currentPath,\n\t\t\t\t\t(nextSha ?? deletedOrMissing) || undefined,\n\t\t\t\t),\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getPreviousComparisonUris(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\trev: string | undefined,\n\t\tskip: number = 0,\n\t\tunsaved?: boolean,\n\t\toptions?: { ordering?: 'date' | 'author-date' | 'topo' | null },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<PreviousComparisonUrisResult | undefined> {\n\t\tif (rev === deletedOrMissing) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tif (rev === uncommitted) {\n\t\t\trev = undefined;\n\t\t}\n\n\t\t/* Rules:\n\t\t * Starting from working tree (rev is empty or undefined):\n\t\t * 1. Check file status:\n\t\t * 1.1. If file has both staged and working (or unsaved) changes:\n\t\t * - If skip=0: diff working with staged\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.2. If file has only staged changes:\n\t\t * - If skip=0: diff working with HEAD\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.3. If file has only working changes:\n\t\t * - If skip=0: diff working with HEAD\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.4. If file has no changes (or no status found):\n\t\t * - If skip=0: diff working with HEAD~1\n\t\t * - If skip>0: skip through revs starting at HEAD~1\n\t\t *\n\t\t * Starting from staged (rev is uncommittedStaged):\n\t\t * 1. Check file status:\n\t\t * 1.1. If status exists:\n\t\t * - If skip=0: diff staged with HEAD\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * 1.2. If status doesn't exist:\n\t\t * - If skip=0: diff HEAD with HEAD~1\n\t\t * - If skip>0: skip through revs starting at HEAD\n\t\t *\n\t\t * Starting from a commit (rev is a SHA):\n\t\t * - If skip=0: diff SHA with SHA^\n\t\t * - If skip>0: skip through revs starting at SHA\n\t\t */\n\n\t\tlet relativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\t\tlet skipPrev = 0;\n\n\t\tlet revs: [string, string][] = [];\n\t\tif (!rev) {\n\t\t\trevs = [['', relativePath]];\n\n\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, pathOrUri);\n\t\t\tif (status != null) {\n\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\trevs.push([uncommittedStaged, status.originalPath ?? status.path]);\n\t\t\t\t}\n\n\t\t\t\trevs.push(['HEAD', status.originalPath ?? status.path]);\n\t\t\t\tif (status.workingTreeStatus == null && !unsaved) {\n\t\t\t\t\tif (skip === 0 || status.indexStatus == null) {\n\t\t\t\t\t\t// Skip over the HEAD commit to get a diff, because working and HEAD are the same\n\t\t\t\t\t\tskipPrev++;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trevs.push(['HEAD', relativePath]);\n\t\t\t\tif (!unsaved) {\n\t\t\t\t\t// Skip over the HEAD commit to get a diff, because working and HEAD are the same\n\t\t\t\t\tskipPrev++;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (isUncommittedStaged(rev)) {\n\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, pathOrUri);\n\t\t\tif (status != null) {\n\t\t\t\trevs = [\n\t\t\t\t\t[uncommittedStaged, status.originalPath ?? status.path],\n\t\t\t\t\t['HEAD', status.originalPath ?? status.path],\n\t\t\t\t];\n\t\t\t} else {\n\t\t\t\t// Even though we supposedly started at staged, there is no staged version\n\t\t\t\trevs = [['HEAD', relativePath]];\n\t\t\t\tif (skip > 0) {\n\t\t\t\t\tskip--;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tlet currentSha;\n\t\tlet currentPath;\n\n\t\tif (revs.length) {\n\t\t\tskip++;\n\t\t\twhile (revs.length && skip > 0) {\n\t\t\t\tskip--;\n\t\t\t\t[rev, relativePath] = revs.shift()!;\n\t\t\t}\n\n\t\t\tif (rev !== 'HEAD') {\n\t\t\t\tcurrentSha = rev;\n\t\t\t\tcurrentPath = relativePath;\n\n\t\t\t\tskipPrev++;\n\t\t\t\twhile (revs.length && skipPrev > 0) {\n\t\t\t\t\tskipPrev--;\n\t\t\t\t\t[rev, relativePath] = revs.shift()!;\n\t\t\t\t}\n\n\t\t\t\tif (skipPrev > 0) {\n\t\t\t\t\tskip += skipPrev;\n\t\t\t\t}\n\n\t\t\t\tif (rev !== 'HEAD' && skip === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha),\n\t\t\t\t\t\tprevious: createRevisionUri(this.provider, repoPath, relativePath, rev),\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst parser = getShaAndFileSummaryLogParser();\n\t\t\tconst args = ['log', ...parser.arguments, `-n${skip + 2}`]; // Don't use --skip as it doesn't work with --follow\n\n\t\t\tconst ordering = options?.ordering ?? this.context.config?.commits.ordering;\n\t\t\tif (ordering) {\n\t\t\t\targs.push(`--${ordering}-order`);\n\t\t\t}\n\n\t\t\targs.push('--follow', rev!, '--', relativePath);\n\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{\n\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\tconfigs: gitConfigsLog,\n\t\t\t\t\tcaching: { cache: this.cache.gitResults, options: { accessTTL: 5 * 60 * 1000 } },\n\t\t\t\t},\n\t\t\t\t...args,\n\t\t\t);\n\n\t\t\tlet previousSha;\n\t\t\tlet previousPath;\n\t\t\tlet file;\n\n\t\t\tfor (const commit of parser.parse(result.stdout)) {\n\t\t\t\tconst path = relativePath;\n\t\t\t\tfile = commit.files.find(f => f.path === path || f.originalPath === path);\n\t\t\t\t// Keep track of the file changing paths\n\t\t\t\tif (file?.originalPath && file.originalPath !== relativePath) {\n\t\t\t\t\trelativePath = file.originalPath;\n\t\t\t\t}\n\n\t\t\t\tif (skip > 0) {\n\t\t\t\t\tskip--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (currentSha == null) {\n\t\t\t\t\tcurrentSha = commit.sha;\n\t\t\t\t\tcurrentPath = file?.path ?? relativePath;\n\t\t\t\t} else if (previousSha == null) {\n\t\t\t\t\tif (commit.sha === rev) continue;\n\n\t\t\t\t\tpreviousSha = commit.sha;\n\t\t\t\t\tpreviousPath = file?.path ?? relativePath;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentSha == null || currentPath == null) return undefined;\n\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha || undefined),\n\t\t\t\tprevious: createRevisionUri(\n\t\t\t\t\tthis.provider,\n\t\t\t\t\trepoPath,\n\t\t\t\t\tpreviousPath ?? currentPath,\n\t\t\t\t\tpreviousSha ?? deletedOrMissing,\n\t\t\t\t),\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getPreviousComparisonUrisForRange(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\trev: string | undefined,\n\t\trange: DiffRange,\n\t\toptions?: { ordering?: 'date' | 'author-date' | 'topo' | null; skipFirstRev?: boolean },\n\t\t_cancellation?: AbortSignal,\n\t): Promise<PreviousRangeComparisonUrisResult | undefined> {\n\t\tif (rev === deletedOrMissing) return undefined;\n\n\t\tconst scope = getScopedLogger();\n\n\t\tif (rev === uncommitted) {\n\t\t\trev = undefined;\n\t\t}\n\n\t\tlet currentSha;\n\t\tlet currentPath;\n\t\tlet relativePath = this.provider.getRelativePath(pathOrUri, repoPath);\n\t\tconst skipFirstRev = options?.skipFirstRev ?? true;\n\n\t\t// If we are at the working tree (i.e. no ref), we need to dig deeper to figure out where to go\n\t\tif (!rev) {\n\t\t\tconst status = await this.provider.status?.getStatusForFile?.(repoPath, pathOrUri);\n\t\t\tif (status != null) {\n\t\t\t\tif (status.indexStatus != null) {\n\t\t\t\t\tif (status.workingTreeStatus != null && !skipFirstRev) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, relativePath, undefined),\n\t\t\t\t\t\t\tprevious: createRevisionUri(this.provider, repoPath, relativePath, uncommittedStaged),\n\t\t\t\t\t\t\trange: range,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\tcurrentSha = uncommittedStaged;\n\t\t\t\t\tcurrentPath = status.originalPath ?? status.path;\n\t\t\t\t\tif (status.originalPath != null) {\n\t\t\t\t\t\trelativePath = status.originalPath;\n\t\t\t\t\t}\n\t\t\t\t\trev = uncommittedStaged;\n\t\t\t\t} else if (status.workingTreeStatus != null && !skipFirstRev) {\n\t\t\t\t\tcurrentSha = '';\n\t\t\t\t\tcurrentPath = relativePath;\n\t\t\t\t\trev = '';\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (!skipFirstRev) {\n\t\t\tcurrentSha = rev;\n\t\t\tcurrentPath = relativePath;\n\t\t}\n\n\t\ttry {\n\t\t\tconst parser = getShaAndFileRangeLogParser();\n\t\t\tconst args = ['log', ...parser.arguments, '-n2']; // Don't use --skip as it doesn't work with --follow\n\n\t\t\tconst ordering = options?.ordering ?? this.context.config?.commits.ordering;\n\t\t\tif (ordering) {\n\t\t\t\targs.push(`--${ordering}-order`);\n\t\t\t}\n\n\t\t\tif (rev && !isUncommittedStaged(rev)) {\n\t\t\t\targs.push(rev);\n\t\t\t}\n\n\t\t\targs.push(`-L${range.startLine},${range.endLine}:${relativePath}`);\n\n\t\t\tlet result: GitResult;\n\t\t\ttry {\n\t\t\t\tresult = await this.git.run({ cwd: repoPath, configs: gitConfigsLog }, ...args);\n\t\t\t} catch (ex) {\n\t\t\t\tif (rev && !isUncommittedStaged(rev)) throw ex;\n\n\t\t\t\t// If the line count is invalid reset to a valid range\n\t\t\t\tconst match = GitErrors.invalidLineCount.exec(ex?.toString() ?? '');\n\t\t\t\tif (match == null) throw ex;\n\n\t\t\t\tconst line = parseInt(match[1], 10);\n\t\t\t\tif (isNaN(line)) throw ex;\n\n\t\t\t\tconst index = args.findIndex(a => a.startsWith('-L'));\n\t\t\t\tif (index === -1) throw ex;\n\n\t\t\t\trange = {\n\t\t\t\t\tstartLine: Math.min(range.startLine, line),\n\t\t\t\t\tstartCharacter: range.startCharacter,\n\t\t\t\t\tendLine: Math.min(range.endLine, line),\n\t\t\t\t\tendCharacter: range.endCharacter,\n\t\t\t\t\tactive: range.active,\n\t\t\t\t};\n\n\t\t\t\targs.splice(index, 1, `-L${range.startLine},${range.endLine}:${relativePath}`);\n\t\t\t\tresult = await this.git.run({ cwd: repoPath, configs: gitConfigsLog }, ...args);\n\t\t\t}\n\n\t\t\tlet currentRange;\n\t\t\tlet previousSha;\n\t\t\tlet previousPath;\n\t\t\tlet file;\n\n\t\t\tfor (const commit of parser.parse(result.stdout)) {\n\t\t\t\tconst path = relativePath;\n\t\t\t\tfile = commit.files.find(f => f.path === path || f.originalPath === path);\n\t\t\t\t// If we couldn't find the file, but there is only one file in the commit, use that one and assume the filename changed\n\t\t\t\tif (file == null && commit.files.length === 1) {\n\t\t\t\t\tfile = commit.files[0];\n\t\t\t\t\trelativePath = file.path;\n\t\t\t\t}\n\t\t\t\t// Keep track of the file changing paths\n\t\t\t\tif (file?.originalPath && file.originalPath !== relativePath) {\n\t\t\t\t\trelativePath = file.originalPath;\n\t\t\t\t}\n\n\t\t\t\tif (currentSha == null) {\n\t\t\t\t\tcurrentSha = commit.sha;\n\t\t\t\t\tcurrentPath = file?.path ?? relativePath;\n\t\t\t\t\tcurrentRange =\n\t\t\t\t\t\tfile?.range != null ? { ...file.range, startCharacter: 1, endCharacter: 1 } : undefined;\n\t\t\t\t} else if (previousSha == null) {\n\t\t\t\t\tif (commit.sha === rev) continue;\n\n\t\t\t\t\tpreviousSha = commit.sha;\n\t\t\t\t\tpreviousPath = file?.path ?? relativePath;\n\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentSha == null || currentPath == null) return undefined;\n\n\t\t\t// If we have no previous SHA but have a real current SHA, resolve the parent commit.\n\t\t\t// This handles the case where a line is newly added — the line has no prior history,\n\t\t\t// but the file likely existed at the parent commit\n\t\t\tif (previousSha == null && currentSha) {\n\t\t\t\tpreviousSha =\n\t\t\t\t\t(await this.provider.refs.validateReference(repoPath, `${currentSha}^`)) ?? deletedOrMissing;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tcurrent: createRevisionUri(this.provider, repoPath, currentPath, currentSha || undefined),\n\t\t\t\tprevious: createRevisionUri(\n\t\t\t\t\tthis.provider,\n\t\t\t\t\trepoPath,\n\t\t\t\t\tpreviousPath ?? currentPath,\n\t\t\t\t\tpreviousSha ?? deletedOrMissing,\n\t\t\t\t),\n\t\t\t\trange: currentRange ?? range,\n\t\t\t};\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/** Returns raw diff output for a file between two revisions. */\n\tasync diff(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref1?: string,\n\t\tref2?: string,\n\t\toptions?: {\n\t\t\tencoding?: string;\n\t\t\tfilters?: GitDiffFilter[];\n\t\t\tlinesOfContext?: number;\n\t\t\trenames?: boolean;\n\t\t\tsimilarityThreshold?: number | null;\n\t\t},\n\t): Promise<GitResult> {\n\t\tconst params = ['diff', '--no-ext-diff', '--minimal'];\n\n\t\tif (options?.linesOfContext != null) {\n\t\t\tparams.push(`-U${options.linesOfContext}`);\n\t\t}\n\n\t\tif (options?.renames) {\n\t\t\tparams.push(`-M${options.similarityThreshold == null ? '' : `${options.similarityThreshold}%`}`);\n\t\t}\n\n\t\tif (options?.filters != null && options.filters.length !== 0) {\n\t\t\tparams.push(`--diff-filter=${options.filters.join('')}`);\n\t\t}\n\n\t\tif (ref1) {\n\t\t\t// <sha>^3 signals an untracked file in a stash and if we are trying to find its parent, use the empty tree\n\t\t\tif (ref1.endsWith('^3^')) {\n\t\t\t\tref1 = await this.provider.revision.getEmptyTreeSha(repoPath);\n\t\t\t}\n\t\t\tparams.push(isUncommittedStaged(ref1) ? '--staged' : ref1);\n\t\t}\n\t\tif (ref2) {\n\t\t\tparams.push(isUncommittedStaged(ref2) ? '--staged' : ref2);\n\t\t}\n\n\t\ttry {\n\t\t\treturn await this.git.run(\n\t\t\t\t{ cwd: repoPath, configs: gitConfigsDiff, encoding: options?.encoding },\n\t\t\t\t...params,\n\t\t\t\t'--',\n\t\t\t\tfileName,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tconst match = GitErrors.badRevision.exec(ex.message);\n\t\t\tif (match !== null) {\n\t\t\t\tconst [, ref] = match;\n\n\t\t\t\t// If the bad ref is trying to find a parent ref, assume we hit to the last commit, so try again using the empty tree\n\t\t\t\tif (ref === ref1 && ref?.endsWith('^')) {\n\t\t\t\t\treturn this.diff(\n\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\tfileName,\n\t\t\t\t\t\tawait this.provider.revision.getEmptyTreeSha(repoPath),\n\t\t\t\t\t\tref2,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/** Returns raw diff output comparing a file's revision with given contents via stdin. */\n\tasync diffContents(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref: string,\n\t\tcontents: string,\n\t\toptions?: { encoding?: string; filters?: GitDiffFilter[]; similarityThreshold?: number | null },\n\t): Promise<string> {\n\t\tconst params = [\n\t\t\t'diff',\n\t\t\t`-M${options?.similarityThreshold == null ? '' : `${options.similarityThreshold}%`}`,\n\t\t\t'--no-ext-diff',\n\t\t\t'-U0',\n\t\t\t'--minimal',\n\t\t];\n\n\t\tif (options?.filters != null && options.filters.length !== 0) {\n\t\t\tparams.push(`--diff-filter=${options.filters.join('')}`);\n\t\t}\n\n\t\tparams.push('--no-index');\n\n\t\ttry {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{\n\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\tconfigs: gitConfigsDiff,\n\t\t\t\t\tencoding: options?.encoding,\n\t\t\t\t\tstdin: contents,\n\t\t\t\t},\n\t\t\t\t...params,\n\t\t\t\t'--',\n\t\t\t\tfileName,\n\t\t\t\t// Pipe the contents to stdin\n\t\t\t\t'-',\n\t\t\t);\n\t\t\treturn result.stdout;\n\t\t} catch (ex) {\n\t\t\tif (ex instanceof GitError && ex.stdout) {\n\t\t\t\treturn ex.stdout;\n\t\t\t}\n\n\t\t\tconst match = GitErrors.badRevision.exec(ex.message);\n\t\t\tif (match !== null) {\n\t\t\t\tconst [, matchedRef] = match;\n\n\t\t\t\t// If the bad ref is trying to find a parent ref, assume we hit to the last commit, so try again using the empty tree\n\t\t\t\tif (matchedRef === ref && matchedRef?.endsWith('^')) {\n\t\t\t\t\treturn this.diffContents(\n\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\tfileName,\n\t\t\t\t\t\tawait this.provider.revision.getEmptyTreeSha(repoPath),\n\t\t\t\t\t\tcontents,\n\t\t\t\t\t\toptions,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t/** Returns parsed diff hunks for a file between two revisions. */\n\t@debug()\n\tgetDiffForFile(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref1: string | undefined,\n\t\tref2?: string,\n\t\toptions?: { encoding?: string },\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\tconst cacheKey = `${normalizePath(fileName)}:${ref1 ?? ''}:${ref2 ?? ''}`;\n\t\treturn this.cache.diff.getOrCreate(\n\t\t\trepoPath,\n\t\t\tcacheKey,\n\t\t\t() => this.getDiffForFileCore(repoPath, fileName, ref1, ref2, options),\n\t\t\t{ errorTTL: 1000 * 60 },\n\t\t);\n\t}\n\n\tprivate async getDiffForFileCore(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref1: string | undefined,\n\t\tref2: string | undefined,\n\t\toptions: { encoding?: string } | undefined,\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\t// Include renames (R) and copies (C) so content diffs on renamed files still surface;\n\t\t// --diff-filter runs after `-M` rename detection, so 'M' alone would drop valid rename diffs.\n\t\tconst result = await this.diff(repoPath, fileName, ref1, ref2, {\n\t\t\tencoding: options?.encoding,\n\t\t\tfilters: ['M', 'R', 'C'],\n\t\t\tlinesOfContext: 0,\n\t\t\trenames: true,\n\t\t\tsimilarityThreshold: this.context.config?.commits.similarityThreshold,\n\t\t});\n\t\treturn parseGitFileDiff(result.stdout);\n\t}\n\n\t/** Returns parsed diff hunks comparing a file's revision with given contents via stdin. */\n\t@debug()\n\tgetDiffForFileContents(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref: string,\n\t\tcontents: string,\n\t\toptions?: { encoding?: string },\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\tconst cacheKey = `${normalizePath(fileName)}:${ref}:~${fnv1aHash(contents)}`;\n\t\treturn this.cache.diff.getOrCreate(\n\t\t\trepoPath,\n\t\t\tcacheKey,\n\t\t\t() => this.getDiffForFileContentsCore(repoPath, fileName, ref, contents, options),\n\t\t\t{ errorTTL: 1000 * 60 },\n\t\t);\n\t}\n\n\tprivate async getDiffForFileContentsCore(\n\t\trepoPath: string,\n\t\tfileName: string,\n\t\tref: string,\n\t\tcontents: string,\n\t\toptions: { encoding?: string } | undefined,\n\t): Promise<ParsedGitDiffHunks | undefined> {\n\t\tconst data = await this.diffContents(repoPath, fileName, ref, contents, {\n\t\t\tencoding: options?.encoding,\n\t\t\tfilters: ['M', 'R', 'C'],\n\t\t\tsimilarityThreshold: this.context.config?.commits.similarityThreshold,\n\t\t});\n\t\treturn parseGitFileDiff(data);\n\t}\n\n\t/** Finds the most recent commit where the given path was copied, renamed, or deleted. */\n\t@debug()\n\tasync findPathStatusChanged(\n\t\trepoPath: string,\n\t\tpathspec: string,\n\t\trev: string | undefined,\n\t\toptions?: { filters?: GitDiffFilter[]; ordering?: 'date' | 'author-date' | 'topo' | null },\n\t): Promise<{ sha: string; file: LogParsedFile | undefined } | undefined> {\n\t\treturn findPathStatusChanged(this.git, repoPath, pathspec, rev, options);\n\t}\n\n\t@debug()\n\tasync openDiffTool(\n\t\trepoPath: string,\n\t\tpathOrUri: string | Uri,\n\t\toptions?: { ref1?: string; ref2?: string; staged?: boolean; tool?: string },\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\t\tconst [relativePath, root] = splitPath(toFsPath(pathOrUri), repoPath);\n\n\t\ttry {\n\t\t\tlet tool = options?.tool;\n\t\t\tif (!tool) {\n\t\t\t\tconst scope = getScopedLogger();\n\n\t\t\t\ttool = await this.getDiffTool(root);\n\t\t\t\tif (tool == null) throw new Error('No diff tool found');\n\n\t\t\t\tscope?.debug(`Using tool=${tool}`);\n\t\t\t}\n\n\t\t\tawait this.git.run(\n\t\t\t\t{ cwd: root },\n\t\t\t\t'difftool',\n\t\t\t\t'--no-prompt',\n\t\t\t\t`--tool=${tool}`,\n\t\t\t\toptions?.staged ? '--staged' : undefined,\n\t\t\t\toptions?.ref1 ? options.ref1 : undefined,\n\t\t\t\toptions?.ref2 ? options.ref2 : undefined,\n\t\t\t\t'--',\n\t\t\t\trelativePath,\n\t\t\t);\n\t\t} catch (ex) {\n\t\t\tconst msg: string = ex?.toString() ?? '';\n\t\t\tif ((ex instanceof Error && ex.message === 'No diff tool found') || /Unknown .+? tool/.test(msg)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tl10n.t(\n\t\t\t\t\t\t'Unable to open changes because the specified diff tool cannot be found or no Git diff tool is configured',\n\t\t\t\t\t),\n\t\t\t\t\t{ cause: ex },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n\n\t@debug()\n\tasync openDirectoryCompare(repoPath: string, ref1: string, ref2?: string, tool?: string): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tif (!tool) {\n\t\t\t\tconst scope = getScopedLogger();\n\n\t\t\t\ttool = await this.getDiffTool(repoPath);\n\t\t\t\tif (tool == null) throw new Error('No diff tool found');\n\n\t\t\t\tscope?.debug(`Using tool=${tool}`);\n\t\t\t}\n\n\t\t\tawait this.git.run({ cwd: repoPath }, 'difftool', '--dir-diff', `--tool=${tool}`, ref1, ref2);\n\t\t} catch (ex) {\n\t\t\tconst msg: string = ex?.toString() ?? '';\n\t\t\tif ((ex instanceof Error && ex.message === 'No diff tool found') || /Unknown .+? tool/.test(msg)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\tl10n.t(\n\t\t\t\t\t\t'Unable to open directory compare because the specified diff tool cannot be found or no Git diff tool is configured',\n\t\t\t\t\t),\n\t\t\t\t\t{ cause: ex },\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tscope?.error(ex);\n\t\t\tthrow ex;\n\t\t}\n\t}\n}\n// Returns true when no --diff-filter is active or when it includes additions ('A' or '*').\n// Untracked files are semantically \"added\" — if a caller restricts filters to e.g. ['M'],\n// honor that and omit untracked.\nfunction filtersAllowUntracked(filters: GitDiffFilter[] | undefined): boolean {\n\tif (!filters?.length) return true;\n\treturn filters.includes('A') || filters.includes('*');\n}\n\n// Returns true when the path is a git pathspec we can't match with simple equality\n// (directory prefix, glob, or magic pathspec). In that case, skip the untracked merge\n// rather than risk excluding legitimate matches or including wrong ones.\nfunction isNonExactPathspec(path: string | undefined): boolean {\n\tif (!path) return false;\n\treturn path.endsWith('/') || /[*?[\\]]|^:/.test(path);\n}\n\n// Returns true when the `git diff` invocation shape includes the working tree\n// (i.e. `git diff`, `git diff HEAD`, or `git diff <ref>` with no `from`), and\n// therefore untracked files are a meaningful addition to the result.\nfunction isWorkingTreeComparison(to: string | undefined, from: string | undefined): boolean {\n\tif (to == null) return true;\n\tif (isRevisionRange(to) || isUncommittedStaged(to)) return false;\n\n\t// `to === uncommitted` always targets the working tree (`prepareToFromDiffArgs` sets\n\t// `from = 'HEAD'` when omitted, or diffs against the caller-supplied `from`).\n\tif (isUncommitted(to)) return true;\n\n\t// `git diff <from>` — `to === ''` means the caller explicitly asked for working tree vs `from`\n\tif (to === '') return from == null || !isUncommittedStaged(from);\n\n\t// `getChangedFilesCount` uses `prepareToFromDiffArgs`, which translates `to` (with no `from`)\n\t// to `${to}^ ${to}` UNLESS `to` is HEAD or empty.\n\tif (to.toUpperCase() === 'HEAD' && from == null) return true;\n\treturn false;\n}\n\nfunction prepareToFromDiffArgs(\n\tto: string,\n\tfrom: string | undefined,\n\targs: string[],\n\tnotation?: GitRevisionRangeNotation,\n): string {\n\tif (to === uncommitted) {\n\t\tif (from != null) {\n\t\t\targs.push(from);\n\t\t} else {\n\t\t\t// Get only unstaged changes\n\t\t\tfrom = 'HEAD';\n\t\t}\n\t} else if (to === uncommittedStaged) {\n\t\targs.push('--staged');\n\t\tif (from != null) {\n\t\t\targs.push(from);\n\t\t} else {\n\t\t\t// Get only staged changes\n\t\t\tfrom = 'HEAD';\n\t\t}\n\t} else if (from == null) {\n\t\tif (to === '' || to.toUpperCase() === 'HEAD') {\n\t\t\tfrom = 'HEAD';\n\t\t\targs.push(from);\n\t\t} else {\n\t\t\tfrom = `${to}^`;\n\t\t\targs.push(from, to);\n\t\t}\n\t} else if (to === '') {\n\t\targs.push(from);\n\t} else if (notation != null) {\n\t\targs.push(`${from}${notation}${to}`);\n\t} else {\n\t\targs.push(from, to);\n\t}\n\treturn from;\n}\n\nfunction createRevisionUri(\n\tprovider: CliGitProviderInternal,\n\trepoPath: string,\n\trelativePath: string,\n\tsha: string | undefined,\n): RevisionUri {\n\treturn {\n\t\turi: sha\n\t\t\t? fromUri({\n\t\t\t\t\tscheme: 'gitlens',\n\t\t\t\t\tauthority: encodeGitLensRevisionUriAuthority({ ref: sha, repoPath: repoPath }),\n\t\t\t\t\tpath: provider.getAbsoluteUri(relativePath, repoPath).path,\n\t\t\t\t})\n\t\t\t: provider.getAbsoluteUri(relativePath, repoPath),\n\t\tpath: relativePath,\n\t\tsha: sha,\n\t\trepoPath: repoPath,\n\t};\n}\n\nexport async function findPathStatusChanged(\n\tgit: Git,\n\trepoPath: string,\n\tpathspec: string,\n\trev: string | undefined,\n\toptions?: { filters?: GitDiffFilter[]; ordering?: 'date' | 'author-date' | 'topo' | null },\n): Promise<{ sha: string; file: LogParsedFile | undefined } | undefined> {\n\tconst parser = getShaAndFileSummaryLogParser();\n\n\tconst ordering = options?.ordering;\n\n\tconst result = await git.run(\n\t\t{ cwd: repoPath, configs: gitConfigsLog },\n\t\t'log',\n\t\t...parser.arguments,\n\t\tordering ? `--${ordering}-order` : undefined,\n\t\t'--no-walk',\n\t\t`--diff-filter=${options?.filters?.length ? options.filters.join('') : 'RCD'}`,\n\t\trev,\n\t\t// Note: --no-walk constrains to the exact commit (no ancestor search). Pathspec is omitted\n\t\t// because git suppresses rename detection with pathspecs (shows D instead of R, losing the destination path).\n\t\t'--',\n\t);\n\n\tconst commit = first(parser.parse(result.stdout));\n\tif (commit == null) return undefined;\n\n\tconst file = commit.files.find(f => f.path === pathspec || f.originalPath === pathspec);\n\treturn { sha: commit.sha, file: file };\n}\n"],"mappings":"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"}