@gitkraken/core-gitlens 0.11.1 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +84 -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 +4 -0
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/branch.d.ts +15 -2
- package/dist/git/models/branch.js +27 -5
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/issue.d.ts +3 -1
- package/dist/git/models/issue.js.map +1 -1
- package/dist/git/models/status.js +1 -1
- 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/providers/branches.d.ts +73 -8
- package/dist/git/providers/pausedOperations.d.ts +6 -0
- package/dist/git/providers/provider.d.ts +34 -2
- 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/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 +8 -1
- package/dist/git/utils/branch.utils.js +30 -1
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/utils/issue.utils.d.ts +3 -1
- package/dist/git/utils/issue.utils.js +5 -1
- package/dist/git/utils/issue.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 +34 -2
- package/dist/git-cli/cliGitProvider.js +119 -17
- 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 +15 -3
- package/dist/git-cli/exec/git.js +38 -17
- 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 +38 -0
- package/dist/git-cli/exec/indexLock.js +150 -0
- package/dist/git-cli/exec/indexLock.js.map +1 -0
- package/dist/git-cli/parsers/worktreeParser.js +42 -2
- package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +109 -10
- package/dist/git-cli/providers/branches.js +624 -131
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +9 -5
- package/dist/git-cli/providers/config.js +60 -27
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +17 -0
- package/dist/git-cli/providers/operations.js +153 -96
- 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 +60 -40
- 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/staging.js +40 -15
- 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 +38 -20
- 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/ipc/discovery.js +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
- package/dist/plus/git-github/providers/github/status.d.ts +3 -1
- package/dist/plus/git-github/providers/github/status.js +1 -1
- 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 +109 -30
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/integration.js +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 +38 -77
- 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 +27 -10
- package/dist/plus/integrations/providers/azure/models.js +40 -20
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +23 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +84 -0
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -0
- package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
- package/dist/plus/integrations/providers/azureDevOps.js +113 -22
- 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/issueSorts.d.ts +0 -1
- package/dist/plus/integrations/providers/jira-server.js +29 -19
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.d.ts +5 -2
- package/dist/plus/integrations/providers/jira.js +63 -44
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/jiraIssueByKey.d.ts +3 -4
- package/dist/plus/integrations/providers/jiraIssueByKey.js +3 -4
- package/dist/plus/integrations/providers/jiraIssueByKey.js.map +1 -1
- package/dist/plus/integrations/providers/linearIssuesEtag.d.ts +5 -5
- package/dist/plus/integrations/providers/linearIssuesEtag.js +6 -6
- package/dist/plus/integrations/providers/linearIssuesEtag.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +12 -3
- package/dist/plus/integrations/providers/models.js +16 -9
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +21 -33
- package/dist/plus/integrations/providers/providersApi.js +159 -49
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +49 -15
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +132 -10
- 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/cursors.js +1 -1
- package/dist/plus/integrations/reads/drains.d.ts +0 -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/hierarchy.js +2 -2
- package/dist/plus/integrations/reads/hierarchy.js.map +1 -1
- package/dist/plus/integrations/reads/hierarchy.utils.d.ts +13 -0
- package/dist/plus/integrations/reads/hierarchy.utils.js +26 -1
- package/dist/plus/integrations/reads/hierarchy.utils.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 +27 -18
- 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 +19 -4
- 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 +48 -4
- package/dist/plus/integrations/results.js +33 -13
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/plus/integrations/utils/integration.utils.d.ts +6 -1
- package/dist/plus/integrations/utils/integration.utils.js +8 -1
- package/dist/plus/integrations/utils/integration.utils.js.map +1 -1
- package/dist/utils/env/node/exec.js +2 -2
- 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 +93 -31
- 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/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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFA,IAAa,aAAb,MAAa,WAAwC;CACpD,OAAe;;CAGf,OAAO,gBACN,kBACA,SACa;EACb,IAAI,WAAW,aAAa,MAC3B,MAAM,IAAI,MAAM,0DAA0D;EAG3E,MAAM,WAAW,IAAI,WAAW,kBAAkB,SAAS,YAAY;EACvE,WAAW,YAAY;EACvB,OAAO;CACR;CAEA,wBAAyC,IAAI,QAA8B;CAC3E,IAAI,uBAAoD;EACvD,OAAO,KAAK,sBAAsB;CACnC;CAEA,2BAA4C,IAAI,QAAiC;;;;;;CAMjF,IAAI,0BAA0D;EAC7D,OAAO,KAAK,yBAAyB;CACtC;;CAGA,uCAAwC,IAAI,IAAwD;CAEpG,QAAgB;CAChB,aAAoD,CAAC;CACrD,gCAAiC,IAAI,IAA+B;CACpE;CACA;CAEA,YAAoB,kBAAyC,cAAoC;EAChG,IAAI,oBAAoB,MAAM;GAC7B,KAAK,gBAAgB,IAAI,uBAAuB;IAC7B;IAClB,SAAS;IACT,kBAAkB,UAAU,eAAe;KAC1C,OAAO,KAAK,YAAY,QAAQ,CAAC,EAAE,SAAS,kBAAkB,UAAU,UAAU;IACnF;IACA,wBAAuB,aACtB,KAAK,YAAY,QAAQ,CAAC,EAAE,SAAS,wBAAwB,QAAQ,KAAK,QAAQ,QAAQ,CAAC,CAAC;GAC9F,CAAC;GACD,KAAK,aAAa,IAAI,uBAAuB;IAC5C,cAAc,KAAK;IACnB,gBAAe,aAAY,KAAK,QAAQ,QAAQ;IAChD,yBAAyB,KAAK;IAC9B,gBAAe,aAAY,KAAK,YAAY,QAAQ,CAAC,EAAE,SAAS,gBAAgB,QAAQ;GACzF,CAAC;EACF;EAEA,oCAAmC,aAAY,KAAK,QAAQ,QAAQ,CAAC;CACtE;CAEA,CAAC,OAAO,WAAiB;EACxB,KAAK,QAAQ;CACd;CAEA,UAAgB;EACf,qCAAqC;EACrC,WAAW,YAAY,KAAA;EACvB,KAAK,cAAc,MAAM;EACzB,KAAK,YAAY,QAAQ;EACzB,KAAK,eAAe,QAAQ;EAG5B,KAAK,MAAM,SAAS,KAAK,YACxB,MAAM,eAAe,QAAQ;EAE9B,KAAK,WAAW,SAAS;EAEzB,KAAK,MAAM,UAAU,KAAK,qBAAqB,OAAO,GACrD,OAAO,aAAa,QAAQ;EAE7B,KAAK,qBAAqB,MAAM;EAEhC,KAAK,sBAAsB,QAAQ;EACnC,KAAK,yBAAyB,QAAQ;CACvC;;CAGA,IAAI,OAAe;EAClB,OAAO,KAAK;CACb;;CAGA,IAAI,eAAwB;EAC3B,OAAO,KAAK,WAAW,SAAS;CACjC;CAEA,IAAI,eAAmD;EACtD,OAAO,KAAK;CACb;;CAGA,IAAI,YAAgD;EACnD,OAAO,KAAK;CACb;;;;;;CAOA,SAAS,UAAuB,WAA6D;EAC5F,MAAM,eAAe,KAAK;EAC1B,MAAM,QAA4B;GACvB;GACC;GACX,eACC,gBAAgB,OACb,SAAS,wBAAuB,MAAK,aAAa,cAAc,EAAE,UAAU,EAAE,OAAO,CAAC,IACtF,KAAA;EACL;EACA,KAAK,WAAW,KAAK,KAAK;EAC1B,KAAK;EACL,KAAK,sBAAsB,QAAQ;EAEnC,KAAK,sBAAsB,KAAK;GAC/B,OAAO,CAAC,QAAQ;GAChB,SAAS,CAAC;GACV,MAAM,KAAK;EACZ,CAAC;EAGD,KAAK,cAAc,MAAM;EAEzB,OAAO,uBAAuB;GAC7B,MAAM,MAAM,KAAK,WAAW,QAAQ,KAAK;GACzC,IAAI,QAAQ,IAAI;IACf,KAAK,WAAW,OAAO,KAAK,CAAC;IAC7B,MAAM,eAAe,QAAQ;IAC7B,KAAK;IACL,KAAK,yBAAyB,QAAQ;IACtC,KAAK,cAAc,MAAM;IAEzB,KAAK,sBAAsB,KAAK;KAC/B,OAAO,CAAC;KACR,SAAS,CAAC,QAAQ;KAClB,MAAM,KAAK;IACZ,CAAC;GACF;EACD,CAAC;CACF;CAEA,sBAA8B,UAA6B;EAC1D,MAAM,QAAQ,SAAS;EACvB,IAAI,SAAS,MAAM;EAEnB,MAAM,SAAS,KAAK,qBAAqB,IAAI,KAAK;EAClD,IAAI,UAAU,MAAM;GACnB,OAAO;GACP;EACD;EAEA,KAAK,qBAAqB,IAAI,OAAO;GACpC,cAAc,OAAM,MAAK,KAAK,yBAAyB,KAAK,CAAC,CAAC;GAC9D,UAAU;EACX,CAAC;CACF;CAEA,yBAAiC,UAA6B;EAC7D,MAAM,QAAQ,SAAS;EACvB,IAAI,SAAS,MAAM;EAEnB,MAAM,SAAS,KAAK,qBAAqB,IAAI,KAAK;EAClD,IAAI,UAAU,QAAQ,EAAE,OAAO,WAAW,GAAG;EAE7C,OAAO,aAAa,QAAQ;EAC5B,KAAK,qBAAqB,OAAO,KAAK;CACvC;;CAGA,YAAY,UAAuE;EAClF,WAAW,cAAc,QAAQ;EACjC,KAAK,MAAM,SAAS,KAAK,YACxB,IAAI,MAAM,UAAU,QAAQ,GAC3B,OAAO;GAAE,UAAU,MAAM;GAAU,MAAM;EAAS;CAIrD;;CAGA,CAAC,eAAsC;EACtC,KAAK,MAAM,SAAS,KAAK,YACxB,MAAM,MAAM;CAEd;;;;;;CAOA,QAAQ,SAAsD;EAC7D,MAAM,WAAW,iBAAiB,OAAO;EAEzC,IAAI,OAAO,KAAK,cAAc,IAAI,QAAQ;EAC1C,IAAI,QAAQ,MAAM,OAAO;EAEzB,MAAM,SAAS,KAAK,YAAY,QAAQ;EACxC,IAAI,UAAU,MAAM,OAAO,KAAA;EAE3B,OAAO,wBAAwB,OAAO,UAAU,UAAU,KAAK,aAAa;EAC5E,KAAK,cAAc,IAAI,UAAU,IAAI;EACrC,OAAO;CACR;;;;;CAMA,UAAU,SAA6B;EACtC,MAAM,WAAW,iBAAiB,OAAO;EACzC,KAAK,cAAc,OAAO,QAAQ;CACnC;;;;;;;;;;;;CAaA,MAAM,aAAa,WAAsD;EACxE,MAAM,MAAM,iBAAiB,SAAS;EACtC,MAAM,SAAS,KAAK,YAAY,GAAG;EACnC,IAAI,UAAU,MAAM,OAAO,EAAE,OAAO,MAAM;EAE1C,MAAM,OAAO,MAAM,OAAO,SAAS,OAAO,oBAAoB,OAAO,IAAI;EACzE,IAAI,QAAQ,MAAM,OAAO,EAAE,OAAO,MAAM;EAExC,IAAI,MAAM,QAAQ,IAAI,GAAG;GACxB,IAAI,KAAK,WAAW,GAAG,OAAO,EAAE,OAAO,MAAM;GAC7C,IAAI,KAAK,OAAO,OAAO,OAAO;IAAE,OAAO;IAAM,MAAM;GAAM;GACzD,OAAO;IAAE,OAAO;IAAM,MAAM;IAAM,UAAU,KAAK;GAAG;EACrD;EAEA,OAAO;GACN,OAAO;GACP,MAAM;GACN,UAAU,KAAK;GACf,QAAQ,KAAK;GACb,cAAc,KAAK;GACnB,kBAAkB,KAAK;EACxB;CACD;AACD;AAEA,SAAS,wBACR,UACA,UACA,cACoB;CACpB,MAAM,0BAAU,IAAI,IAAyC;CAE7D,SAAS,SAA2B,MAAc,aAA+D;EAChH,IAAI,eAAe,MAAM,OAAO,KAAA;EAEhC,IAAI,QAAQ,QAAQ,IAAI,IAAI;EAC5B,IAAI,SAAS,MAAM;GAClB,QAAQ,8BAA8B,aAAa,QAAQ;GAC3D,QAAQ,IAAI,MAAM,KAAK;EACxB;EACA,OAAO;CACR;CAEA,OAAO;EACN,MAAM;EACN,UAAU,SAAS;EAEnB,IAAI,kBAAsC;GACzC,OAAO,cAAc,WAAW,QAAQ,CAAC,EAAE;EAC5C;EAEA,iBAAiB,iBAA8B;GAC9C,OAAO,SAAS,eAAe,cAAc,QAAQ;EACtD;EAGA,IAAI,WAAW;GACd,MAAM,QAAQ,SAAS,YAAY,SAAS,QAAQ;GACpD,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,kEAAkE;GACrG,OAAO;EACR;EACA,IAAI,UAAU;GACb,MAAM,QAAQ,SAAS,WAAW,SAAS,OAAO;GAClD,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,iEAAiE;GACpG,OAAO;EACR;EACA,IAAI,SAAS;GACZ,MAAM,QAAQ,SAAS,UAAU,SAAS,MAAM;GAChD,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,gEAAgE;GACnG,OAAO;EACR;EACA,IAAI,eAAe;GAClB,MAAM,QAAQ,SAAS,gBAAgB,SAAS,YAAY;GAC5D,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,sEAAsE;GACzG,OAAO;EACR;EACA,IAAI,OAAO;GACV,MAAM,QAAQ,SAAS,QAAQ,SAAS,IAAI;GAC5C,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,8DAA8D;GACjG,OAAO;EACR;EACA,IAAI,QAAQ;GACX,MAAM,QAAQ,SAAS,SAAS,SAAS,KAAK;GAC9C,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,+DAA+D;GAClG,OAAO;EACR;EACA,IAAI,OAAO;GACV,MAAM,QAAQ,SAAS,QAAQ,SAAS,IAAI;GAC5C,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,8DAA8D;GACjG,OAAO;EACR;EACA,IAAI,UAAU;GACb,MAAM,QAAQ,SAAS,WAAW,SAAS,OAAO;GAClD,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,iEAAiE;GACpG,OAAO;EACR;EACA,IAAI,WAAW;GACd,MAAM,QAAQ,SAAS,YAAY,SAAS,QAAQ;GACpD,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,kEAAkE;GACrG,OAAO;EACR;EACA,IAAI,SAAS;GACZ,MAAM,QAAQ,SAAS,UAAU,SAAS,MAAM;GAChD,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,gEAAgE;GACnG,OAAO;EACR;EACA,IAAI,OAAO;GACV,MAAM,QAAQ,SAAS,QAAQ,SAAS,IAAI;GAC5C,IAAI,SAAS,MAAM,MAAM,IAAI,MAAM,8DAA8D;GACjG,OAAO;EACR;EAGA,IAAI,QAAQ;GACX,OAAO,SAAS,SAAS,SAAS,KAAK;EACxC;EACA,IAAI,cAAc;GACjB,OAAO,SAAS,eAAe,SAAS,WAAW;EACpD;EACA,IAAI,MAAM;GACT,OAAO,SAAS,OAAO,SAAS,GAAG;EACpC;EACA,IAAI,QAAQ;GACX,OAAO,SAAS,SAAS,SAAS,KAAK;EACxC;EACA,IAAI,YAAY;GACf,OAAO,SAAS,aAAa,SAAS,SAAS;EAChD;EACA,IAAI,UAAU;GACb,OAAO,SAAS,WAAW,SAAS,OAAO;EAC5C;EACA,IAAI,QAAQ;GACX,OAAO,SAAS,SAAS,SAAS,KAAK;EACxC;EACA,IAAI,YAAY;GACf,OAAO,SAAS,aAAa,SAAS,SAAS;EAChD;CACD;AACD"}
|
|
@@ -1,6 +1,6 @@
|
|
|
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];
|
|
@@ -16,5 +16,12 @@ export declare function parseRefName(refName: string): {
|
|
|
16
16
|
export declare function getLocalBranchByUpstream(remoteBranchName: string, branches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>): Promise<GitBranch | undefined>;
|
|
17
17
|
export declare function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>>;
|
|
18
18
|
export declare function parseUpstream(upstream: string, tracking: string): GitTrackingUpstream | undefined;
|
|
19
|
+
/** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */
|
|
20
|
+
export declare function parseBranchMetadataGkConfigKey(key: string): {
|
|
21
|
+
branch: string;
|
|
22
|
+
key: BranchMetadataGkConfigKey;
|
|
23
|
+
} | undefined;
|
|
24
|
+
/** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */
|
|
25
|
+
export declare function setBranchMetadataValue(metadata: BranchMetadata, key: BranchMetadataGkConfigKey, value: string | undefined): void;
|
|
19
26
|
//#endregion
|
|
20
27
|
//# sourceMappingURL=branch.utils.d.ts.map
|
|
@@ -101,7 +101,36 @@ function parseUpstream(upstream, tracking) {
|
|
|
101
101
|
}
|
|
102
102
|
};
|
|
103
103
|
}
|
|
104
|
+
const branchMetadataGkConfigKeyRegex = /^branch\.(.+)\.(gk-(?:last-accessed|last-modified|agent-last-activity|disposition))$/;
|
|
105
|
+
/** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */
|
|
106
|
+
function parseBranchMetadataGkConfigKey(key) {
|
|
107
|
+
const match = branchMetadataGkConfigKeyRegex.exec(key);
|
|
108
|
+
if (match == null) return void 0;
|
|
109
|
+
return {
|
|
110
|
+
branch: match[1],
|
|
111
|
+
key: match[2]
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */
|
|
115
|
+
function setBranchMetadataValue(metadata, key, value) {
|
|
116
|
+
switch (key) {
|
|
117
|
+
case "gk-last-accessed":
|
|
118
|
+
if (value != null) metadata.lastAccessedAt = value;
|
|
119
|
+
else delete metadata.lastAccessedAt;
|
|
120
|
+
break;
|
|
121
|
+
case "gk-last-modified":
|
|
122
|
+
if (value != null) metadata.lastModifiedAt = value;
|
|
123
|
+
else delete metadata.lastModifiedAt;
|
|
124
|
+
break;
|
|
125
|
+
case "gk-agent-last-activity":
|
|
126
|
+
if (value != null) metadata.agentLastActivityAt = value;
|
|
127
|
+
else delete metadata.agentLastActivityAt;
|
|
128
|
+
break;
|
|
129
|
+
case "gk-disposition": if (value === "starred" || value === "archived") metadata.disposition = value;
|
|
130
|
+
else delete metadata.disposition;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
104
133
|
//#endregion
|
|
105
|
-
export { formatDetachedHeadName, getBranchNameAndRemote, getBranchTrackingWithoutRemote, getLocalBranchByUpstream, getLocalBranchUpstreamNames, isDetachedHead, isOfBranchRefType, isRemoteBranch, isRemoteHEAD, parseRefName, parseUpstream };
|
|
134
|
+
export { formatDetachedHeadName, getBranchNameAndRemote, getBranchTrackingWithoutRemote, getLocalBranchByUpstream, getLocalBranchUpstreamNames, isDetachedHead, isOfBranchRefType, isRemoteBranch, isRemoteHEAD, parseBranchMetadataGkConfigKey, parseRefName, parseUpstream, setBranchMetadataValue };
|
|
106
135
|
|
|
107
136
|
//# sourceMappingURL=branch.utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"branch.utils.js","names":[],"sources":["../../../../git/src/utils/branch.utils.ts"],"sourcesContent":["import { getRemoteNameSlashIndex } from '@gitlens/utils/gitRefs.js';\nimport type { PageableResult } from '@gitlens/utils/paging.js';\nimport type { GitBranch, GitTrackingUpstream } from '../models/branch.js';\nimport type { GitBranchReference, GitReference } from '../models/reference.js';\nimport { shortenRevision } from './revision.utils.js';\n\n/**\n * The names git uses where a branch name would go when HEAD isn't on a branch — `(HEAD detached\n * at|from <rev>)`, `(no branch)`, `(no branch, rebasing <name>)`, `(no branch, bisect started on\n * <name>)`, and `git status --porcelain=v2`'s `branch.head` token `(detached)` — plus bare `HEAD`\n * and our own {@link formatDetachedHeadName} output (`(<rev>...)`).\n *\n * Deliberately NOT \"anything parenthesized\": parentheses are legal in ref names, so `(release)`,\n * `v1.0(rc)` and `feat/(wip)` are all real branches a user can be on. Matching those made\n * `GitBranch` rewrite a genuine branch's `name` to the synthesized `(sha…)` label and re-key its\n * `id` by SHA, which corrupts its identity for every downstream consumer.\n */\nconst detachedHEADRegex = /^(?:HEAD|\\(detached\\)|\\(HEAD detached (?:at|from) .+\\)|\\(no branch.*\\)|\\(.+\\.\\.\\.\\))$/;\n\nexport function formatDetachedHeadName(sha: string): string {\n\treturn `(${shortenRevision(sha)}...)`;\n}\n\nexport function getBranchNameAndRemote(ref: GitBranchReference): [name: string, remote: string | undefined] {\n\tif (ref.remote) {\n\t\tconst index = getRemoteNameSlashIndex(ref.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name.substring(index + 1), ref.name.substring(0, index)];\n\t}\n\n\tif (ref.upstream?.name != null) {\n\t\tconst index = getRemoteNameSlashIndex(ref.upstream.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name, ref.upstream.name.substring(0, index)];\n\t}\n\n\treturn [ref.name, undefined];\n}\n\nexport function getBranchTrackingWithoutRemote(ref: GitBranchReference): string | undefined {\n\treturn ref.upstream?.name.substring(getRemoteNameSlashIndex(ref.upstream.name) + 1);\n}\n\nexport function isDetachedHead(name: string): boolean {\n\t// If there is whitespace in the name assume this is not a valid branch name\n\t// Deals with detached HEAD states\n\tname = name.trim();\n\treturn name.length ? detachedHEADRegex.test(name) : true;\n}\n\nexport function isOfBranchRefType(branch: GitReference | undefined): branch is GitBranchReference {\n\treturn branch?.refType === 'branch';\n}\n\nconst localBranchesPrefixRegex = /^(refs\\/)?heads\\//i;\nconst remoteBranchesPrefixRegex = /^(refs\\/)?remotes\\//i;\nconst remoteHEADRegex = /^(refs\\/)?remotes\\/(.*\\/)HEAD$/i;\n\nexport function isRemoteBranch(refName: string): boolean {\n\treturn remoteBranchesPrefixRegex.test(refName);\n}\n\nexport function isRemoteHEAD(refName: string): boolean {\n\treturn remoteHEADRegex.test(refName);\n}\n\nexport function parseRefName(refName: string): { name: string; remote: boolean } {\n\tconst remote = isRemoteBranch(refName);\n\n\tlet name;\n\tif (remote) {\n\t\t// Strip off refs/remotes/\n\t\tname = refName.replace(remoteBranchesPrefixRegex, '');\n\t} else {\n\t\t// Strip off refs/heads/\n\t\tname = refName.replace(localBranchesPrefixRegex, '');\n\t}\n\n\treturn { name: name, remote: remote };\n}\n\nexport async function getLocalBranchByUpstream(\n\tremoteBranchName: string,\n\tbranches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>,\n): Promise<GitBranch | undefined> {\n\tlet qualifiedRemoteBranchName;\n\tif (remoteBranchName.startsWith('remotes/')) {\n\t\tqualifiedRemoteBranchName = remoteBranchName;\n\t\tremoteBranchName = remoteBranchName.substring(8);\n\t} else {\n\t\tqualifiedRemoteBranchName = `remotes/${remoteBranchName}`;\n\t}\n\n\tfunction matches(branch: GitBranch): boolean {\n\t\treturn (\n\t\t\t!branch.remote &&\n\t\t\tbranch.upstream?.name != null &&\n\t\t\t(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName!)\n\t\t);\n\t}\n\n\tconst values = branches.values();\n\tif (Symbol.asyncIterator in values) {\n\t\tfor await (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t} else {\n\t\tfor (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport async function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>> {\n\tconst remoteBranches = new Set<string>();\n\n\tfor await (const branch of branches.values()) {\n\t\tif (!branch.remote && branch.upstream?.name != null) {\n\t\t\tremoteBranches.add(branch.upstream.name);\n\t\t}\n\t}\n\n\treturn remoteBranches;\n}\n\nconst trackingStateRegex = /(?:\\[(?:ahead ([0-9]+))?[,\\s]*(?:behind ([0-9]+))?]|\\[(gone)])/;\n\nexport function parseUpstream(upstream: string, tracking: string): GitTrackingUpstream | undefined {\n\tif (!upstream) return undefined;\n\n\tconst { name } = parseRefName(upstream);\n\n\tconst match = trackingStateRegex.exec(tracking);\n\tif (match == null) {\n\t\treturn { name: name, missing: false, state: { ahead: 0, behind: 0 } };\n\t}\n\n\tconst ahead = match[1] == null ? 0 : Number(match[1]);\n\tconst behind = match[2] == null ? 0 : Number(match[2]);\n\n\treturn {\n\t\tname: name,\n\t\tmissing: match[3] != null,\n\t\tstate: { ahead: isNaN(ahead) ? 0 : ahead, behind: isNaN(behind) ? 0 : behind },\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"branch.utils.js","names":[],"sources":["../../../../git/src/utils/branch.utils.ts"],"sourcesContent":["import { getRemoteNameSlashIndex } from '@gitlens/utils/gitRefs.js';\nimport type { PageableResult } from '@gitlens/utils/paging.js';\nimport type { BranchMetadata, BranchMetadataGkConfigKey, GitBranch, GitTrackingUpstream } from '../models/branch.js';\nimport type { GitBranchReference, GitReference } from '../models/reference.js';\nimport { shortenRevision } from './revision.utils.js';\n\n/**\n * The names git uses where a branch name would go when HEAD isn't on a branch — `(HEAD detached\n * at|from <rev>)`, `(no branch)`, `(no branch, rebasing <name>)`, `(no branch, bisect started on\n * <name>)`, and `git status --porcelain=v2`'s `branch.head` token `(detached)` — plus bare `HEAD`\n * and our own {@link formatDetachedHeadName} output (`(<rev>...)`).\n *\n * Deliberately NOT \"anything parenthesized\": parentheses are legal in ref names, so `(release)`,\n * `v1.0(rc)` and `feat/(wip)` are all real branches a user can be on. Matching those made\n * `GitBranch` rewrite a genuine branch's `name` to the synthesized `(sha…)` label and re-key its\n * `id` by SHA, which corrupts its identity for every downstream consumer.\n */\nconst detachedHEADRegex = /^(?:HEAD|\\(detached\\)|\\(HEAD detached (?:at|from) .+\\)|\\(no branch.*\\)|\\(.+\\.\\.\\.\\))$/;\n\nexport function formatDetachedHeadName(sha: string): string {\n\treturn `(${shortenRevision(sha)}...)`;\n}\n\nexport function getBranchNameAndRemote(ref: GitBranchReference): [name: string, remote: string | undefined] {\n\tif (ref.remote) {\n\t\tconst index = getRemoteNameSlashIndex(ref.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name.substring(index + 1), ref.name.substring(0, index)];\n\t}\n\n\tif (ref.upstream?.name != null) {\n\t\tconst index = getRemoteNameSlashIndex(ref.upstream.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name, ref.upstream.name.substring(0, index)];\n\t}\n\n\treturn [ref.name, undefined];\n}\n\nexport function getBranchTrackingWithoutRemote(ref: GitBranchReference): string | undefined {\n\treturn ref.upstream?.name.substring(getRemoteNameSlashIndex(ref.upstream.name) + 1);\n}\n\nexport function isDetachedHead(name: string): boolean {\n\t// If there is whitespace in the name assume this is not a valid branch name\n\t// Deals with detached HEAD states\n\tname = name.trim();\n\treturn name.length ? detachedHEADRegex.test(name) : true;\n}\n\nexport function isOfBranchRefType(branch: GitReference | undefined): branch is GitBranchReference {\n\treturn branch?.refType === 'branch';\n}\n\nconst localBranchesPrefixRegex = /^(refs\\/)?heads\\//i;\nconst remoteBranchesPrefixRegex = /^(refs\\/)?remotes\\//i;\nconst remoteHEADRegex = /^(refs\\/)?remotes\\/(.*\\/)HEAD$/i;\n\nexport function isRemoteBranch(refName: string): boolean {\n\treturn remoteBranchesPrefixRegex.test(refName);\n}\n\nexport function isRemoteHEAD(refName: string): boolean {\n\treturn remoteHEADRegex.test(refName);\n}\n\nexport function parseRefName(refName: string): { name: string; remote: boolean } {\n\tconst remote = isRemoteBranch(refName);\n\n\tlet name;\n\tif (remote) {\n\t\t// Strip off refs/remotes/\n\t\tname = refName.replace(remoteBranchesPrefixRegex, '');\n\t} else {\n\t\t// Strip off refs/heads/\n\t\tname = refName.replace(localBranchesPrefixRegex, '');\n\t}\n\n\treturn { name: name, remote: remote };\n}\n\nexport async function getLocalBranchByUpstream(\n\tremoteBranchName: string,\n\tbranches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>,\n): Promise<GitBranch | undefined> {\n\tlet qualifiedRemoteBranchName;\n\tif (remoteBranchName.startsWith('remotes/')) {\n\t\tqualifiedRemoteBranchName = remoteBranchName;\n\t\tremoteBranchName = remoteBranchName.substring(8);\n\t} else {\n\t\tqualifiedRemoteBranchName = `remotes/${remoteBranchName}`;\n\t}\n\n\tfunction matches(branch: GitBranch): boolean {\n\t\treturn (\n\t\t\t!branch.remote &&\n\t\t\tbranch.upstream?.name != null &&\n\t\t\t(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName!)\n\t\t);\n\t}\n\n\tconst values = branches.values();\n\tif (Symbol.asyncIterator in values) {\n\t\tfor await (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t} else {\n\t\tfor (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport async function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>> {\n\tconst remoteBranches = new Set<string>();\n\n\tfor await (const branch of branches.values()) {\n\t\tif (!branch.remote && branch.upstream?.name != null) {\n\t\t\tremoteBranches.add(branch.upstream.name);\n\t\t}\n\t}\n\n\treturn remoteBranches;\n}\n\nconst trackingStateRegex = /(?:\\[(?:ahead ([0-9]+))?[,\\s]*(?:behind ([0-9]+))?]|\\[(gone)])/;\n\nexport function parseUpstream(upstream: string, tracking: string): GitTrackingUpstream | undefined {\n\tif (!upstream) return undefined;\n\n\tconst { name } = parseRefName(upstream);\n\n\tconst match = trackingStateRegex.exec(tracking);\n\tif (match == null) {\n\t\treturn { name: name, missing: false, state: { ahead: 0, behind: 0 } };\n\t}\n\n\tconst ahead = match[1] == null ? 0 : Number(match[1]);\n\tconst behind = match[2] == null ? 0 : Number(match[2]);\n\n\treturn {\n\t\tname: name,\n\t\tmissing: match[3] != null,\n\t\tstate: { ahead: isNaN(ahead) ? 0 : ahead, behind: isNaN(behind) ? 0 : behind },\n\t};\n}\n\nconst branchMetadataGkConfigKeyRegex =\n\t/^branch\\.(.+)\\.(gk-(?:last-accessed|last-modified|agent-last-activity|disposition))$/;\n\n/** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */\nexport function parseBranchMetadataGkConfigKey(\n\tkey: string,\n): { branch: string; key: BranchMetadataGkConfigKey } | undefined {\n\tconst match = branchMetadataGkConfigKeyRegex.exec(key);\n\tif (match == null) return undefined;\n\n\treturn { branch: match[1], key: match[2] as BranchMetadataGkConfigKey };\n}\n\n/** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */\nexport function setBranchMetadataValue(\n\tmetadata: BranchMetadata,\n\tkey: BranchMetadataGkConfigKey,\n\tvalue: string | undefined,\n): void {\n\tswitch (key) {\n\t\tcase 'gk-last-accessed':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.lastAccessedAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.lastAccessedAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-last-modified':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.lastModifiedAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.lastModifiedAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-agent-last-activity':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.agentLastActivityAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.agentLastActivityAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-disposition':\n\t\t\tif (value === 'starred' || value === 'archived') {\n\t\t\t\tmetadata.disposition = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.disposition;\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAM,oBAAoB;AAE1B,SAAgB,uBAAuB,KAAqB;CAC3D,OAAO,IAAI,gBAAgB,GAAG,EAAE;AACjC;AAEA,SAAgB,uBAAuB,KAAqE;CAC3G,IAAI,IAAI,QAAQ;EACf,MAAM,QAAQ,wBAAwB,IAAI,IAAI;EAC9C,IAAI,UAAU,IAAI,OAAO,CAAC,IAAI,MAAM,KAAA,CAAS;EAE7C,OAAO,CAAC,IAAI,KAAK,UAAU,QAAQ,CAAC,GAAG,IAAI,KAAK,UAAU,GAAG,KAAK,CAAC;CACpE;CAEA,IAAI,IAAI,UAAU,QAAQ,MAAM;EAC/B,MAAM,QAAQ,wBAAwB,IAAI,SAAS,IAAI;EACvD,IAAI,UAAU,IAAI,OAAO,CAAC,IAAI,MAAM,KAAA,CAAS;EAE7C,OAAO,CAAC,IAAI,MAAM,IAAI,SAAS,KAAK,UAAU,GAAG,KAAK,CAAC;CACxD;CAEA,OAAO,CAAC,IAAI,MAAM,KAAA,CAAS;AAC5B;AAEA,SAAgB,+BAA+B,KAA6C;CAC3F,OAAO,IAAI,UAAU,KAAK,UAAU,wBAAwB,IAAI,SAAS,IAAI,IAAI,CAAC;AACnF;AAEA,SAAgB,eAAe,MAAuB;CAGrD,OAAO,KAAK,KAAK;CACjB,OAAO,KAAK,SAAS,kBAAkB,KAAK,IAAI,IAAI;AACrD;AAEA,SAAgB,kBAAkB,QAAgE;CACjG,OAAO,QAAQ,YAAY;AAC5B;AAEA,MAAM,2BAA2B;AACjC,MAAM,4BAA4B;AAClC,MAAM,kBAAkB;AAExB,SAAgB,eAAe,SAA0B;CACxD,OAAO,0BAA0B,KAAK,OAAO;AAC9C;AAEA,SAAgB,aAAa,SAA0B;CACtD,OAAO,gBAAgB,KAAK,OAAO;AACpC;AAEA,SAAgB,aAAa,SAAoD;CAChF,MAAM,SAAS,eAAe,OAAO;CAErC,IAAI;CACJ,IAAI,QAEH,OAAO,QAAQ,QAAQ,2BAA2B,EAAE;MAGpD,OAAO,QAAQ,QAAQ,0BAA0B,EAAE;CAGpD,OAAO;EAAQ;EAAc;CAAO;AACrC;AAEA,eAAsB,yBACrB,kBACA,UACiC;CACjC,IAAI;CACJ,IAAI,iBAAiB,WAAW,UAAU,GAAG;EAC5C,4BAA4B;EAC5B,mBAAmB,iBAAiB,UAAU,CAAC;CAChD,OACC,4BAA4B,WAAW;CAGxC,SAAS,QAAQ,QAA4B;EAC5C,OACC,CAAC,OAAO,UACR,OAAO,UAAU,QAAQ,SACxB,OAAO,SAAS,SAAS,oBAAoB,OAAO,SAAS,SAAS;CAEzE;CAEA,MAAM,SAAS,SAAS,OAAO;CAC/B,IAAI,OAAO,iBAAiB,QAChB;aAAA,MAAM,UAAU,QAC1B,IAAI,QAAQ,MAAM,GAAG,OAAO;CAAA,OAG7B,KAAK,MAAM,UAAU,QACpB,IAAI,QAAQ,MAAM,GAAG,OAAO;AAK/B;AAEA,eAAsB,4BAA4B,UAA2D;CAC5G,MAAM,iCAAiB,IAAI,IAAY;CAEvC,WAAW,MAAM,UAAU,SAAS,OAAO,GAC1C,IAAI,CAAC,OAAO,UAAU,OAAO,UAAU,QAAQ,MAC9C,eAAe,IAAI,OAAO,SAAS,IAAI;CAIzC,OAAO;AACR;AAEA,MAAM,qBAAqB;AAE3B,SAAgB,cAAc,UAAkB,UAAmD;CAClG,IAAI,CAAC,UAAU,OAAO,KAAA;CAEtB,MAAM,EAAE,SAAS,aAAa,QAAQ;CAEtC,MAAM,QAAQ,mBAAmB,KAAK,QAAQ;CAC9C,IAAI,SAAS,MACZ,OAAO;EAAQ;EAAM,SAAS;EAAO,OAAO;GAAE,OAAO;GAAG,QAAQ;EAAE;CAAE;CAGrE,MAAM,QAAQ,MAAM,MAAM,OAAO,IAAI,OAAO,MAAM,EAAE;CACpD,MAAM,SAAS,MAAM,MAAM,OAAO,IAAI,OAAO,MAAM,EAAE;CAErD,OAAO;EACA;EACN,SAAS,MAAM,MAAM;EACrB,OAAO;GAAE,OAAO,MAAM,KAAK,IAAI,IAAI;GAAO,QAAQ,MAAM,MAAM,IAAI,IAAI;EAAO;CAC9E;AACD;AAEA,MAAM,iCACL;;AAGD,SAAgB,+BACf,KACiE;CACjE,MAAM,QAAQ,+BAA+B,KAAK,GAAG;CACrD,IAAI,SAAS,MAAM,OAAO,KAAA;CAE1B,OAAO;EAAE,QAAQ,MAAM;EAAI,KAAK,MAAM;CAAgC;AACvE;;AAGA,SAAgB,uBACf,UACA,KACA,OACO;CACP,QAAQ,KAAR;EACC,KAAK;GACJ,IAAI,SAAS,MACZ,SAAS,iBAAiB;QAE1B,OAAO,SAAS;GAEjB;EACD,KAAK;GACJ,IAAI,SAAS,MACZ,SAAS,iBAAiB;QAE1B,OAAO,SAAS;GAEjB;EACD,KAAK;GACJ,IAAI,SAAS,MACZ,SAAS,sBAAsB;QAE/B,OAAO,SAAS;GAEjB;EACD,KAAK,kBACJ,IAAI,UAAU,aAAa,UAAU,YACpC,SAAS,cAAc;OAEvB,OAAO,SAAS;CAGnB;AACD"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Issue, IssueRepositoryIdentityDescriptor, IssueShape, IssueSorting } from "../models/issue.js";
|
|
1
|
+
import { Issue, IssueProviderState, IssueRepositoryIdentityDescriptor, IssueShape, IssueSorting } from "../models/issue.js";
|
|
2
2
|
//#region ../git/src/utils/issue.utils.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* The order a read that ALWAYS orders is being made under: the caller's key, or the facade's default.
|
|
@@ -46,5 +46,7 @@ export declare function getRepositoryIdentityForIssue(issue: IssueShape | Issue)
|
|
|
46
46
|
* report the viewer's access as fetched when the copy no longer has it.
|
|
47
47
|
*/
|
|
48
48
|
export declare function serializeIssue(value: IssueShape): IssueShape;
|
|
49
|
+
/** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */
|
|
50
|
+
export declare function isTerminalIssueStateCategory(category: IssueProviderState['category']): boolean;
|
|
49
51
|
//#endregion
|
|
50
52
|
//# sourceMappingURL=issue.utils.d.ts.map
|
|
@@ -164,7 +164,11 @@ function serializeIssue(value) {
|
|
|
164
164
|
}))
|
|
165
165
|
};
|
|
166
166
|
}
|
|
167
|
+
/** Whether a provider's workflow category ends the issue: done or abandoned (see {@link IssueProviderState.category}). */
|
|
168
|
+
function isTerminalIssueStateCategory(category) {
|
|
169
|
+
return category === "DONE" || category === "CANCELED";
|
|
170
|
+
}
|
|
167
171
|
//#endregion
|
|
168
|
-
export { effectiveIssueSort, getIssueComparator, getRepositoryIdentityForIssue, serializeIssue };
|
|
172
|
+
export { effectiveIssueSort, getIssueComparator, getRepositoryIdentityForIssue, isTerminalIssueStateCategory, serializeIssue };
|
|
169
173
|
|
|
170
174
|
//# sourceMappingURL=issue.utils.js.map
|