@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.
- package/CHANGELOG.md +78 -1
- package/dist/git/cache.d.ts +84 -29
- package/dist/git/cache.js +188 -54
- package/dist/git/cache.js.map +1 -1
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.js +5 -0
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/branch.d.ts +20 -2
- package/dist/git/models/branch.js +28 -6
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/reference.d.ts +3 -0
- package/dist/git/models/status.js +2 -2
- package/dist/git/models/worktree.d.ts +4 -1
- package/dist/git/models/worktree.js +4 -2
- package/dist/git/models/worktree.js.map +1 -1
- package/dist/git/parsers/diffParser.js +1 -1
- package/dist/git/providers/branches.d.ts +73 -8
- package/dist/git/providers/operations.d.ts +3 -2
- package/dist/git/providers/pausedOperations.d.ts +6 -0
- package/dist/git/providers/provider.d.ts +45 -2
- package/dist/git/providers/revision.d.ts +17 -3
- package/dist/git/providers/status.d.ts +8 -2
- package/dist/git/remotes/azure-devops.js +61 -53
- package/dist/git/remotes/azure-devops.js.map +1 -1
- package/dist/git/run.types.d.ts +12 -0
- package/dist/git/service.d.ts +8 -5
- package/dist/git/service.js +16 -8
- package/dist/git/service.js.map +1 -1
- package/dist/git/utils/branch.utils.d.ts +12 -2
- package/dist/git/utils/branch.utils.js +49 -5
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/utils/reference.utils.d.ts +1 -0
- package/dist/git/utils/reference.utils.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +13 -2
- package/dist/git/watching/changeEvent.js +17 -3
- package/dist/git/watching/changeEvent.js.map +1 -1
- package/dist/git/watching/gitDirPoller.d.ts +111 -0
- package/dist/git/watching/gitDirPoller.js +266 -0
- package/dist/git/watching/gitDirPoller.js.map +1 -0
- package/dist/git/watching/provider.d.ts +16 -0
- package/dist/git/watching/watchGroup.d.ts +40 -4
- package/dist/git/watching/watchGroup.js +143 -17
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +70 -1
- package/dist/git/watching/watchService.js +158 -13
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +44 -0
- package/dist/git/watching/watchSession.js +91 -6
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +21 -0
- package/dist/git/watching/watcherPatterns.js +67 -1
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +34 -7
- package/dist/git/watching/worktreeSummary.js +39 -12
- package/dist/git/watching/worktreeSummary.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +40 -2
- package/dist/git-cli/cliGitProvider.js +182 -18
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/commandClassifier.d.ts +15 -0
- package/dist/git-cli/exec/commandClassifier.js +48 -2
- package/dist/git-cli/exec/commandClassifier.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +20 -3
- package/dist/git-cli/exec/git.js +63 -19
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitQueue.d.ts +2 -0
- package/dist/git-cli/exec/gitQueue.js +2 -1
- package/dist/git-cli/exec/gitQueue.js.map +1 -1
- package/dist/git-cli/exec/indexLock.d.ts +39 -0
- package/dist/git-cli/exec/indexLock.js +160 -0
- package/dist/git-cli/exec/indexLock.js.map +1 -0
- package/dist/git-cli/parsers/blameParser.js +2 -2
- package/dist/git-cli/parsers/refParser.js +4 -2
- package/dist/git-cli/parsers/refParser.js.map +1 -1
- package/dist/git-cli/parsers/statusParser.d.ts +6 -0
- package/dist/git-cli/parsers/statusParser.js +33 -3
- package/dist/git-cli/parsers/statusParser.js.map +1 -1
- package/dist/git-cli/parsers/worktreeParser.js +42 -2
- package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
- package/dist/git-cli/providers/branchRemote.d.ts +14 -0
- package/dist/git-cli/providers/branchRemote.js +36 -0
- package/dist/git-cli/providers/branchRemote.js.map +1 -0
- package/dist/git-cli/providers/branches.d.ts +109 -10
- package/dist/git-cli/providers/branches.js +630 -134
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.js +2 -2
- package/dist/git-cli/providers/config.d.ts +9 -5
- package/dist/git-cli/providers/config.js +58 -25
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.js +1 -1
- package/dist/git-cli/providers/graph.js +9 -6
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +17 -0
- package/dist/git-cli/providers/operations.js +172 -111
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +8 -0
- package/dist/git-cli/providers/pausedOperations.js +59 -39
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.js +11 -8
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/remotes.js +19 -10
- package/dist/git-cli/providers/remotes.js.map +1 -1
- package/dist/git-cli/providers/revision.d.ts +6 -2
- package/dist/git-cli/providers/revision.js +38 -12
- package/dist/git-cli/providers/revision.js.map +1 -1
- package/dist/git-cli/providers/staging.js +39 -14
- package/dist/git-cli/providers/staging.js.map +1 -1
- package/dist/git-cli/providers/stash.d.ts +2 -0
- package/dist/git-cli/providers/stash.js +33 -18
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/status.d.ts +3 -1
- package/dist/git-cli/providers/status.js +40 -22
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/tags.js +6 -4
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts +1 -0
- package/dist/git-cli/providers/worktrees.js +60 -31
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts +6 -0
- package/dist/git-cli/service.js +5 -2
- package/dist/git-cli/service.js.map +1 -1
- package/dist/plus/git-github/providers/github/revision.d.ts +6 -2
- package/dist/plus/git-github/providers/github/revision.js +15 -10
- package/dist/plus/git-github/providers/github/revision.js.map +1 -1
- package/dist/plus/git-github/providers/github/status.d.ts +3 -1
- package/dist/plus/git-github/providers/github/status.js +2 -2
- package/dist/plus/git-github/providers/github/status.js.map +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +13 -2
- package/dist/plus/integrations/collectionMetadata.js +14 -2
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +1 -0
- package/dist/plus/integrations/integrationService.js +1 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +1 -0
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +41 -5
- package/dist/plus/integrations/models/gitHostIntegration.js +106 -29
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +17 -4
- package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
- package/dist/plus/integrations/providers/apiClients.js +7 -1
- package/dist/plus/integrations/providers/apiClients.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.d.ts +5 -4
- package/dist/plus/integrations/providers/azure/azure.js +36 -76
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.utils.js +20 -17
- package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +2 -2
- package/dist/plus/integrations/providers/azure/models.js +4 -4
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +21 -12
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +74 -17
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
- package/dist/plus/integrations/providers/azureDevOps.js +108 -17
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +45 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +121 -6
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/jira-server.js +18 -14
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.js +18 -14
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +1 -2
- package/dist/plus/integrations/providers/models.js +9 -2
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +7 -17
- package/dist/plus/integrations/providers/providersApi.js +85 -78
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +22 -0
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +69 -9
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js +8 -2
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +3 -3
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +2 -1
- package/dist/plus/integrations/reads/issueBatch.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +23 -14
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +2 -1
- package/dist/plus/integrations/reads/issues.js +18 -3
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequestBatch.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +12 -3
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +11 -4
- package/dist/plus/integrations/reads/warnings.js +15 -5
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +36 -2
- package/dist/plus/integrations/results.js +33 -13
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/promiseCache.d.ts +10 -0
- package/dist/utils/promiseCache.js +17 -2
- package/dist/utils/promiseCache.js.map +1 -1
- package/docs/integrations.md +52 -20
- 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
|
-
|
|
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
|
-
|
|
39
|
-
|
|
22
|
+
const prefix = segments.slice(0, gitIndex);
|
|
23
|
+
let collection;
|
|
24
|
+
let projectSegment;
|
|
25
|
+
let virtualDirectory;
|
|
40
26
|
if (legacyVsts) {
|
|
41
|
-
if (
|
|
42
|
-
return
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
39
|
+
const project = projectSegment ?? repository;
|
|
50
40
|
return {
|
|
51
|
-
collection
|
|
41
|
+
collection,
|
|
52
42
|
project,
|
|
43
|
+
repository,
|
|
53
44
|
repositoryPath: `${project}/_git/${repository}`,
|
|
54
|
-
virtualDirectory
|
|
45
|
+
virtualDirectory
|
|
55
46
|
};
|
|
56
47
|
}
|
|
57
|
-
function
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
100
|
-
|
|
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
|
-
|
|
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
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
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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"}
|
package/dist/git/run.types.d.ts
CHANGED
|
@@ -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.
|
package/dist/git/service.d.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
69
|
-
*
|
|
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 */
|
package/dist/git/service.js
CHANGED
|
@@ -33,9 +33,10 @@ import { WorktreeSummaryService } from "./watching/worktreeSummary.js";
|
|
|
33
33
|
*/
|
|
34
34
|
var GitService = class GitService {
|
|
35
35
|
static _instance;
|
|
36
|
-
|
|
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
|
|
49
|
-
*
|
|
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();
|
package/dist/git/service.js.map
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|