@gitkraken/core-gitlens 0.12.0 → 0.14.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 (198) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/dist/git/cache.d.ts +84 -29
  3. package/dist/git/cache.js +188 -54
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/features.d.ts +1 -1
  6. package/dist/git/features.js +5 -0
  7. package/dist/git/features.js.map +1 -1
  8. package/dist/git/models/branch.d.ts +20 -2
  9. package/dist/git/models/branch.js +28 -6
  10. package/dist/git/models/branch.js.map +1 -1
  11. package/dist/git/models/reference.d.ts +3 -0
  12. package/dist/git/models/status.js +2 -2
  13. package/dist/git/models/worktree.d.ts +4 -1
  14. package/dist/git/models/worktree.js +4 -2
  15. package/dist/git/models/worktree.js.map +1 -1
  16. package/dist/git/parsers/diffParser.js +1 -1
  17. package/dist/git/providers/branches.d.ts +73 -8
  18. package/dist/git/providers/operations.d.ts +3 -2
  19. package/dist/git/providers/pausedOperations.d.ts +6 -0
  20. package/dist/git/providers/provider.d.ts +45 -2
  21. package/dist/git/providers/revision.d.ts +17 -3
  22. package/dist/git/providers/status.d.ts +8 -2
  23. package/dist/git/remotes/azure-devops.js +61 -53
  24. package/dist/git/remotes/azure-devops.js.map +1 -1
  25. package/dist/git/run.types.d.ts +12 -0
  26. package/dist/git/service.d.ts +8 -5
  27. package/dist/git/service.js +16 -8
  28. package/dist/git/service.js.map +1 -1
  29. package/dist/git/utils/branch.utils.d.ts +12 -2
  30. package/dist/git/utils/branch.utils.js +49 -5
  31. package/dist/git/utils/branch.utils.js.map +1 -1
  32. package/dist/git/utils/reference.utils.d.ts +1 -0
  33. package/dist/git/utils/reference.utils.js.map +1 -1
  34. package/dist/git/watching/changeEvent.d.ts +13 -2
  35. package/dist/git/watching/changeEvent.js +17 -3
  36. package/dist/git/watching/changeEvent.js.map +1 -1
  37. package/dist/git/watching/gitDirPoller.d.ts +111 -0
  38. package/dist/git/watching/gitDirPoller.js +266 -0
  39. package/dist/git/watching/gitDirPoller.js.map +1 -0
  40. package/dist/git/watching/provider.d.ts +16 -0
  41. package/dist/git/watching/watchGroup.d.ts +40 -4
  42. package/dist/git/watching/watchGroup.js +143 -17
  43. package/dist/git/watching/watchGroup.js.map +1 -1
  44. package/dist/git/watching/watchService.d.ts +70 -1
  45. package/dist/git/watching/watchService.js +158 -13
  46. package/dist/git/watching/watchService.js.map +1 -1
  47. package/dist/git/watching/watchSession.d.ts +44 -0
  48. package/dist/git/watching/watchSession.js +91 -6
  49. package/dist/git/watching/watchSession.js.map +1 -1
  50. package/dist/git/watching/watcherPatterns.d.ts +21 -0
  51. package/dist/git/watching/watcherPatterns.js +67 -1
  52. package/dist/git/watching/watcherPatterns.js.map +1 -1
  53. package/dist/git/watching/worktreeSummary.d.ts +34 -7
  54. package/dist/git/watching/worktreeSummary.js +39 -12
  55. package/dist/git/watching/worktreeSummary.js.map +1 -1
  56. package/dist/git-cli/cliGitProvider.d.ts +40 -2
  57. package/dist/git-cli/cliGitProvider.js +182 -18
  58. package/dist/git-cli/cliGitProvider.js.map +1 -1
  59. package/dist/git-cli/exec/commandClassifier.d.ts +15 -0
  60. package/dist/git-cli/exec/commandClassifier.js +48 -2
  61. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  62. package/dist/git-cli/exec/git.d.ts +20 -3
  63. package/dist/git-cli/exec/git.js +63 -19
  64. package/dist/git-cli/exec/git.js.map +1 -1
  65. package/dist/git-cli/exec/gitQueue.d.ts +2 -0
  66. package/dist/git-cli/exec/gitQueue.js +2 -1
  67. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  68. package/dist/git-cli/exec/indexLock.d.ts +39 -0
  69. package/dist/git-cli/exec/indexLock.js +160 -0
  70. package/dist/git-cli/exec/indexLock.js.map +1 -0
  71. package/dist/git-cli/parsers/blameParser.js +2 -2
  72. package/dist/git-cli/parsers/refParser.js +4 -2
  73. package/dist/git-cli/parsers/refParser.js.map +1 -1
  74. package/dist/git-cli/parsers/statusParser.d.ts +6 -0
  75. package/dist/git-cli/parsers/statusParser.js +33 -3
  76. package/dist/git-cli/parsers/statusParser.js.map +1 -1
  77. package/dist/git-cli/parsers/worktreeParser.js +42 -2
  78. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  79. package/dist/git-cli/providers/branchRemote.d.ts +14 -0
  80. package/dist/git-cli/providers/branchRemote.js +36 -0
  81. package/dist/git-cli/providers/branchRemote.js.map +1 -0
  82. package/dist/git-cli/providers/branches.d.ts +109 -10
  83. package/dist/git-cli/providers/branches.js +630 -134
  84. package/dist/git-cli/providers/branches.js.map +1 -1
  85. package/dist/git-cli/providers/commits.js +2 -2
  86. package/dist/git-cli/providers/config.d.ts +9 -5
  87. package/dist/git-cli/providers/config.js +58 -25
  88. package/dist/git-cli/providers/config.js.map +1 -1
  89. package/dist/git-cli/providers/contributors.js +1 -1
  90. package/dist/git-cli/providers/graph.js +9 -6
  91. package/dist/git-cli/providers/graph.js.map +1 -1
  92. package/dist/git-cli/providers/operations.d.ts +17 -0
  93. package/dist/git-cli/providers/operations.js +172 -111
  94. package/dist/git-cli/providers/operations.js.map +1 -1
  95. package/dist/git-cli/providers/pausedOperations.d.ts +8 -0
  96. package/dist/git-cli/providers/pausedOperations.js +59 -39
  97. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  98. package/dist/git-cli/providers/refs.js +11 -8
  99. package/dist/git-cli/providers/refs.js.map +1 -1
  100. package/dist/git-cli/providers/remotes.js +19 -10
  101. package/dist/git-cli/providers/remotes.js.map +1 -1
  102. package/dist/git-cli/providers/revision.d.ts +6 -2
  103. package/dist/git-cli/providers/revision.js +38 -12
  104. package/dist/git-cli/providers/revision.js.map +1 -1
  105. package/dist/git-cli/providers/staging.js +39 -14
  106. package/dist/git-cli/providers/staging.js.map +1 -1
  107. package/dist/git-cli/providers/stash.d.ts +2 -0
  108. package/dist/git-cli/providers/stash.js +33 -18
  109. package/dist/git-cli/providers/stash.js.map +1 -1
  110. package/dist/git-cli/providers/status.d.ts +3 -1
  111. package/dist/git-cli/providers/status.js +40 -22
  112. package/dist/git-cli/providers/status.js.map +1 -1
  113. package/dist/git-cli/providers/tags.js +6 -4
  114. package/dist/git-cli/providers/tags.js.map +1 -1
  115. package/dist/git-cli/providers/worktrees.d.ts +1 -0
  116. package/dist/git-cli/providers/worktrees.js +60 -31
  117. package/dist/git-cli/providers/worktrees.js.map +1 -1
  118. package/dist/git-cli/service.d.ts +6 -0
  119. package/dist/git-cli/service.js +5 -2
  120. package/dist/git-cli/service.js.map +1 -1
  121. package/dist/plus/git-github/providers/github/revision.d.ts +6 -2
  122. package/dist/plus/git-github/providers/github/revision.js +15 -10
  123. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  124. package/dist/plus/git-github/providers/github/status.d.ts +3 -1
  125. package/dist/plus/git-github/providers/github/status.js +2 -2
  126. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  127. package/dist/plus/integrations/collectionMetadata.d.ts +13 -2
  128. package/dist/plus/integrations/collectionMetadata.js +14 -2
  129. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  130. package/dist/plus/integrations/index.d.ts +2 -2
  131. package/dist/plus/integrations/index.js.map +1 -1
  132. package/dist/plus/integrations/integrationService.d.ts +1 -0
  133. package/dist/plus/integrations/integrationService.js +1 -1
  134. package/dist/plus/integrations/integrationService.js.map +1 -1
  135. package/dist/plus/integrations/manager.d.ts +1 -0
  136. package/dist/plus/integrations/models/gitHostIntegration.d.ts +41 -5
  137. package/dist/plus/integrations/models/gitHostIntegration.js +106 -29
  138. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  139. package/dist/plus/integrations/models/issueReads.d.ts +17 -4
  140. package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
  141. package/dist/plus/integrations/providers/apiClients.js +7 -1
  142. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  143. package/dist/plus/integrations/providers/azure/azure.d.ts +5 -4
  144. package/dist/plus/integrations/providers/azure/azure.js +36 -76
  145. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  146. package/dist/plus/integrations/providers/azure/azure.utils.js +20 -17
  147. package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
  148. package/dist/plus/integrations/providers/azure/models.d.ts +2 -2
  149. package/dist/plus/integrations/providers/azure/models.js +4 -4
  150. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  151. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +21 -12
  152. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +74 -17
  153. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  154. package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
  155. package/dist/plus/integrations/providers/azureDevOps.js +108 -17
  156. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  157. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +45 -1
  158. package/dist/plus/integrations/providers/azureDevOpsServer.js +121 -6
  159. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  160. package/dist/plus/integrations/providers/jira-server.js +18 -14
  161. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  162. package/dist/plus/integrations/providers/jira.js +18 -14
  163. package/dist/plus/integrations/providers/jira.js.map +1 -1
  164. package/dist/plus/integrations/providers/models.d.ts +1 -2
  165. package/dist/plus/integrations/providers/models.js +9 -2
  166. package/dist/plus/integrations/providers/models.js.map +1 -1
  167. package/dist/plus/integrations/providers/providersApi.d.ts +7 -17
  168. package/dist/plus/integrations/providers/providersApi.js +85 -78
  169. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  170. package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +22 -0
  171. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +69 -9
  172. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
  173. package/dist/plus/integrations/providers/utils/providerPaging.js +8 -2
  174. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  175. package/dist/plus/integrations/reads/filters.d.ts +3 -3
  176. package/dist/plus/integrations/reads/filters.js.map +1 -1
  177. package/dist/plus/integrations/reads/issueBatch.d.ts +2 -1
  178. package/dist/plus/integrations/reads/issueBatch.js +1 -1
  179. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  180. package/dist/plus/integrations/reads/issueTracker.js +23 -14
  181. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  182. package/dist/plus/integrations/reads/issues.d.ts +2 -1
  183. package/dist/plus/integrations/reads/issues.js +18 -3
  184. package/dist/plus/integrations/reads/issues.js.map +1 -1
  185. package/dist/plus/integrations/reads/pullRequestBatch.js +1 -1
  186. package/dist/plus/integrations/reads/resolveRepository.js +12 -3
  187. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  188. package/dist/plus/integrations/reads/warnings.d.ts +11 -4
  189. package/dist/plus/integrations/reads/warnings.js +15 -5
  190. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  191. package/dist/plus/integrations/results.d.ts +36 -2
  192. package/dist/plus/integrations/results.js +33 -13
  193. package/dist/plus/integrations/results.js.map +1 -1
  194. package/dist/utils/promiseCache.d.ts +10 -0
  195. package/dist/utils/promiseCache.js +17 -2
  196. package/dist/utils/promiseCache.js.map +1 -1
  197. package/docs/integrations.md +52 -20
  198. package/package.json +2 -2
@@ -3,6 +3,8 @@ import * as l10n from "@vscode/l10n";
3
3
  //#region ../git/src/remotes/azure-devops.ts
4
4
  const gitRegex = /\/_git\/?/i;
5
5
  const gitTailRegex = /\/_git(?:\/.*)?$/i;
6
+ const edgeSlashesRegex = /^\/+|\/+$/g;
7
+ const leadingGitRegex = /^_git\//i;
6
8
  const legacyDefaultCollectionRegex = /^DefaultCollection\//i;
7
9
  const orgAndProjectRegex = /^(.*?)\/(.*?)\/(.*)/;
8
10
  const sshDomainRegex = /^(ssh|vs-ssh)\./i;
@@ -10,64 +12,59 @@ const sshPathRegex = /^\/?v\d\//i;
10
12
  const azureSshUrlRegex = /^(?:[^@]+@)?([^:]+):v\d\//;
11
13
  const vstsHostnameSuffix = ".visualstudio.com";
12
14
  function isVsts(domain) {
13
- return domain.endsWith(vstsHostnameSuffix);
15
+ return domain.toLowerCase().endsWith(vstsHostnameSuffix);
14
16
  }
15
- function getVSTSOwner(url) {
16
- return url.hostname.split(".")[0];
17
- }
18
- const azureProjectRepoRegex = /([^/]+)\/_git\/([^/]+)/;
19
- function parseVstsHttpsUrl(url) {
20
- const owner = getVSTSOwner(url);
21
- const match = azureProjectRepoRegex.exec(url.pathname);
22
- if (match == null) throw new Error(`Invalid VSTS URL: ${url.toString()}`);
23
- const [, project, repo] = match;
24
- return [
25
- owner,
26
- project,
27
- repo
28
- ];
29
- }
30
- const azureHttpsUrlRegex2 = /([^/]+)\/([^/]+)\/_git\/([^/]+)/;
31
17
  function parseAzureRepositoryPath(path, legacyVsts, cloud) {
32
18
  const segments = path.split("/").map((segment) => decodeURIComponent(segment));
33
19
  const gitIndex = segments.length - 2;
34
- const projectIndex = gitIndex - 1;
35
- if (gitIndex < 1 || segments[gitIndex]?.toLowerCase() !== "_git" || segments.some((segment) => !segment || segment === "." || segment === ".." || /[/\\]/.test(segment))) return;
36
- const project = segments[projectIndex];
20
+ if (gitIndex < 0 || segments[gitIndex]?.toLowerCase() !== "_git" || segments.some((segment) => !segment || segment === "." || segment === ".." || /[/\\]/.test(segment))) return;
37
21
  const repository = segments[gitIndex + 1];
38
- if (!project || !repository) return void 0;
39
- const collectionPath = segments.slice(0, projectIndex);
22
+ const prefix = segments.slice(0, gitIndex);
23
+ let collection;
24
+ let projectSegment;
25
+ let virtualDirectory;
40
26
  if (legacyVsts) {
41
- if (collectionPath.length !== 0) return void 0;
42
- return {
43
- collection: void 0,
44
- project,
45
- repositoryPath: `${project}/_git/${repository}`,
46
- virtualDirectory: void 0
47
- };
27
+ if (prefix[0]?.toLowerCase() === "defaultcollection") prefix.shift();
28
+ if (prefix.length > 1) return void 0;
29
+ [projectSegment] = prefix;
30
+ } else if (cloud) {
31
+ if (prefix.length === 0 || prefix.length > 2) return void 0;
32
+ [collection, projectSegment] = prefix;
33
+ } else if (prefix.length === 1) [collection] = prefix;
34
+ else {
35
+ if (prefix.length === 0) return void 0;
36
+ [collection, projectSegment] = prefix.slice(-2);
37
+ if (prefix.length > 2) virtualDirectory = prefix.slice(0, -2).join("/");
48
38
  }
49
- if (collectionPath.length === 0 || cloud && collectionPath.length !== 1) return void 0;
39
+ const project = projectSegment ?? repository;
50
40
  return {
51
- collection: collectionPath.at(-1),
41
+ collection,
52
42
  project,
43
+ repository,
53
44
  repositoryPath: `${project}/_git/${repository}`,
54
- virtualDirectory: collectionPath.length > 1 ? collectionPath.slice(0, -1).join("/") : void 0
45
+ virtualDirectory
55
46
  };
56
47
  }
57
- function parseAzureNewStyleUrl(url) {
58
- const match = azureHttpsUrlRegex2.exec(url.pathname);
59
- if (match == null) throw new Error(`Invalid Azure URL: ${url.toString()}`);
60
- const [, owner, project, repo] = match;
61
- return [
48
+ function parseAzureHttpsRepository(url) {
49
+ const vsts = isVsts(url.hostname);
50
+ const parsed = parseAzureRepositoryPath(url.pathname.replace(edgeSlashesRegex, ""), vsts, url.hostname.toLowerCase() === "dev.azure.com");
51
+ const owner = vsts ? url.hostname.split(".")[0] : parsed?.collection;
52
+ if (parsed == null || owner == null) return void 0;
53
+ return {
62
54
  owner,
63
- project,
64
- repo
65
- ];
55
+ project: parsed.project,
56
+ repo: parsed.repository
57
+ };
66
58
  }
67
59
  function parseAzureHttpsUrl(arg) {
68
60
  const url = typeof arg === "string" ? new URL(arg) : arg;
69
- if (url.hostname.endsWith(vstsHostnameSuffix)) return parseVstsHttpsUrl(url);
70
- return parseAzureNewStyleUrl(url);
61
+ const parsed = parseAzureHttpsRepository(url);
62
+ if (parsed == null) throw new Error(`Invalid Azure URL: ${url.toString()}`);
63
+ return [
64
+ parsed.owner,
65
+ parsed.project,
66
+ parsed.repo
67
+ ];
71
68
  }
72
69
  var AzureDevOpsRemoteProvider = class AzureDevOpsRemoteProvider extends RemoteProvider {
73
70
  collection;
@@ -96,8 +93,15 @@ var AzureDevOpsRemoteProvider = class AzureDevOpsRemoteProvider extends RemotePr
96
93
  this.virtualDirectory = repositoryPath?.virtualDirectory;
97
94
  }
98
95
  get issueLinkPattern() {
99
- const projectUrl = this.baseUrl.replace(gitTailRegex, "");
100
- return `${projectUrl.substring(0, projectUrl.lastIndexOf("/"))}/_workitems/edit/<num>`;
96
+ let orgUrl;
97
+ if (this.repositoryPath != null) {
98
+ const orgPath = [this.virtualDirectory, this.collection].filter(Boolean).join("/");
99
+ orgUrl = `${this.protocol}://${this.domain}${orgPath ? `/${orgPath}` : ""}`;
100
+ } else {
101
+ const projectUrl = this.baseUrl.replace(gitTailRegex, "");
102
+ orgUrl = projectUrl.substring(0, projectUrl.lastIndexOf("/"));
103
+ }
104
+ return `${orgUrl}/_workitems/edit/<num>`;
101
105
  }
102
106
  _autolinks;
103
107
  get autolinks() {
@@ -153,8 +157,7 @@ var AzureDevOpsRemoteProvider = class AzureDevOpsRemoteProvider extends RemotePr
153
157
  return this.collection ?? super.owner;
154
158
  }
155
159
  get repoName() {
156
- if (isVsts(this.domain)) return this.path;
157
- return this.repositoryPath ?? super.repoName;
160
+ return this.repositoryPath ?? (isVsts(this.domain) ? this.path : super.repoName);
158
161
  }
159
162
  get repoDesc() {
160
163
  const descriptor = super.repoDesc;
@@ -174,7 +177,7 @@ var AzureDevOpsRemoteProvider = class AzureDevOpsRemoteProvider extends RemotePr
174
177
  }
175
178
  _displayPath;
176
179
  get displayPath() {
177
- this._displayPath ??= this.path.replace(gitRegex, "/").replace(legacyDefaultCollectionRegex, "");
180
+ this._displayPath ??= this.path.replace(leadingGitRegex, "").replace(gitRegex, "/").replace(legacyDefaultCollectionRegex, "");
178
181
  return this._displayPath;
179
182
  }
180
183
  static fileQueryRegex = /path=([^&]+)/i;
@@ -260,12 +263,17 @@ function parseAzureUrl(url) {
260
263
  };
261
264
  }
262
265
  } else {
263
- const [org, project, rest] = parseAzureHttpsUrl(url);
264
- return {
265
- org,
266
- project,
267
- repo: rest
268
- };
266
+ let parsed;
267
+ try {
268
+ parsed = parseAzureHttpsRepository(new URL(url));
269
+ } catch {
270
+ return;
271
+ }
272
+ return parsed != null ? {
273
+ org: parsed.owner,
274
+ project: parsed.project,
275
+ repo: parsed.repo
276
+ } : void 0;
269
277
  }
270
278
  }
271
279
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"azure-devops.js","names":[],"sources":["../../../../git/src/remotes/azure-devops.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport type { Brand, Unbrand } from '@gitlens/utils/brand.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { RemoteProviderContext } from '../context.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '../models/autolink.js';\nimport type { LineRange } from '../models/lineRange.js';\nimport type { ParsedRemoteFileUri, RemoteProviderId } from '../models/remoteProvider.js';\nimport { RemoteProvider } from '../models/remoteProvider.js';\nimport type { CreatePullRequestRemoteResource } from '../models/remoteResource.js';\nimport type { GkProviderId } from '../models/repositoryIdentities.js';\nimport type { ResourceDescriptor } from '../models/resourceDescriptor.js';\nimport type { GitRevisionRangeNotation } from '../models/revision.js';\n\nconst gitRegex = /\\/_git\\/?/i;\nconst gitTailRegex = /\\/_git(?:\\/.*)?$/i;\nconst legacyDefaultCollectionRegex = /^DefaultCollection\\//i;\nconst orgAndProjectRegex = /^(.*?)\\/(.*?)\\/(.*)/;\nconst sshDomainRegex = /^(ssh|vs-ssh)\\./i;\nconst sshPathRegex = /^\\/?v\\d\\//i;\n\nconst azureSshUrlRegex = /^(?:[^@]+@)?([^:]+):v\\d\\//;\n\nconst vstsHostnameSuffix = '.visualstudio.com';\n\nexport function isVsts(domain: string): boolean {\n\treturn domain.endsWith(vstsHostnameSuffix);\n}\n\nfunction getVSTSOwner(url: URL): string {\n\treturn url.hostname.split('.')[0];\n}\n\nconst azureProjectRepoRegex = /([^/]+)\\/_git\\/([^/]+)/;\n\nfunction parseVstsHttpsUrl(url: URL): [string, string, string] {\n\tconst owner = getVSTSOwner(url);\n\tconst match = azureProjectRepoRegex.exec(url.pathname);\n\tif (match == null) throw new Error(`Invalid VSTS URL: ${url.toString()}`);\n\n\tconst [, project, repo] = match;\n\treturn [owner, project, repo];\n}\n\nconst azureHttpsUrlRegex2 = /([^/]+)\\/([^/]+)\\/_git\\/([^/]+)/;\n\ntype AzureRepositoryPath = {\n\tcollection: string | undefined;\n\tproject: string;\n\trepositoryPath: string;\n\tvirtualDirectory: string | undefined;\n};\n\nexport type AzureDevOpsRepositoryDescriptor = ResourceDescriptor & {\n\towner: string | undefined;\n\tname: string | undefined;\n\tvirtualDirectory?: string;\n};\n\nfunction parseAzureRepositoryPath(path: string, legacyVsts: boolean, cloud: boolean): AzureRepositoryPath | undefined {\n\tconst segments = path.split('/').map(segment => decodeURIComponent(segment));\n\tconst gitIndex = segments.length - 2;\n\tconst projectIndex = gitIndex - 1;\n\tif (\n\t\tgitIndex < 1 ||\n\t\tsegments[gitIndex]?.toLowerCase() !== '_git' ||\n\t\tsegments.some(segment => !segment || segment === '.' || segment === '..' || /[/\\\\]/.test(segment))\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst project = segments[projectIndex];\n\tconst repository = segments[gitIndex + 1];\n\tif (!project || !repository) return undefined;\n\n\tconst collectionPath = segments.slice(0, projectIndex);\n\tif (legacyVsts) {\n\t\tif (collectionPath.length !== 0) return undefined;\n\n\t\treturn {\n\t\t\tcollection: undefined,\n\t\t\tproject: project,\n\t\t\trepositoryPath: `${project}/_git/${repository}`,\n\t\t\tvirtualDirectory: undefined,\n\t\t};\n\t}\n\n\tif (collectionPath.length === 0 || (cloud && collectionPath.length !== 1)) return undefined;\n\n\treturn {\n\t\tcollection: collectionPath.at(-1),\n\t\tproject: project,\n\t\trepositoryPath: `${project}/_git/${repository}`,\n\t\tvirtualDirectory: collectionPath.length > 1 ? collectionPath.slice(0, -1).join('/') : undefined,\n\t};\n}\n\nfunction parseAzureNewStyleUrl(url: URL): [string, string, string] {\n\tconst match = azureHttpsUrlRegex2.exec(url.pathname);\n\tif (match == null) throw new Error(`Invalid Azure URL: ${url.toString()}`);\n\n\tconst [, owner, project, repo] = match;\n\treturn [owner, project, repo];\n}\n\nexport function parseAzureHttpsUrl(url: string): [owner: string, project: string, repo: string];\nexport function parseAzureHttpsUrl(urlObj: URL): [owner: string, project: string, repo: string];\nexport function parseAzureHttpsUrl(arg: URL | string): [owner: string, project: string, repo: string] {\n\tconst url = typeof arg === 'string' ? new URL(arg) : arg;\n\tif (url.hostname.endsWith(vstsHostnameSuffix)) return parseVstsHttpsUrl(url);\n\treturn parseAzureNewStyleUrl(url);\n}\n\nexport class AzureDevOpsRemoteProvider extends RemoteProvider<AzureDevOpsRepositoryDescriptor> {\n\tprivate readonly collection: string | undefined;\n\tprivate readonly project: string | undefined;\n\tprivate readonly repositoryPath: string | undefined;\n\tprivate readonly virtualDirectory: string | undefined;\n\n\tconstructor(\n\t\tdomain: string,\n\t\tpath: string,\n\t\tprotocol?: string,\n\t\tname?: string,\n\t\tisVsts: boolean = false,\n\t\tcontext?: RemoteProviderContext,\n\t) {\n\t\tif (sshDomainRegex.test(domain)) {\n\t\t\tpath = path.replace(sshPathRegex, '');\n\t\t\tdomain = domain.replace(sshDomainRegex, '');\n\n\t\t\t// Add in /_git/ into ssh urls\n\t\t\tconst match = orgAndProjectRegex.exec(path);\n\t\t\tif (match != null) {\n\t\t\t\tconst [, org, project, rest] = match;\n\n\t\t\t\t// VSTS puts the org in the subdomain; modern Azure DevOps puts it in the path\n\t\t\t\tif (isVsts) {\n\t\t\t\t\tdomain = `${org}.${domain}`;\n\t\t\t\t\tpath = `${project}/_git/${rest}`;\n\t\t\t\t} else {\n\t\t\t\t\tpath = `${org}/${project}/_git/${rest}`;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Azure DevOps allows projects and repository names with spaces. In that situation,\n\t\t// the `path` will be previously encoded during git clone\n\t\t// revert that encoding to avoid double-encoding by gitlens during copy remote and open remote\n\t\tconst repositoryPath = parseAzureRepositoryPath(path, isVsts, domain.toLowerCase() === 'dev.azure.com');\n\t\tpath = decodeURIComponent(path);\n\t\tsuper(domain, path, protocol, name, undefined, context);\n\t\tthis.collection = repositoryPath?.collection;\n\t\tthis.project = repositoryPath?.project;\n\t\tthis.repositoryPath = repositoryPath?.repositoryPath;\n\t\tthis.virtualDirectory = repositoryPath?.virtualDirectory;\n\t}\n\n\tprotected override get issueLinkPattern(): string {\n\t\tconst projectUrl = this.baseUrl.replace(gitTailRegex, '');\n\t\tconst orgUrl = projectUrl.substring(0, projectUrl.lastIndexOf('/'));\n\t\treturn `${orgUrl}/_workitems/edit/<num>`;\n\t}\n\n\tprivate _autolinks: (AutolinkReference | DynamicAutolinkReference)[] | undefined;\n\toverride get autolinks(): (AutolinkReference | DynamicAutolinkReference)[] {\n\t\t// Strip off any `_git` part from the repo url\n\t\tthis._autolinks ??= [\n\t\t\t...super.autolinks,\n\t\t\t{\n\t\t\t\tprefix: '#',\n\t\t\t\turl: this.issueLinkPattern,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\ttitle: l10n.t('Open Work Item #{number} on {provider}', { number: '<num>', provider: this.name }),\n\n\t\t\t\ttype: 'issue',\n\t\t\t\tdescription: l10n.t('{provider} Work Item #{number}', { provider: this.name, number: '<num>' }),\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Default Pull request message when merging a PR in ADO. Will not catch commits & pushes following a different pattern.\n\t\t\t\tprefix: 'PR ',\n\t\t\t\turl: `${this.baseUrl}/pullrequest/<num>`,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\ttitle: l10n.t('Open Pull Request #{number} on {provider}', { number: '<num>', provider: this.name }),\n\n\t\t\t\ttype: 'pullrequest',\n\t\t\t\tdescription: l10n.t('{provider} Pull Request #{number}', {\n\t\t\t\t\tprovider: this.name,\n\t\t\t\t\tnumber: '<num>',\n\t\t\t\t}),\n\t\t\t},\n\t\t];\n\t\treturn this._autolinks;\n\t}\n\n\toverride get icon(): string {\n\t\treturn 'azdo';\n\t}\n\n\tget id(): RemoteProviderId {\n\t\treturn 'azure-devops';\n\t}\n\n\tget gkProviderId(): GkProviderId {\n\t\treturn 'azureDevops' satisfies Unbrand<GkProviderId> as Brand<GkProviderId>;\n\t}\n\n\tget name(): string {\n\t\treturn 'Azure DevOps';\n\t}\n\n\toverride get owner(): string | undefined {\n\t\tif (isVsts(this.domain)) {\n\t\t\treturn this.domain.split('.')[0];\n\t\t}\n\t\treturn this.collection ?? super.owner;\n\t}\n\n\toverride get repoName(): string | undefined {\n\t\tif (isVsts(this.domain)) {\n\t\t\treturn this.path;\n\t\t}\n\t\treturn this.repositoryPath ?? super.repoName;\n\t}\n\n\toverride get repoDesc(): AzureDevOpsRepositoryDescriptor {\n\t\tconst descriptor = super.repoDesc;\n\t\treturn this.virtualDirectory == null ? descriptor : { ...descriptor, virtualDirectory: this.virtualDirectory };\n\t}\n\n\toverride get providerDesc():\n\t\t| {\n\t\t\t\tid: GkProviderId;\n\t\t\t\trepoDomain: string;\n\t\t\t\trepoName: string;\n\t\t\t\trepoOwnerDomain: string;\n\t\t }\n\t\t| undefined {\n\t\tif (this.gkProviderId == null || this.owner == null || this.repoName == null || this.project == null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tid: this.gkProviderId,\n\t\t\trepoDomain: this.project,\n\t\t\trepoName: this.repoName,\n\t\t\trepoOwnerDomain: this.owner,\n\t\t};\n\t}\n\n\tprivate _displayPath: string | undefined;\n\toverride get displayPath(): string {\n\t\tthis._displayPath ??= this.path.replace(gitRegex, '/').replace(legacyDefaultCollectionRegex, '');\n\t\treturn this._displayPath;\n\t}\n\n\tprivate static readonly fileQueryRegex = /path=([^&]+)/i;\n\tprivate static readonly rangeQueryRegex = /line=(\\d+)(?:&lineEnd=(\\d+))?/;\n\n\toverride parseRemoteFileUri(uri: Uri): ParsedRemoteFileUri | undefined {\n\t\tif (uri.authority !== this.domain) return undefined;\n\n\t\tlet startLine;\n\t\tlet endLine;\n\t\tif (uri.query) {\n\t\t\tconst rangeMatch = AzureDevOpsRemoteProvider.rangeQueryRegex.exec(uri.query);\n\t\t\tif (rangeMatch != null) {\n\t\t\t\tconst [, start, end] = rangeMatch;\n\t\t\t\tif (start) {\n\t\t\t\t\tstartLine = parseInt(start, 10);\n\t\t\t\t\tif (end) {\n\t\t\t\t\t\tendLine = parseInt(end, 10);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst fileMatch = AzureDevOpsRemoteProvider.fileQueryRegex.exec(uri.query);\n\t\tif (fileMatch == null) return undefined;\n\n\t\tconst [, path] = fileMatch;\n\n\t\treturn {\n\t\t\tstartLine: startLine,\n\t\t\tendLine: endLine,\n\t\t\tcandidates: [{ type: 'pathOnly', filePath: path }],\n\t\t};\n\t}\n\n\tprotected getUrlForBranches(): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/branches`);\n\t}\n\n\tprotected getUrlForBranch(branch: string): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/?version=GB${branch}&_a=history`);\n\t}\n\n\tprotected getUrlForCommit(sha: string): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/commit/${sha}`);\n\t}\n\n\tprotected override getUrlForComparison(base: string, head: string, _notation: GitRevisionRangeNotation): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/branchCompare?baseVersion=GB${base}&targetVersion=GB${head}`);\n\t}\n\n\tprotected override async getUrlForCreatePullRequest({\n\t\tbase,\n\t\thead,\n\t}: CreatePullRequestRemoteResource): Promise<string | undefined> {\n\t\tconst query = new URLSearchParams({ sourceRef: head.branch, targetRef: base.branch ?? '' });\n\n\t\tif (base.remote.url !== head.remote.url) {\n\t\t\tconst parsedBaseUrl = parseAzureUrl(base.remote.url);\n\t\t\tif (parsedBaseUrl == null) return undefined;\n\n\t\t\tconst { org: baseOrg, project: baseProject, repo: baseName } = parsedBaseUrl;\n\t\t\tconst repoInfo = await this.context?.getRepositoryInfo?.(this.id, {\n\t\t\t\towner: baseOrg,\n\t\t\t\tname: baseName,\n\t\t\t\tproject: baseProject,\n\t\t\t});\n\t\t\tif (!repoInfo) return undefined;\n\n\t\t\tquery.set('targetRepositoryId', repoInfo.id);\n\t\t}\n\n\t\treturn `${this.encodeUrl(`${this.getRepoBaseUrl(head.remote.path)}/pullrequestcreate`)}?${query.toString()}`;\n\t}\n\n\tprotected getUrlForFile(fileName: string, branch?: string, sha?: string, range?: LineRange): string {\n\t\tlet line;\n\t\tif (range != null) {\n\t\t\tif (range.startLine === range.endLine) {\n\t\t\t\tline = `&line=${range.startLine}&lineStartColumn=${range.startCharacter}&lineEndColumn=${range.endCharacter}`;\n\t\t\t} else {\n\t\t\t\tline = `&line=${range.startLine}&lineEnd=${range.endLine}&lineStartColumn=${range.startCharacter}&lineEndColumn=${range.endCharacter}`;\n\t\t\t}\n\t\t} else {\n\t\t\tline = '';\n\t\t}\n\n\t\tif (sha) return this.encodeUrl(`${this.baseUrl}?path=${fileName}&version=GC${sha}${line}&_a=contents`);\n\t\tif (branch) return this.encodeUrl(`${this.baseUrl}/?path=/${fileName}&version=GB${branch}&_a=contents${line}`);\n\t\treturn this.encodeUrl(`${this.baseUrl}?path=/${fileName}${line}`);\n\t}\n}\n\nfunction parseAzureUrl(url: string): { org: string; project: string; repo: string } | undefined {\n\tif (azureSshUrlRegex.test(url)) {\n\t\t// Examples of SSH urls:\n\t\t// - old one: bbbchiv@vs-ssh.visualstudio.com:v3/bbbchiv/MyFirstProject/test\n\t\t// - modern one: git@ssh.dev.azure.com:v3/bbbchiv2/MyFirstProject/test\n\t\turl = url.replace(azureSshUrlRegex, '');\n\t\tconst match = orgAndProjectRegex.exec(url);\n\t\tif (match != null) {\n\t\t\tconst [, org, project, rest] = match;\n\t\t\treturn { org: org, project: project, repo: rest };\n\t\t}\n\t} else {\n\t\tconst [org, project, rest] = parseAzureHttpsUrl(url);\n\t\treturn { org: org, project: project, repo: rest };\n\t}\n\treturn undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"azure-devops.js","names":[],"sources":["../../../../git/src/remotes/azure-devops.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport type { Brand, Unbrand } from '@gitlens/utils/brand.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { RemoteProviderContext } from '../context.js';\nimport type { AutolinkReference, DynamicAutolinkReference } from '../models/autolink.js';\nimport type { LineRange } from '../models/lineRange.js';\nimport type { ParsedRemoteFileUri, RemoteProviderId } from '../models/remoteProvider.js';\nimport { RemoteProvider } from '../models/remoteProvider.js';\nimport type { CreatePullRequestRemoteResource } from '../models/remoteResource.js';\nimport type { GkProviderId } from '../models/repositoryIdentities.js';\nimport type { ResourceDescriptor } from '../models/resourceDescriptor.js';\nimport type { GitRevisionRangeNotation } from '../models/revision.js';\n\nconst gitRegex = /\\/_git\\/?/i;\nconst gitTailRegex = /\\/_git(?:\\/.*)?$/i;\nconst edgeSlashesRegex = /^\\/+|\\/+$/g;\nconst leadingGitRegex = /^_git\\//i;\nconst legacyDefaultCollectionRegex = /^DefaultCollection\\//i;\nconst orgAndProjectRegex = /^(.*?)\\/(.*?)\\/(.*)/;\nconst sshDomainRegex = /^(ssh|vs-ssh)\\./i;\nconst sshPathRegex = /^\\/?v\\d\\//i;\n\nconst azureSshUrlRegex = /^(?:[^@]+@)?([^:]+):v\\d\\//;\n\nconst vstsHostnameSuffix = '.visualstudio.com';\n\nexport function isVsts(domain: string): boolean {\n\treturn domain.toLowerCase().endsWith(vstsHostnameSuffix);\n}\n\ntype AzureRepositoryPath = {\n\tcollection: string | undefined;\n\tproject: string;\n\trepository: string;\n\trepositoryPath: string;\n\tvirtualDirectory: string | undefined;\n};\n\nexport type AzureDevOpsRepositoryDescriptor = ResourceDescriptor & {\n\towner: string | undefined;\n\tname: string | undefined;\n\tvirtualDirectory?: string;\n};\n\nfunction parseAzureRepositoryPath(path: string, legacyVsts: boolean, cloud: boolean): AzureRepositoryPath | undefined {\n\tconst segments = path.split('/').map(segment => decodeURIComponent(segment));\n\tconst gitIndex = segments.length - 2;\n\tif (\n\t\tgitIndex < 0 ||\n\t\tsegments[gitIndex]?.toLowerCase() !== '_git' ||\n\t\tsegments.some(segment => !segment || segment === '.' || segment === '..' || /[/\\\\]/.test(segment))\n\t) {\n\t\treturn undefined;\n\t}\n\n\tconst repository = segments[gitIndex + 1];\n\tconst prefix = segments.slice(0, gitIndex);\n\n\tlet collection: string | undefined;\n\tlet projectSegment: string | undefined;\n\tlet virtualDirectory: string | undefined;\n\tif (legacyVsts) {\n\t\t// A VSTS-era clone URL may still name the service's only collection, which is the organization the host\n\t\t// already carries, so it never selects a project\n\t\tif (prefix[0]?.toLowerCase() === 'defaultcollection') {\n\t\t\tprefix.shift();\n\t\t}\n\t\tif (prefix.length > 1) return undefined;\n\n\t\t[projectSegment] = prefix;\n\t} else if (cloud) {\n\t\tif (prefix.length === 0 || prefix.length > 2) return undefined;\n\n\t\t[collection, projectSegment] = prefix;\n\t} else if (prefix.length === 1) {\n\t\t// A server path always names its collection, so a lone segment before `_git` is the collection, and the project\n\t\t// segment was left out (TFS 2018 reports that form as the clone URL of a repository named after its project)\n\t\t[collection] = prefix;\n\t} else {\n\t\t// With more segments the last two read as collection and project. A project-less path behind a virtual directory\n\t\t// (`{vdir}/{collection}/_git/{repo}`) reads the same way, which the URL can't tell apart; the Azure DevOps Server\n\t\t// integration settles it against the collections it discovers\n\t\tif (prefix.length === 0) return undefined;\n\n\t\t[collection, projectSegment] = prefix.slice(-2);\n\t\tif (prefix.length > 2) {\n\t\t\tvirtualDirectory = prefix.slice(0, -2).join('/');\n\t\t}\n\t}\n\n\t// With no project segment (`{org}/_git/{repo}`), Azure names the repository after its project\n\tconst project = projectSegment ?? repository;\n\treturn {\n\t\tcollection: collection,\n\t\tproject: project,\n\t\trepository: repository,\n\t\trepositoryPath: `${project}/_git/${repository}`,\n\t\tvirtualDirectory: virtualDirectory,\n\t};\n}\n\nfunction parseAzureHttpsRepository(url: URL): { owner: string; project: string; repo: string } | undefined {\n\tconst vsts = isVsts(url.hostname);\n\tconst parsed = parseAzureRepositoryPath(\n\t\turl.pathname.replace(edgeSlashesRegex, ''),\n\t\tvsts,\n\t\turl.hostname.toLowerCase() === 'dev.azure.com',\n\t);\n\t// VSTS carries the organization in the host rather than in the path\n\tconst owner = vsts ? url.hostname.split('.')[0] : parsed?.collection;\n\tif (parsed == null || owner == null) return undefined;\n\n\treturn { owner: owner, project: parsed.project, repo: parsed.repository };\n}\n\nexport function parseAzureHttpsUrl(url: string): [owner: string, project: string, repo: string];\nexport function parseAzureHttpsUrl(urlObj: URL): [owner: string, project: string, repo: string];\nexport function parseAzureHttpsUrl(arg: URL | string): [owner: string, project: string, repo: string] {\n\tconst url = typeof arg === 'string' ? new URL(arg) : arg;\n\tconst parsed = parseAzureHttpsRepository(url);\n\tif (parsed == null) throw new Error(`Invalid Azure URL: ${url.toString()}`);\n\n\treturn [parsed.owner, parsed.project, parsed.repo];\n}\n\nexport class AzureDevOpsRemoteProvider extends RemoteProvider<AzureDevOpsRepositoryDescriptor> {\n\tprivate readonly collection: string | undefined;\n\tprivate readonly project: string | undefined;\n\tprivate readonly repositoryPath: string | undefined;\n\tprivate readonly virtualDirectory: string | undefined;\n\n\tconstructor(\n\t\tdomain: string,\n\t\tpath: string,\n\t\tprotocol?: string,\n\t\tname?: string,\n\t\tisVsts: boolean = false,\n\t\tcontext?: RemoteProviderContext,\n\t) {\n\t\tif (sshDomainRegex.test(domain)) {\n\t\t\tpath = path.replace(sshPathRegex, '');\n\t\t\tdomain = domain.replace(sshDomainRegex, '');\n\n\t\t\t// Add in /_git/ into ssh urls\n\t\t\tconst match = orgAndProjectRegex.exec(path);\n\t\t\tif (match != null) {\n\t\t\t\tconst [, org, project, rest] = match;\n\n\t\t\t\t// VSTS puts the org in the subdomain; modern Azure DevOps puts it in the path\n\t\t\t\tif (isVsts) {\n\t\t\t\t\tdomain = `${org}.${domain}`;\n\t\t\t\t\tpath = `${project}/_git/${rest}`;\n\t\t\t\t} else {\n\t\t\t\t\tpath = `${org}/${project}/_git/${rest}`;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Azure DevOps allows projects and repository names with spaces. In that situation,\n\t\t// the `path` will be previously encoded during git clone\n\t\t// revert that encoding to avoid double-encoding by gitlens during copy remote and open remote\n\t\tconst repositoryPath = parseAzureRepositoryPath(path, isVsts, domain.toLowerCase() === 'dev.azure.com');\n\t\tpath = decodeURIComponent(path);\n\t\tsuper(domain, path, protocol, name, undefined, context);\n\t\tthis.collection = repositoryPath?.collection;\n\t\tthis.project = repositoryPath?.project;\n\t\tthis.repositoryPath = repositoryPath?.repositoryPath;\n\t\tthis.virtualDirectory = repositoryPath?.virtualDirectory;\n\t}\n\n\tprotected override get issueLinkPattern(): string {\n\t\tlet orgUrl;\n\t\tif (this.repositoryPath != null) {\n\t\t\t// Work items live at the organization (collection) root: the host itself on VSTS, which is why a legacy\n\t\t\t// `DefaultCollection` segment and a project-less repository path need no special casing here\n\t\t\tconst orgPath = [this.virtualDirectory, this.collection].filter(Boolean).join('/');\n\t\t\torgUrl = `${this.protocol}://${this.domain}${orgPath ? `/${orgPath}` : ''}`;\n\t\t} else {\n\t\t\tconst projectUrl = this.baseUrl.replace(gitTailRegex, '');\n\t\t\torgUrl = projectUrl.substring(0, projectUrl.lastIndexOf('/'));\n\t\t}\n\t\treturn `${orgUrl}/_workitems/edit/<num>`;\n\t}\n\n\tprivate _autolinks: (AutolinkReference | DynamicAutolinkReference)[] | undefined;\n\toverride get autolinks(): (AutolinkReference | DynamicAutolinkReference)[] {\n\t\t// Strip off any `_git` part from the repo url\n\t\tthis._autolinks ??= [\n\t\t\t...super.autolinks,\n\t\t\t{\n\t\t\t\tprefix: '#',\n\t\t\t\turl: this.issueLinkPattern,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\ttitle: l10n.t('Open Work Item #{number} on {provider}', { number: '<num>', provider: this.name }),\n\n\t\t\t\ttype: 'issue',\n\t\t\t\tdescription: l10n.t('{provider} Work Item #{number}', { provider: this.name, number: '<num>' }),\n\t\t\t},\n\t\t\t{\n\t\t\t\t// Default Pull request message when merging a PR in ADO. Will not catch commits & pushes following a different pattern.\n\t\t\t\tprefix: 'PR ',\n\t\t\t\turl: `${this.baseUrl}/pullrequest/<num>`,\n\t\t\t\talphanumeric: false,\n\t\t\t\tignoreCase: false,\n\t\t\t\ttitle: l10n.t('Open Pull Request #{number} on {provider}', { number: '<num>', provider: this.name }),\n\n\t\t\t\ttype: 'pullrequest',\n\t\t\t\tdescription: l10n.t('{provider} Pull Request #{number}', {\n\t\t\t\t\tprovider: this.name,\n\t\t\t\t\tnumber: '<num>',\n\t\t\t\t}),\n\t\t\t},\n\t\t];\n\t\treturn this._autolinks;\n\t}\n\n\toverride get icon(): string {\n\t\treturn 'azdo';\n\t}\n\n\tget id(): RemoteProviderId {\n\t\treturn 'azure-devops';\n\t}\n\n\tget gkProviderId(): GkProviderId {\n\t\treturn 'azureDevops' satisfies Unbrand<GkProviderId> as Brand<GkProviderId>;\n\t}\n\n\tget name(): string {\n\t\treturn 'Azure DevOps';\n\t}\n\n\toverride get owner(): string | undefined {\n\t\tif (isVsts(this.domain)) {\n\t\t\treturn this.domain.split('.')[0];\n\t\t}\n\t\treturn this.collection ?? super.owner;\n\t}\n\n\toverride get repoName(): string | undefined {\n\t\treturn this.repositoryPath ?? (isVsts(this.domain) ? this.path : super.repoName);\n\t}\n\n\toverride get repoDesc(): AzureDevOpsRepositoryDescriptor {\n\t\tconst descriptor = super.repoDesc;\n\t\treturn this.virtualDirectory == null ? descriptor : { ...descriptor, virtualDirectory: this.virtualDirectory };\n\t}\n\n\toverride get providerDesc():\n\t\t| {\n\t\t\t\tid: GkProviderId;\n\t\t\t\trepoDomain: string;\n\t\t\t\trepoName: string;\n\t\t\t\trepoOwnerDomain: string;\n\t\t }\n\t\t| undefined {\n\t\tif (this.gkProviderId == null || this.owner == null || this.repoName == null || this.project == null) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tid: this.gkProviderId,\n\t\t\trepoDomain: this.project,\n\t\t\trepoName: this.repoName,\n\t\t\trepoOwnerDomain: this.owner,\n\t\t};\n\t}\n\n\tprivate _displayPath: string | undefined;\n\toverride get displayPath(): string {\n\t\tthis._displayPath ??= this.path\n\t\t\t.replace(leadingGitRegex, '')\n\t\t\t.replace(gitRegex, '/')\n\t\t\t.replace(legacyDefaultCollectionRegex, '');\n\t\treturn this._displayPath;\n\t}\n\n\tprivate static readonly fileQueryRegex = /path=([^&]+)/i;\n\tprivate static readonly rangeQueryRegex = /line=(\\d+)(?:&lineEnd=(\\d+))?/;\n\n\toverride parseRemoteFileUri(uri: Uri): ParsedRemoteFileUri | undefined {\n\t\tif (uri.authority !== this.domain) return undefined;\n\n\t\tlet startLine;\n\t\tlet endLine;\n\t\tif (uri.query) {\n\t\t\tconst rangeMatch = AzureDevOpsRemoteProvider.rangeQueryRegex.exec(uri.query);\n\t\t\tif (rangeMatch != null) {\n\t\t\t\tconst [, start, end] = rangeMatch;\n\t\t\t\tif (start) {\n\t\t\t\t\tstartLine = parseInt(start, 10);\n\t\t\t\t\tif (end) {\n\t\t\t\t\t\tendLine = parseInt(end, 10);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst fileMatch = AzureDevOpsRemoteProvider.fileQueryRegex.exec(uri.query);\n\t\tif (fileMatch == null) return undefined;\n\n\t\tconst [, path] = fileMatch;\n\n\t\treturn {\n\t\t\tstartLine: startLine,\n\t\t\tendLine: endLine,\n\t\t\tcandidates: [{ type: 'pathOnly', filePath: path }],\n\t\t};\n\t}\n\n\tprotected getUrlForBranches(): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/branches`);\n\t}\n\n\tprotected getUrlForBranch(branch: string): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/?version=GB${branch}&_a=history`);\n\t}\n\n\tprotected getUrlForCommit(sha: string): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/commit/${sha}`);\n\t}\n\n\tprotected override getUrlForComparison(base: string, head: string, _notation: GitRevisionRangeNotation): string {\n\t\treturn this.encodeUrl(`${this.baseUrl}/branchCompare?baseVersion=GB${base}&targetVersion=GB${head}`);\n\t}\n\n\tprotected override async getUrlForCreatePullRequest({\n\t\tbase,\n\t\thead,\n\t}: CreatePullRequestRemoteResource): Promise<string | undefined> {\n\t\tconst query = new URLSearchParams({ sourceRef: head.branch, targetRef: base.branch ?? '' });\n\n\t\tif (base.remote.url !== head.remote.url) {\n\t\t\tconst parsedBaseUrl = parseAzureUrl(base.remote.url);\n\t\t\tif (parsedBaseUrl == null) return undefined;\n\n\t\t\tconst { org: baseOrg, project: baseProject, repo: baseName } = parsedBaseUrl;\n\t\t\tconst repoInfo = await this.context?.getRepositoryInfo?.(this.id, {\n\t\t\t\towner: baseOrg,\n\t\t\t\tname: baseName,\n\t\t\t\tproject: baseProject,\n\t\t\t});\n\t\t\tif (!repoInfo) return undefined;\n\n\t\t\tquery.set('targetRepositoryId', repoInfo.id);\n\t\t}\n\n\t\treturn `${this.encodeUrl(`${this.getRepoBaseUrl(head.remote.path)}/pullrequestcreate`)}?${query.toString()}`;\n\t}\n\n\tprotected getUrlForFile(fileName: string, branch?: string, sha?: string, range?: LineRange): string {\n\t\tlet line;\n\t\tif (range != null) {\n\t\t\tif (range.startLine === range.endLine) {\n\t\t\t\tline = `&line=${range.startLine}&lineStartColumn=${range.startCharacter}&lineEndColumn=${range.endCharacter}`;\n\t\t\t} else {\n\t\t\t\tline = `&line=${range.startLine}&lineEnd=${range.endLine}&lineStartColumn=${range.startCharacter}&lineEndColumn=${range.endCharacter}`;\n\t\t\t}\n\t\t} else {\n\t\t\tline = '';\n\t\t}\n\n\t\tif (sha) return this.encodeUrl(`${this.baseUrl}?path=${fileName}&version=GC${sha}${line}&_a=contents`);\n\t\tif (branch) return this.encodeUrl(`${this.baseUrl}/?path=/${fileName}&version=GB${branch}&_a=contents${line}`);\n\t\treturn this.encodeUrl(`${this.baseUrl}?path=/${fileName}${line}`);\n\t}\n}\n\nfunction parseAzureUrl(url: string): { org: string; project: string; repo: string } | undefined {\n\tif (azureSshUrlRegex.test(url)) {\n\t\t// Examples of SSH urls:\n\t\t// - old one: bbbchiv@vs-ssh.visualstudio.com:v3/bbbchiv/MyFirstProject/test\n\t\t// - modern one: git@ssh.dev.azure.com:v3/bbbchiv2/MyFirstProject/test\n\t\turl = url.replace(azureSshUrlRegex, '');\n\t\tconst match = orgAndProjectRegex.exec(url);\n\t\tif (match != null) {\n\t\t\tconst [, org, project, rest] = match;\n\t\t\treturn { org: org, project: project, repo: rest };\n\t\t}\n\t} else {\n\t\tlet parsed;\n\t\ttry {\n\t\t\tparsed = parseAzureHttpsRepository(new URL(url));\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t\treturn parsed != null ? { org: parsed.owner, project: parsed.project, repo: parsed.repo } : undefined;\n\t}\n\treturn undefined;\n}\n"],"mappings":"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"}
@@ -91,6 +91,18 @@ export interface GitRunOptions {
91
91
  configs?: readonly string[];
92
92
  readonly correlationKey?: string;
93
93
  errors?: GitErrorHandling;
94
+ /**
95
+ * Called before core retries a write that found `index.lock` held by another process — after the wait, right
96
+ * before the next attempt. The index may have changed while the other process held the lock, so this lets the
97
+ * caller re-check what the retry will act on (e.g. the staged set a `commit` records). Rejecting gives up: the
98
+ * rejection becomes the run's failure, in every `errors` mode. Throwing `lockError` back opts out of the retry,
99
+ * leaving the lock failure as the result.
100
+ *
101
+ * It runs while this checkout's index writes are held in order, so it must not write the index through core
102
+ * (it would wait on itself). A run given this hook is never shared with an identical concurrent run. Only the
103
+ * index-lock retry calls it, not the config-lock retry.
104
+ */
105
+ beforeIndexLockRetry?: (lockError: Error) => void | Promise<void>;
94
106
  /**
95
107
  * Non-zero exit codes that are answers rather than failures (e.g. `1` from `diff --quiet`, "has differences");
96
108
  * a listed code resolves as `exited` with that `exitCode` in every `errors` mode.
@@ -2,7 +2,7 @@ import { UnifiedDisposable } from "../utils/disposable.js";
2
2
  import { Event } from "../utils/event.js";
3
3
  import { Uri } from "../utils/uri.js";
4
4
  import { FileWatchingProvider } from "./watching/provider.js";
5
- import { RepositoryWatchService } from "./watching/watchService.js";
5
+ import { RepositoryWatchService, WatchPollingOptions } from "./watching/watchService.js";
6
6
  import { MergeTargetsChangeEvent } from "./cache.js";
7
7
  import { GitProvider } from "./providers/provider.js";
8
8
  import { RepositoryService } from "./repositoryService.js";
@@ -60,14 +60,17 @@ export type ValidateRepoResult = {
60
60
  */
61
61
  export declare class GitService implements UnifiedDisposable {
62
62
  private static _instance;
63
- static createSingleton(watchingProvider?: FileWatchingProvider): GitService;
63
+ /** `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch */
64
+ static createSingleton(watchingProvider?: FileWatchingProvider, options?: {
65
+ watchPolling?: WatchPollingOptions;
66
+ }): GitService;
64
67
  private readonly _onDidChangeProviders;
65
68
  get onDidChangeProviders(): Event<ProvidersChangeEvent>;
66
69
  private readonly _onDidChangeMergeTargets;
67
70
  /**
68
- * Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys, for any registered
69
- * provider that reports it — never for activity timestamps or core's own writes. Once per change per repository
70
- * (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.
71
+ * Fires when a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that
72
+ * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming
73
+ * the branch. Once per change per repository (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.
71
74
  */
72
75
  get onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent>;
73
76
  /** Keyed by the provider's event, so providers sharing one cache are forwarded once */
@@ -33,9 +33,10 @@ import { WorktreeSummaryService } from "./watching/worktreeSummary.js";
33
33
  */
34
34
  var GitService = class GitService {
35
35
  static _instance;
36
- static createSingleton(watchingProvider) {
36
+ /** `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch */
37
+ static createSingleton(watchingProvider, options) {
37
38
  if (GitService._instance != null) throw new Error("GitService already exists — only one instance is allowed");
38
- const instance = new GitService(watchingProvider);
39
+ const instance = new GitService(watchingProvider, options?.watchPolling);
39
40
  GitService._instance = instance;
40
41
  return instance;
41
42
  }
@@ -45,9 +46,9 @@ var GitService = class GitService {
45
46
  }
46
47
  _onDidChangeMergeTargets = new Emitter();
47
48
  /**
48
- * Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys, for any registered
49
- * provider that reports it — never for activity timestamps or core's own writes. Once per change per repository
50
- * (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.
49
+ * Fires when a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that
50
+ * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming
51
+ * the branch. Once per change per repository (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.
51
52
  */
52
53
  get onDidChangeMergeTargets() {
53
54
  return this._onDidChangeMergeTargets.event;
@@ -59,10 +60,11 @@ var GitService = class GitService {
59
60
  _serviceCache = /* @__PURE__ */ new Map();
60
61
  _watchService;
61
62
  _summaries;
62
- constructor(watchingProvider) {
63
+ constructor(watchingProvider, watchPolling) {
63
64
  if (watchingProvider != null) {
64
65
  this._watchService = new RepositoryWatchService({
65
66
  watchingProvider,
67
+ polling: watchPolling,
66
68
  getIgnoreFilter: (repoPath, gitDirPath) => {
67
69
  return this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);
68
70
  },
@@ -70,7 +72,9 @@ var GitService = class GitService {
70
72
  });
71
73
  this._summaries = new WorktreeSummaryService({
72
74
  watchService: this._watchService,
73
- getRepository: (repoPath) => this.forRepo(repoPath)
75
+ getRepository: (repoPath) => this.forRepo(repoPath),
76
+ onDidChangeMergeTargets: this.onDidChangeMergeTargets,
77
+ getCommonPath: (repoPath) => this.getProvider(repoPath)?.provider.getCommonPath?.(repoPath)
74
78
  });
75
79
  }
76
80
  setGlobalRepositoryServiceResolver((repoPath) => this.forRepo(repoPath));
@@ -84,6 +88,7 @@ var GitService = class GitService {
84
88
  this._serviceCache.clear();
85
89
  this._summaries?.dispose();
86
90
  this._watchService?.dispose();
91
+ for (const entry of this._providers) entry.announcements?.dispose();
87
92
  this._providers.length = 0;
88
93
  for (const source of this._mergeTargetsSources.values()) source.subscription.dispose();
89
94
  this._mergeTargetsSources.clear();
@@ -111,9 +116,11 @@ var GitService = class GitService {
111
116
  * @returns A disposable that unregisters the provider.
112
117
  */
113
118
  register(provider, canHandle) {
119
+ const watchService = this._watchService;
114
120
  const entry = {
115
121
  provider,
116
- canHandle
122
+ canHandle,
123
+ announcements: watchService != null ? provider.onDidAnnounceChanges?.((e) => watchService.noteSelfWrite(e.repoPath, e.changes)) : void 0
117
124
  };
118
125
  this._providers.push(entry);
119
126
  this._etag++;
@@ -128,6 +135,7 @@ var GitService = class GitService {
128
135
  const idx = this._providers.indexOf(entry);
129
136
  if (idx !== -1) {
130
137
  this._providers.splice(idx, 1);
138
+ entry.announcements?.dispose();
131
139
  this._etag++;
132
140
  this.removeMergeTargetsSource(provider);
133
141
  this._serviceCache.clear();
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../../git/src/service.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from './cache.js';\nimport type { GitProvider } from './providers/provider.js';\nimport type { RepositoryService, SubProviderForRepo } from './repositoryService.js';\nimport {\n\tclearGlobalRepositoryServiceResolver,\n\tcreateSubProviderProxyForRepo,\n\tsetGlobalRepositoryServiceResolver,\n} from './repositoryService.js';\nimport type { FileWatchingProvider } from './watching/provider.js';\nimport { RepositoryWatchService } from './watching/watchService.js';\nimport { WorktreeSummaryService } from './watching/worktreeSummary.js';\n\nexport interface ProvidersChangeEvent {\n\treadonly added: readonly GitProvider[];\n\treadonly removed: readonly GitProvider[];\n\treadonly etag: number;\n}\n\n/**\n * Result of {@link GitService.validateRepo}.\n *\n * - `valid: true, safe: true` — a usable git repository at `repoPath`; `gitDir` / `commonGitDir` / `superprojectPath` are populated when available\n * - `valid: true, safe: false` — git detected unsafe (dubious) ownership at the given path; no `repoPath` is surfaced (the raw call doesn't carry one back)\n * - `valid: false` — not a git repository, no registered provider can handle the path, or the routed provider doesn't implement `getRepositoryInfo`\n */\nexport type ValidateRepoResult =\n\t| {\n\t\t\tvalid: true;\n\t\t\tsafe: true;\n\t\t\trepoPath: string;\n\t\t\tgitDir?: string;\n\t\t\tcommonGitDir?: string;\n\t\t\tsuperprojectPath?: string;\n\t }\n\t| { valid: true; safe: false }\n\t| { valid: false };\n\ninterface RegisteredProvider {\n\tprovider: GitProvider;\n\tcanHandle: (repoPath: string) => boolean;\n}\n\ninterface MergeTargetsSource {\n\treadonly subscription: UnifiedDisposable;\n\trefCount: number;\n}\n\n/**\n * Entry point — provider router, repo-scoped proxy cache, and global configuration.\n *\n * Creating an instance wires up the module-level repository\n * resolver. Calling {@link dispose} tears it down.\n *\n * Consumers register providers with routing predicates, then call\n * {@link forRepo} to get a {@link RepositoryService} with sub-provider\n * proxies that auto-inject the repository path.\n *\n * The service has no discovery or repository tracking state — it trusts\n * the caller to pass valid repository paths. Discovery and lifecycle\n * (open/close, file watching, etc.) are application-level concerns.\n *\n * @example\n * ```typescript\n * const service = GitService.createSingleton();\n * service.register(cliProvider, (path) => true);\n *\n * const repo = service.forRepo('/home/user/repo');\n * const branches = await repo.branches.getBranches();\n *\n * service.dispose(); // cleans up module-level hooks\n * ```\n */\nexport class GitService implements UnifiedDisposable {\n\tprivate static _instance: GitService | undefined;\n\n\tstatic createSingleton(watchingProvider?: FileWatchingProvider): GitService {\n\t\tif (GitService._instance != null) {\n\t\t\tthrow new Error('GitService already exists — only one instance is allowed');\n\t\t}\n\n\t\tconst instance = new GitService(watchingProvider);\n\t\tGitService._instance = instance;\n\t\treturn instance;\n\t}\n\n\tprivate readonly _onDidChangeProviders = new Emitter<ProvidersChangeEvent>();\n\tget onDidChangeProviders(): Event<ProvidersChangeEvent> {\n\t\treturn this._onDidChangeProviders.event;\n\t}\n\n\tprivate readonly _onDidChangeMergeTargets = new Emitter<MergeTargetsChangeEvent>();\n\t/**\n\t * Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys, for any registered\n\t * provider that reports it — never for activity timestamps or core's own writes. Once per change per repository\n\t * (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.\n\t */\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._onDidChangeMergeTargets.event;\n\t}\n\n\t/** Keyed by the provider's event, so providers sharing one cache are forwarded once */\n\tprivate readonly _mergeTargetsSources = new Map<Event<MergeTargetsChangeEvent>, MergeTargetsSource>();\n\n\tprivate _etag = 0;\n\tprivate readonly _providers: RegisteredProvider[] = [];\n\tprivate readonly _serviceCache = new Map<string, RepositoryService>();\n\tprivate readonly _watchService: RepositoryWatchService | undefined;\n\tprivate readonly _summaries: WorktreeSummaryService | undefined;\n\n\tprivate constructor(watchingProvider?: FileWatchingProvider) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tgetIgnoreFilter: (repoPath, gitDirPath) => {\n\t\t\t\t\treturn this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);\n\t\t\t\t},\n\t\t\t\tgetIgnoredDirectories: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([]),\n\t\t\t});\n\t\t\tthis._summaries = new WorktreeSummaryService({\n\t\t\t\twatchService: this._watchService,\n\t\t\t\tgetRepository: repoPath => this.forRepo(repoPath),\n\t\t\t});\n\t\t}\n\n\t\tsetGlobalRepositoryServiceResolver(repoPath => this.forRepo(repoPath));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tclearGlobalRepositoryServiceResolver();\n\t\tGitService._instance = undefined;\n\t\tthis._serviceCache.clear();\n\t\tthis._summaries?.dispose();\n\t\tthis._watchService?.dispose();\n\n\t\t// Don't dispose providers since they're owned by the caller, but do clear the array to release references\n\t\tthis._providers.length = 0;\n\n\t\tfor (const source of this._mergeTargetsSources.values()) {\n\t\t\tsource.subscription.dispose();\n\t\t}\n\t\tthis._mergeTargetsSources.clear();\n\n\t\tthis._onDidChangeProviders.dispose();\n\t\tthis._onDidChangeMergeTargets.dispose();\n\t}\n\n\t/** Monotonically increasing counter incremented on structural changes. */\n\tget etag(): number {\n\t\treturn this._etag;\n\t}\n\n\t/** Whether any providers are registered. */\n\tget hasProviders(): boolean {\n\t\treturn this._providers.length > 0;\n\t}\n\n\tget watchService(): RepositoryWatchService | undefined {\n\t\treturn this._watchService;\n\t}\n\n\t/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */\n\tget summaries(): WorktreeSummaryService | undefined {\n\t\treturn this._summaries;\n\t}\n\n\t/**\n\t * Registers a provider with a routing predicate.\n\t *\n\t * @returns A disposable that unregisters the provider.\n\t */\n\tregister(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable {\n\t\tconst entry: RegisteredProvider = { provider: provider, canHandle: canHandle };\n\t\tthis._providers.push(entry);\n\t\tthis._etag++;\n\t\tthis.addMergeTargetsSource(provider);\n\n\t\tthis._onDidChangeProviders.fire({\n\t\t\tadded: [provider],\n\t\t\tremoved: [],\n\t\t\tetag: this._etag,\n\t\t});\n\n\t\t// Invalidate cached proxies since provider routing may have changed\n\t\tthis._serviceCache.clear();\n\n\t\treturn createDisposable(() => {\n\t\t\tconst idx = this._providers.indexOf(entry);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis._providers.splice(idx, 1);\n\t\t\t\tthis._etag++;\n\t\t\t\tthis.removeMergeTargetsSource(provider);\n\t\t\t\tthis._serviceCache.clear();\n\n\t\t\t\tthis._onDidChangeProviders.fire({\n\t\t\t\t\tadded: [],\n\t\t\t\t\tremoved: [provider],\n\t\t\t\t\tetag: this._etag,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate addMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source != null) {\n\t\t\tsource.refCount++;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._mergeTargetsSources.set(event, {\n\t\t\tsubscription: event(e => this._onDidChangeMergeTargets.fire(e)),\n\t\t\trefCount: 1,\n\t\t});\n\t}\n\n\tprivate removeMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source == null || --source.refCount > 0) return;\n\n\t\tsource.subscription.dispose();\n\t\tthis._mergeTargetsSources.delete(event);\n\t}\n\n\t/** Returns the provider that can handle the given path, or undefined if none matches. */\n\tgetProvider(repoPath: string): { provider: GitProvider; path: string } | undefined {\n\t\trepoPath = normalizePath(repoPath);\n\t\tfor (const entry of this._providers) {\n\t\t\tif (entry.canHandle(repoPath)) {\n\t\t\t\treturn { provider: entry.provider, path: repoPath };\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Returns all registered providers. */\n\t*getProviders(): Iterable<GitProvider> {\n\t\tfor (const entry of this._providers) {\n\t\t\tyield entry.provider;\n\t\t}\n\t}\n\n\t/**\n\t * Routes a repo URI/path to a provider and returns a cached\n\t * {@link RepositoryService} with repo-scoped sub-provider proxies.\n\t * Returns undefined if no provider can handle the path.\n\t */\n\tforRepo(repoUri: Uri | string): RepositoryService | undefined {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\n\t\tlet repo = this._serviceCache.get(repoPath);\n\t\tif (repo != null) return repo;\n\n\t\tconst result = this.getProvider(repoPath);\n\t\tif (result == null) return undefined;\n\n\t\trepo = createRepositoryService(result.provider, repoPath, this._watchService);\n\t\tthis._serviceCache.set(repoPath, repo);\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Evicts the cached {@link RepositoryService} for a closed or removed repository.\n\t * No-op if the path isn't cached.\n\t */\n\tcloseRepo(repoUri: Uri | string): void {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\t\tthis._serviceCache.delete(repoPath);\n\t}\n\n\t/**\n\t * Validates whether `pathOrUri` points to (or resides inside) a git repository,\n\t * and returns information about it when it does.\n\t *\n\t * Routes the path to a registered provider and delegates to its config sub-provider's\n\t * `getRepositoryInfo` implementation, then normalizes the raw result into a clean\n\t * discriminated union. For a simple yes/no check, use `(await validateRepo(path)).valid`.\n\t *\n\t * Returns `{ valid: false }` if no provider can handle the path or if the routed\n\t * provider does not implement `getRepositoryInfo`.\n\t */\n\tasync validateRepo(pathOrUri: Uri | string): Promise<ValidateRepoResult> {\n\t\tconst key = getRepositoryKey(pathOrUri);\n\t\tconst routed = this.getProvider(key);\n\t\tif (routed == null) return { valid: false };\n\n\t\tconst info = await routed.provider.config.getRepositoryInfo?.(routed.path);\n\t\tif (info == null) return { valid: false };\n\n\t\tif (Array.isArray(info)) {\n\t\t\tif (info.length === 0) return { valid: false };\n\t\t\tif (info[0] === false) return { valid: true, safe: false };\n\t\t\treturn { valid: true, safe: true, repoPath: info[1] };\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsafe: true,\n\t\t\trepoPath: info.repoPath,\n\t\t\tgitDir: info.gitDir,\n\t\t\tcommonGitDir: info.commonGitDir,\n\t\t\tsuperprojectPath: info.superprojectPath,\n\t\t};\n\t}\n}\n\nfunction createRepositoryService(\n\tprovider: GitProvider,\n\trepoPath: string,\n\twatchService?: RepositoryWatchService,\n): RepositoryService {\n\tconst proxies = new Map<string, SubProviderForRepo<unknown>>();\n\n\tfunction getProxy<T extends object>(prop: string, subProvider: T | undefined): SubProviderForRepo<T> | undefined {\n\t\tif (subProvider == null) return undefined;\n\n\t\tlet proxy = proxies.get(prop);\n\t\tif (proxy == null) {\n\t\t\tproxy = createSubProviderProxyForRepo(subProvider, repoPath);\n\t\t\tproxies.set(prop, proxy);\n\t\t}\n\t\treturn proxy as SubProviderForRepo<T>;\n\t}\n\n\treturn {\n\t\tpath: repoPath,\n\t\tprovider: provider.descriptor,\n\n\t\tget etagWorkingTree(): number | undefined {\n\t\t\treturn watchService?.getSession(repoPath)?.etagWorkingTree;\n\t\t},\n\n\t\tgetAbsoluteUri: (relativePath: string): Uri => {\n\t\t\treturn provider.getAbsoluteUri(relativePath, repoPath);\n\t\t},\n\n\t\t// Required sub-providers\n\t\tget branches() {\n\t\t\tconst proxy = getProxy('branches', provider.branches);\n\t\t\tif (proxy == null) throw new Error('GitService: branches sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget commits() {\n\t\t\tconst proxy = getProxy('commits', provider.commits);\n\t\t\tif (proxy == null) throw new Error('GitService: commits sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget config() {\n\t\t\tconst proxy = getProxy('config', provider.config);\n\t\t\tif (proxy == null) throw new Error('GitService: config sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget contributors() {\n\t\t\tconst proxy = getProxy('contributors', provider.contributors);\n\t\t\tif (proxy == null) throw new Error('GitService: contributors sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget diff() {\n\t\t\tconst proxy = getProxy('diff', provider.diff);\n\t\t\tif (proxy == null) throw new Error('GitService: diff sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget graph() {\n\t\t\tconst proxy = getProxy('graph', provider.graph);\n\t\t\tif (proxy == null) throw new Error('GitService: graph sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget refs() {\n\t\t\tconst proxy = getProxy('refs', provider.refs);\n\t\t\tif (proxy == null) throw new Error('GitService: refs sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget remotes() {\n\t\t\tconst proxy = getProxy('remotes', provider.remotes);\n\t\t\tif (proxy == null) throw new Error('GitService: remotes sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget revision() {\n\t\t\tconst proxy = getProxy('revision', provider.revision);\n\t\t\tif (proxy == null) throw new Error('GitService: revision sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget status() {\n\t\t\tconst proxy = getProxy('status', provider.status);\n\t\t\tif (proxy == null) throw new Error('GitService: status sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget tags() {\n\t\t\tconst proxy = getProxy('tags', provider.tags);\n\t\t\tif (proxy == null) throw new Error('GitService: tags sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\n\t\t// Optional sub-providers\n\t\tget blame() {\n\t\t\treturn getProxy('blame', provider.blame);\n\t\t},\n\t\tget maintenance() {\n\t\t\treturn getProxy('maintenance', provider.maintenance);\n\t\t},\n\t\tget ops() {\n\t\t\treturn getProxy('ops', provider.ops);\n\t\t},\n\t\tget patch() {\n\t\t\treturn getProxy('patch', provider.patch);\n\t\t},\n\t\tget pausedOps() {\n\t\t\treturn getProxy('pausedOps', provider.pausedOps);\n\t\t},\n\t\tget staging() {\n\t\t\treturn getProxy('staging', provider.staging);\n\t\t},\n\t\tget stash() {\n\t\t\treturn getProxy('stash', provider.stash);\n\t\t},\n\t\tget worktrees() {\n\t\t\treturn getProxy('worktrees', provider.worktrees);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../git/src/service.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from './cache.js';\nimport type { GitProvider } from './providers/provider.js';\nimport type { RepositoryService, SubProviderForRepo } from './repositoryService.js';\nimport {\n\tclearGlobalRepositoryServiceResolver,\n\tcreateSubProviderProxyForRepo,\n\tsetGlobalRepositoryServiceResolver,\n} from './repositoryService.js';\nimport type { FileWatchingProvider } from './watching/provider.js';\nimport type { WatchPollingOptions } from './watching/watchService.js';\nimport { RepositoryWatchService } from './watching/watchService.js';\nimport { WorktreeSummaryService } from './watching/worktreeSummary.js';\n\nexport interface ProvidersChangeEvent {\n\treadonly added: readonly GitProvider[];\n\treadonly removed: readonly GitProvider[];\n\treadonly etag: number;\n}\n\n/**\n * Result of {@link GitService.validateRepo}.\n *\n * - `valid: true, safe: true` — a usable git repository at `repoPath`; `gitDir` / `commonGitDir` / `superprojectPath` are populated when available\n * - `valid: true, safe: false` — git detected unsafe (dubious) ownership at the given path; no `repoPath` is surfaced (the raw call doesn't carry one back)\n * - `valid: false` — not a git repository, no registered provider can handle the path, or the routed provider doesn't implement `getRepositoryInfo`\n */\nexport type ValidateRepoResult =\n\t| {\n\t\t\tvalid: true;\n\t\t\tsafe: true;\n\t\t\trepoPath: string;\n\t\t\tgitDir?: string;\n\t\t\tcommonGitDir?: string;\n\t\t\tsuperprojectPath?: string;\n\t }\n\t| { valid: true; safe: false }\n\t| { valid: false };\n\ninterface RegisteredProvider {\n\tprovider: GitProvider;\n\tcanHandle: (repoPath: string) => boolean;\n\t/** Forwards the provider's write announcements to {@link GitService.watchService}, when there is one */\n\tannouncements?: UnifiedDisposable;\n}\n\ninterface MergeTargetsSource {\n\treadonly subscription: UnifiedDisposable;\n\trefCount: number;\n}\n\n/**\n * Entry point — provider router, repo-scoped proxy cache, and global configuration.\n *\n * Creating an instance wires up the module-level repository\n * resolver. Calling {@link dispose} tears it down.\n *\n * Consumers register providers with routing predicates, then call\n * {@link forRepo} to get a {@link RepositoryService} with sub-provider\n * proxies that auto-inject the repository path.\n *\n * The service has no discovery or repository tracking state — it trusts\n * the caller to pass valid repository paths. Discovery and lifecycle\n * (open/close, file watching, etc.) are application-level concerns.\n *\n * @example\n * ```typescript\n * const service = GitService.createSingleton();\n * service.register(cliProvider, (path) => true);\n *\n * const repo = service.forRepo('/home/user/repo');\n * const branches = await repo.branches.getBranches();\n *\n * service.dispose(); // cleans up module-level hooks\n * ```\n */\nexport class GitService implements UnifiedDisposable {\n\tprivate static _instance: GitService | undefined;\n\n\t/** `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch */\n\tstatic createSingleton(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\toptions?: { watchPolling?: WatchPollingOptions },\n\t): GitService {\n\t\tif (GitService._instance != null) {\n\t\t\tthrow new Error('GitService already exists — only one instance is allowed');\n\t\t}\n\n\t\tconst instance = new GitService(watchingProvider, options?.watchPolling);\n\t\tGitService._instance = instance;\n\t\treturn instance;\n\t}\n\n\tprivate readonly _onDidChangeProviders = new Emitter<ProvidersChangeEvent>();\n\tget onDidChangeProviders(): Event<ProvidersChangeEvent> {\n\t\treturn this._onDidChangeProviders.event;\n\t}\n\n\tprivate readonly _onDidChangeMergeTargets = new Emitter<MergeTargetsChangeEvent>();\n\t/**\n\t * Fires when a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that\n\t * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming\n\t * the branch. Once per change per repository (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.\n\t */\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._onDidChangeMergeTargets.event;\n\t}\n\n\t/** Keyed by the provider's event, so providers sharing one cache are forwarded once */\n\tprivate readonly _mergeTargetsSources = new Map<Event<MergeTargetsChangeEvent>, MergeTargetsSource>();\n\n\tprivate _etag = 0;\n\tprivate readonly _providers: RegisteredProvider[] = [];\n\tprivate readonly _serviceCache = new Map<string, RepositoryService>();\n\tprivate readonly _watchService: RepositoryWatchService | undefined;\n\tprivate readonly _summaries: WorktreeSummaryService | undefined;\n\n\tprivate constructor(watchingProvider?: FileWatchingProvider, watchPolling?: WatchPollingOptions) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tpolling: watchPolling,\n\t\t\t\tgetIgnoreFilter: (repoPath, gitDirPath) => {\n\t\t\t\t\treturn this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);\n\t\t\t\t},\n\t\t\t\tgetIgnoredDirectories: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([]),\n\t\t\t});\n\t\t\tthis._summaries = new WorktreeSummaryService({\n\t\t\t\twatchService: this._watchService,\n\t\t\t\tgetRepository: repoPath => this.forRepo(repoPath),\n\t\t\t\tonDidChangeMergeTargets: this.onDidChangeMergeTargets,\n\t\t\t\tgetCommonPath: repoPath => this.getProvider(repoPath)?.provider.getCommonPath?.(repoPath),\n\t\t\t});\n\t\t}\n\n\t\tsetGlobalRepositoryServiceResolver(repoPath => this.forRepo(repoPath));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tclearGlobalRepositoryServiceResolver();\n\t\tGitService._instance = undefined;\n\t\tthis._serviceCache.clear();\n\t\tthis._summaries?.dispose();\n\t\tthis._watchService?.dispose();\n\n\t\t// Don't dispose providers since they're owned by the caller, but do clear the array to release references\n\t\tfor (const entry of this._providers) {\n\t\t\tentry.announcements?.dispose();\n\t\t}\n\t\tthis._providers.length = 0;\n\n\t\tfor (const source of this._mergeTargetsSources.values()) {\n\t\t\tsource.subscription.dispose();\n\t\t}\n\t\tthis._mergeTargetsSources.clear();\n\n\t\tthis._onDidChangeProviders.dispose();\n\t\tthis._onDidChangeMergeTargets.dispose();\n\t}\n\n\t/** Monotonically increasing counter incremented on structural changes. */\n\tget etag(): number {\n\t\treturn this._etag;\n\t}\n\n\t/** Whether any providers are registered. */\n\tget hasProviders(): boolean {\n\t\treturn this._providers.length > 0;\n\t}\n\n\tget watchService(): RepositoryWatchService | undefined {\n\t\treturn this._watchService;\n\t}\n\n\t/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */\n\tget summaries(): WorktreeSummaryService | undefined {\n\t\treturn this._summaries;\n\t}\n\n\t/**\n\t * Registers a provider with a routing predicate.\n\t *\n\t * @returns A disposable that unregisters the provider.\n\t */\n\tregister(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable {\n\t\tconst watchService = this._watchService;\n\t\tconst entry: RegisteredProvider = {\n\t\t\tprovider: provider,\n\t\t\tcanHandle: canHandle,\n\t\t\tannouncements:\n\t\t\t\twatchService != null\n\t\t\t\t\t? provider.onDidAnnounceChanges?.(e => watchService.noteSelfWrite(e.repoPath, e.changes))\n\t\t\t\t\t: undefined,\n\t\t};\n\t\tthis._providers.push(entry);\n\t\tthis._etag++;\n\t\tthis.addMergeTargetsSource(provider);\n\n\t\tthis._onDidChangeProviders.fire({\n\t\t\tadded: [provider],\n\t\t\tremoved: [],\n\t\t\tetag: this._etag,\n\t\t});\n\n\t\t// Invalidate cached proxies since provider routing may have changed\n\t\tthis._serviceCache.clear();\n\n\t\treturn createDisposable(() => {\n\t\t\tconst idx = this._providers.indexOf(entry);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis._providers.splice(idx, 1);\n\t\t\t\tentry.announcements?.dispose();\n\t\t\t\tthis._etag++;\n\t\t\t\tthis.removeMergeTargetsSource(provider);\n\t\t\t\tthis._serviceCache.clear();\n\n\t\t\t\tthis._onDidChangeProviders.fire({\n\t\t\t\t\tadded: [],\n\t\t\t\t\tremoved: [provider],\n\t\t\t\t\tetag: this._etag,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate addMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source != null) {\n\t\t\tsource.refCount++;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._mergeTargetsSources.set(event, {\n\t\t\tsubscription: event(e => this._onDidChangeMergeTargets.fire(e)),\n\t\t\trefCount: 1,\n\t\t});\n\t}\n\n\tprivate removeMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source == null || --source.refCount > 0) return;\n\n\t\tsource.subscription.dispose();\n\t\tthis._mergeTargetsSources.delete(event);\n\t}\n\n\t/** Returns the provider that can handle the given path, or undefined if none matches. */\n\tgetProvider(repoPath: string): { provider: GitProvider; path: string } | undefined {\n\t\trepoPath = normalizePath(repoPath);\n\t\tfor (const entry of this._providers) {\n\t\t\tif (entry.canHandle(repoPath)) {\n\t\t\t\treturn { provider: entry.provider, path: repoPath };\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Returns all registered providers. */\n\t*getProviders(): Iterable<GitProvider> {\n\t\tfor (const entry of this._providers) {\n\t\t\tyield entry.provider;\n\t\t}\n\t}\n\n\t/**\n\t * Routes a repo URI/path to a provider and returns a cached\n\t * {@link RepositoryService} with repo-scoped sub-provider proxies.\n\t * Returns undefined if no provider can handle the path.\n\t */\n\tforRepo(repoUri: Uri | string): RepositoryService | undefined {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\n\t\tlet repo = this._serviceCache.get(repoPath);\n\t\tif (repo != null) return repo;\n\n\t\tconst result = this.getProvider(repoPath);\n\t\tif (result == null) return undefined;\n\n\t\trepo = createRepositoryService(result.provider, repoPath, this._watchService);\n\t\tthis._serviceCache.set(repoPath, repo);\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Evicts the cached {@link RepositoryService} for a closed or removed repository.\n\t * No-op if the path isn't cached.\n\t */\n\tcloseRepo(repoUri: Uri | string): void {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\t\tthis._serviceCache.delete(repoPath);\n\t}\n\n\t/**\n\t * Validates whether `pathOrUri` points to (or resides inside) a git repository,\n\t * and returns information about it when it does.\n\t *\n\t * Routes the path to a registered provider and delegates to its config sub-provider's\n\t * `getRepositoryInfo` implementation, then normalizes the raw result into a clean\n\t * discriminated union. For a simple yes/no check, use `(await validateRepo(path)).valid`.\n\t *\n\t * Returns `{ valid: false }` if no provider can handle the path or if the routed\n\t * provider does not implement `getRepositoryInfo`.\n\t */\n\tasync validateRepo(pathOrUri: Uri | string): Promise<ValidateRepoResult> {\n\t\tconst key = getRepositoryKey(pathOrUri);\n\t\tconst routed = this.getProvider(key);\n\t\tif (routed == null) return { valid: false };\n\n\t\tconst info = await routed.provider.config.getRepositoryInfo?.(routed.path);\n\t\tif (info == null) return { valid: false };\n\n\t\tif (Array.isArray(info)) {\n\t\t\tif (info.length === 0) return { valid: false };\n\t\t\tif (info[0] === false) return { valid: true, safe: false };\n\t\t\treturn { valid: true, safe: true, repoPath: info[1] };\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsafe: true,\n\t\t\trepoPath: info.repoPath,\n\t\t\tgitDir: info.gitDir,\n\t\t\tcommonGitDir: info.commonGitDir,\n\t\t\tsuperprojectPath: info.superprojectPath,\n\t\t};\n\t}\n}\n\nfunction createRepositoryService(\n\tprovider: GitProvider,\n\trepoPath: string,\n\twatchService?: RepositoryWatchService,\n): RepositoryService {\n\tconst proxies = new Map<string, SubProviderForRepo<unknown>>();\n\n\tfunction getProxy<T extends object>(prop: string, subProvider: T | undefined): SubProviderForRepo<T> | undefined {\n\t\tif (subProvider == null) return undefined;\n\n\t\tlet proxy = proxies.get(prop);\n\t\tif (proxy == null) {\n\t\t\tproxy = createSubProviderProxyForRepo(subProvider, repoPath);\n\t\t\tproxies.set(prop, proxy);\n\t\t}\n\t\treturn proxy as SubProviderForRepo<T>;\n\t}\n\n\treturn {\n\t\tpath: repoPath,\n\t\tprovider: provider.descriptor,\n\n\t\tget etagWorkingTree(): number | undefined {\n\t\t\treturn watchService?.getSession(repoPath)?.etagWorkingTree;\n\t\t},\n\n\t\tgetAbsoluteUri: (relativePath: string): Uri => {\n\t\t\treturn provider.getAbsoluteUri(relativePath, repoPath);\n\t\t},\n\n\t\t// Required sub-providers\n\t\tget branches() {\n\t\t\tconst proxy = getProxy('branches', provider.branches);\n\t\t\tif (proxy == null) throw new Error('GitService: branches sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget commits() {\n\t\t\tconst proxy = getProxy('commits', provider.commits);\n\t\t\tif (proxy == null) throw new Error('GitService: commits sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget config() {\n\t\t\tconst proxy = getProxy('config', provider.config);\n\t\t\tif (proxy == null) throw new Error('GitService: config sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget contributors() {\n\t\t\tconst proxy = getProxy('contributors', provider.contributors);\n\t\t\tif (proxy == null) throw new Error('GitService: contributors sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget diff() {\n\t\t\tconst proxy = getProxy('diff', provider.diff);\n\t\t\tif (proxy == null) throw new Error('GitService: diff sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget graph() {\n\t\t\tconst proxy = getProxy('graph', provider.graph);\n\t\t\tif (proxy == null) throw new Error('GitService: graph sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget refs() {\n\t\t\tconst proxy = getProxy('refs', provider.refs);\n\t\t\tif (proxy == null) throw new Error('GitService: refs sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget remotes() {\n\t\t\tconst proxy = getProxy('remotes', provider.remotes);\n\t\t\tif (proxy == null) throw new Error('GitService: remotes sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget revision() {\n\t\t\tconst proxy = getProxy('revision', provider.revision);\n\t\t\tif (proxy == null) throw new Error('GitService: revision sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget status() {\n\t\t\tconst proxy = getProxy('status', provider.status);\n\t\t\tif (proxy == null) throw new Error('GitService: status sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget tags() {\n\t\t\tconst proxy = getProxy('tags', provider.tags);\n\t\t\tif (proxy == null) throw new Error('GitService: tags sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\n\t\t// Optional sub-providers\n\t\tget blame() {\n\t\t\treturn getProxy('blame', provider.blame);\n\t\t},\n\t\tget maintenance() {\n\t\t\treturn getProxy('maintenance', provider.maintenance);\n\t\t},\n\t\tget ops() {\n\t\t\treturn getProxy('ops', provider.ops);\n\t\t},\n\t\tget patch() {\n\t\t\treturn getProxy('patch', provider.patch);\n\t\t},\n\t\tget pausedOps() {\n\t\t\treturn getProxy('pausedOps', provider.pausedOps);\n\t\t},\n\t\tget staging() {\n\t\t\treturn getProxy('staging', provider.staging);\n\t\t},\n\t\tget stash() {\n\t\t\treturn getProxy('stash', provider.stash);\n\t\t},\n\t\tget worktrees() {\n\t\t\treturn getProxy('worktrees', provider.worktrees);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
@@ -1,10 +1,12 @@
1
1
  import { PageableResult } from "../../utils/paging.js";
2
2
  import { GitBranchReference, GitReference } from "../models/reference.js";
3
- import { GitBranch, GitTrackingUpstream } from "../models/branch.js";
3
+ import { BranchMetadata, BranchMetadataGkConfigKey, GitBranch, GitTrackingUpstream } from "../models/branch.js";
4
4
  //#region ../git/src/utils/branch.utils.d.ts
5
5
  export declare function formatDetachedHeadName(sha: string): string;
6
6
  export declare function getBranchNameAndRemote(ref: GitBranchReference): [name: string, remote: string | undefined];
7
7
  export declare function getBranchTrackingWithoutRemote(ref: GitBranchReference): string | undefined;
8
+ /** Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves whole */
9
+ export declare function splitRemoteBranchName(name: string, remoteNames: Iterable<string>): [remote: string, branch: string] | undefined;
8
10
  export declare function isDetachedHead(name: string): boolean;
9
11
  export declare function isOfBranchRefType(branch: GitReference | undefined): branch is GitBranchReference;
10
12
  export declare function isRemoteBranch(refName: string): boolean;
@@ -15,6 +17,14 @@ export declare function parseRefName(refName: string): {
15
17
  };
16
18
  export declare function getLocalBranchByUpstream(remoteBranchName: string, branches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>): Promise<GitBranch | undefined>;
17
19
  export declare function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>>;
18
- export declare function parseUpstream(upstream: string, tracking: string): GitTrackingUpstream | undefined;
20
+ /** @param remote - `%(upstream:remotename)`; empty or `.` (a local upstream) is not a remote */
21
+ export declare function parseUpstream(upstream: string, tracking: string, remote?: string): GitTrackingUpstream | undefined;
22
+ /** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */
23
+ export declare function parseBranchMetadataGkConfigKey(key: string): {
24
+ branch: string;
25
+ key: BranchMetadataGkConfigKey;
26
+ } | undefined;
27
+ /** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */
28
+ export declare function setBranchMetadataValue(metadata: BranchMetadata, key: BranchMetadataGkConfigKey, value: string | undefined): void;
19
29
  //#endregion
20
30
  //# sourceMappingURL=branch.utils.d.ts.map
@@ -22,6 +22,7 @@ function getBranchNameAndRemote(ref) {
22
22
  if (index === -1) return [ref.name, void 0];
23
23
  return [ref.name.substring(index + 1), ref.name.substring(0, index)];
24
24
  }
25
+ if (ref.upstream?.remote) return [ref.name, ref.upstream.remote];
25
26
  if (ref.upstream?.name != null) {
26
27
  const index = getRemoteNameSlashIndex(ref.upstream.name);
27
28
  if (index === -1) return [ref.name, void 0];
@@ -30,7 +31,17 @@ function getBranchNameAndRemote(ref) {
30
31
  return [ref.name, void 0];
31
32
  }
32
33
  function getBranchTrackingWithoutRemote(ref) {
33
- return ref.upstream?.name.substring(getRemoteNameSlashIndex(ref.upstream.name) + 1);
34
+ if (ref.upstream == null) return void 0;
35
+ const { name, remote } = ref.upstream;
36
+ if (remote && name.startsWith(`${remote}/`)) return name.substring(remote.length + 1);
37
+ return name.substring(getRemoteNameSlashIndex(name) + 1);
38
+ }
39
+ /** Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves whole */
40
+ function splitRemoteBranchName(name, remoteNames) {
41
+ if (name.startsWith("remotes/")) name = name.substring(8);
42
+ let match;
43
+ for (const remote of remoteNames) if (name.length > remote.length + 1 && name.charCodeAt(remote.length) === 47 && name.startsWith(remote) && (match == null || remote.length > match.length)) match = remote;
44
+ return match != null ? [match, name.substring(match.length + 1)] : void 0;
34
45
  }
35
46
  function isDetachedHead(name) {
36
47
  name = name.trim();
@@ -78,9 +89,11 @@ async function getLocalBranchUpstreamNames(branches) {
78
89
  return remoteBranches;
79
90
  }
80
91
  const trackingStateRegex = /(?:\[(?:ahead ([0-9]+))?[,\s]*(?:behind ([0-9]+))?]|\[(gone)])/;
81
- function parseUpstream(upstream, tracking) {
92
+ /** @param remote - `%(upstream:remotename)`; empty or `.` (a local upstream) is not a remote */
93
+ function parseUpstream(upstream, tracking, remote) {
82
94
  if (!upstream) return void 0;
83
95
  const { name } = parseRefName(upstream);
96
+ const remoteProp = remote && remote !== "." ? { remote } : void 0;
84
97
  const match = trackingStateRegex.exec(tracking);
85
98
  if (match == null) return {
86
99
  name,
@@ -88,7 +101,8 @@ function parseUpstream(upstream, tracking) {
88
101
  state: {
89
102
  ahead: 0,
90
103
  behind: 0
91
- }
104
+ },
105
+ ...remoteProp
92
106
  };
93
107
  const ahead = match[1] == null ? 0 : Number(match[1]);
94
108
  const behind = match[2] == null ? 0 : Number(match[2]);
@@ -98,10 +112,40 @@ function parseUpstream(upstream, tracking) {
98
112
  state: {
99
113
  ahead: isNaN(ahead) ? 0 : ahead,
100
114
  behind: isNaN(behind) ? 0 : behind
101
- }
115
+ },
116
+ ...remoteProp
117
+ };
118
+ }
119
+ const branchMetadataGkConfigKeyRegex = /^branch\.(.+)\.(gk-(?:last-accessed|last-modified|agent-last-activity|disposition))$/;
120
+ /** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */
121
+ function parseBranchMetadataGkConfigKey(key) {
122
+ const match = branchMetadataGkConfigKeyRegex.exec(key);
123
+ if (match == null) return void 0;
124
+ return {
125
+ branch: match[1],
126
+ key: match[2]
102
127
  };
103
128
  }
129
+ /** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */
130
+ function setBranchMetadataValue(metadata, key, value) {
131
+ switch (key) {
132
+ case "gk-last-accessed":
133
+ if (value != null) metadata.lastAccessedAt = value;
134
+ else delete metadata.lastAccessedAt;
135
+ break;
136
+ case "gk-last-modified":
137
+ if (value != null) metadata.lastModifiedAt = value;
138
+ else delete metadata.lastModifiedAt;
139
+ break;
140
+ case "gk-agent-last-activity":
141
+ if (value != null) metadata.agentLastActivityAt = value;
142
+ else delete metadata.agentLastActivityAt;
143
+ break;
144
+ case "gk-disposition": if (value === "starred" || value === "archived") metadata.disposition = value;
145
+ else delete metadata.disposition;
146
+ }
147
+ }
104
148
  //#endregion
105
- export { formatDetachedHeadName, getBranchNameAndRemote, getBranchTrackingWithoutRemote, getLocalBranchByUpstream, getLocalBranchUpstreamNames, isDetachedHead, isOfBranchRefType, isRemoteBranch, isRemoteHEAD, parseRefName, parseUpstream };
149
+ export { formatDetachedHeadName, getBranchNameAndRemote, getBranchTrackingWithoutRemote, getLocalBranchByUpstream, getLocalBranchUpstreamNames, isDetachedHead, isOfBranchRefType, isRemoteBranch, isRemoteHEAD, parseBranchMetadataGkConfigKey, parseRefName, parseUpstream, setBranchMetadataValue, splitRemoteBranchName };
106
150
 
107
151
  //# sourceMappingURL=branch.utils.js.map