@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
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region ../git-cli/src/exec/indexLock.d.ts
|
|
2
|
+
/** How long to wait before each retry of a run that found `index.lock` held; its length is the retry count. */
|
|
3
|
+
export declare const indexLockRetryDelays: readonly number[];
|
|
4
|
+
/**
|
|
5
|
+
* Whether the argv is a command that writes the index (`apply` only does with `--index`, `--cached` or `--3way`,
|
|
6
|
+
* which implies `--index`, and never with `--check`).
|
|
7
|
+
*/
|
|
8
|
+
export declare function isIndexWriteCommand(args: readonly string[]): boolean;
|
|
9
|
+
/** Whether a run failed because another process held `index.lock`. */
|
|
10
|
+
export declare function isIndexLockHeldError(ex: unknown): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the argv is `git config`. Only that verb: others that write config (`branch -m`, `remote add`) can
|
|
13
|
+
* fail after a partial change, so a blind retry isn't safe.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isConfigCommand(args: readonly string[]): boolean;
|
|
16
|
+
/** Whether a run failed because another process may hold the config file's `.lock`: the reason is `File exists` or absent. */
|
|
17
|
+
export declare function isConfigLockHeldError(ex: unknown): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails on a held
|
|
20
|
+
* `index.lock`. Any other failure, or the last lock failure, is rethrown as is.
|
|
21
|
+
*/
|
|
22
|
+
export declare function retryWhileIndexLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T>;
|
|
23
|
+
/**
|
|
24
|
+
* Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another
|
|
25
|
+
* process — git fails at once rather than waiting.
|
|
26
|
+
*/
|
|
27
|
+
export declare function retryWhileConfigLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Runs the work for each key one at a time, in arrival order. A failing run doesn't hold up the next one, and a
|
|
30
|
+
* run whose signal aborts while it waits rejects at once without ever starting.
|
|
31
|
+
*/
|
|
32
|
+
export declare class KeyedSequencer {
|
|
33
|
+
/** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */
|
|
34
|
+
private readonly _tails;
|
|
35
|
+
run<T>(key: string, signal: AbortSignal | undefined, fn: () => Promise<T>): Promise<T>;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
//# sourceMappingURL=indexLock.d.ts.map
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { leadingCommand } from "./commandClassifier.js";
|
|
2
|
+
import { CancelledRunError, RunError } from "../../utils/env/node/exec.js";
|
|
3
|
+
import { abortReason } from "./gitQueue.js";
|
|
4
|
+
import { setTimeout } from "timers/promises";
|
|
5
|
+
//#region ../git-cli/src/exec/indexLock.ts
|
|
6
|
+
/**
|
|
7
|
+
* Verbs that take `<gitdir>/index.lock` before changing anything, so a run that loses the race fails at once
|
|
8
|
+
* with nothing half-done and is safe to retry. `stash` is left out because it is multi-step and can fail part
|
|
9
|
+
* way, and the long or interactive verbs (`merge`, `rebase`, `cherry-pick`, `revert`, `am`, `pull`) because
|
|
10
|
+
* holding the lock-ordering chain for their whole run would stall every other write to the checkout.
|
|
11
|
+
*/
|
|
12
|
+
const indexWritingVerbs = /* @__PURE__ */ new Set([
|
|
13
|
+
"add",
|
|
14
|
+
"checkout",
|
|
15
|
+
"commit",
|
|
16
|
+
"mv",
|
|
17
|
+
"read-tree",
|
|
18
|
+
"reset",
|
|
19
|
+
"restore",
|
|
20
|
+
"rm",
|
|
21
|
+
"switch",
|
|
22
|
+
"update-index"
|
|
23
|
+
]);
|
|
24
|
+
const applyIndexOptions = /* @__PURE__ */ new Set([
|
|
25
|
+
"--index",
|
|
26
|
+
"--cached",
|
|
27
|
+
"--3way",
|
|
28
|
+
"-3"
|
|
29
|
+
]);
|
|
30
|
+
/** Matches git's `Unable to create '<path>/index.lock': File exists.` — the wording varies a little by version. */
|
|
31
|
+
const indexLockHeldRegex = /index\.lock': File exists/i;
|
|
32
|
+
/** How long to wait before each retry of a run that found `index.lock` held; its length is the retry count. */
|
|
33
|
+
const indexLockRetryDelays = [
|
|
34
|
+
100,
|
|
35
|
+
200,
|
|
36
|
+
400
|
|
37
|
+
];
|
|
38
|
+
/**
|
|
39
|
+
* Whether the argv is a command that writes the index (`apply` only does with `--index`, `--cached` or `--3way`,
|
|
40
|
+
* which implies `--index`, and never with `--check`).
|
|
41
|
+
*/
|
|
42
|
+
function isIndexWriteCommand(args) {
|
|
43
|
+
const { verb, rest } = leadingCommand(args);
|
|
44
|
+
if (verb === "apply") return !rest.includes("--check") && rest.some((a) => applyIndexOptions.has(a));
|
|
45
|
+
return indexWritingVerbs.has(verb);
|
|
46
|
+
}
|
|
47
|
+
/** Whether a run failed because another process held `index.lock`. */
|
|
48
|
+
function isIndexLockHeldError(ex) {
|
|
49
|
+
return ex instanceof RunError && !(ex instanceof CancelledRunError) && indexLockHeldRegex.test(ex.stderr);
|
|
50
|
+
}
|
|
51
|
+
/** Resolves after `ms`, or rejects as soon as `signal` aborts. */
|
|
52
|
+
async function delay(ms, signal) {
|
|
53
|
+
try {
|
|
54
|
+
await setTimeout(ms, void 0, { signal });
|
|
55
|
+
} catch (ex) {
|
|
56
|
+
if (signal?.aborted) throw abortReason(signal);
|
|
57
|
+
throw ex;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Captures the rest of git's `error: could not lock config file <path>[: <reason>]` line. A held lock reads
|
|
62
|
+
* `: File exists`, except `--remove-section`/`--rename-section` print no reason (for any failure); permanent
|
|
63
|
+
* failures (`Permission denied`, `No such file or directory`, ...) must not be retried.
|
|
64
|
+
*/
|
|
65
|
+
const configLockFailureRegex = /could not lock config file ([^\r\n]*)/i;
|
|
66
|
+
/**
|
|
67
|
+
* Whether the argv is `git config`. Only that verb: others that write config (`branch -m`, `remote add`) can
|
|
68
|
+
* fail after a partial change, so a blind retry isn't safe.
|
|
69
|
+
*/
|
|
70
|
+
function isConfigCommand(args) {
|
|
71
|
+
return leadingCommand(args).verb === "config";
|
|
72
|
+
}
|
|
73
|
+
/** Whether a run failed because another process may hold the config file's `.lock`: the reason is `File exists` or absent. */
|
|
74
|
+
function isConfigLockHeldError(ex) {
|
|
75
|
+
if (!(ex instanceof RunError) || ex instanceof CancelledRunError) return false;
|
|
76
|
+
const rest = configLockFailureRegex.exec(ex.stderr)?.[1];
|
|
77
|
+
if (rest == null) return false;
|
|
78
|
+
return /: File exists\s*$/i.test(rest) || !rest.includes(": ");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails with an error
|
|
82
|
+
* `isHeldError` accepts. Any other failure, or the last lock failure, is rethrown as is.
|
|
83
|
+
*/
|
|
84
|
+
async function retryWhileHeld(attempt, signal, isHeldError) {
|
|
85
|
+
for (let retry = 0;; retry++) {
|
|
86
|
+
try {
|
|
87
|
+
return await attempt();
|
|
88
|
+
} catch (ex) {
|
|
89
|
+
if (retry >= indexLockRetryDelays.length || !isHeldError(ex)) throw ex;
|
|
90
|
+
}
|
|
91
|
+
await delay(indexLockRetryDelays[retry], signal);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails on a held
|
|
96
|
+
* `index.lock`. Any other failure, or the last lock failure, is rethrown as is.
|
|
97
|
+
*/
|
|
98
|
+
function retryWhileIndexLocked(attempt, signal) {
|
|
99
|
+
return retryWhileHeld(attempt, signal, isIndexLockHeldError);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another
|
|
103
|
+
* process — git fails at once rather than waiting.
|
|
104
|
+
*/
|
|
105
|
+
function retryWhileConfigLocked(attempt, signal) {
|
|
106
|
+
return retryWhileHeld(attempt, signal, isConfigLockHeldError);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Runs the work for each key one at a time, in arrival order. A failing run doesn't hold up the next one, and a
|
|
110
|
+
* run whose signal aborts while it waits rejects at once without ever starting.
|
|
111
|
+
*/
|
|
112
|
+
var KeyedSequencer = class {
|
|
113
|
+
/** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */
|
|
114
|
+
_tails = /* @__PURE__ */ new Map();
|
|
115
|
+
async run(key, signal, fn) {
|
|
116
|
+
const previous = this._tails.get(key);
|
|
117
|
+
let release;
|
|
118
|
+
const tail = new Promise((resolve) => release = resolve);
|
|
119
|
+
this._tails.set(key, tail);
|
|
120
|
+
const finish = () => {
|
|
121
|
+
release();
|
|
122
|
+
if (this._tails.get(key) === tail) this._tails.delete(key);
|
|
123
|
+
};
|
|
124
|
+
let started = false;
|
|
125
|
+
try {
|
|
126
|
+
if (previous != null) await waitFor(previous, signal);
|
|
127
|
+
started = true;
|
|
128
|
+
return await fn();
|
|
129
|
+
} finally {
|
|
130
|
+
if (started || previous == null) finish();
|
|
131
|
+
else previous.then(finish);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
function waitFor(promise, signal) {
|
|
136
|
+
if (signal == null) return promise;
|
|
137
|
+
if (signal.aborted) return Promise.reject(abortReason(signal));
|
|
138
|
+
return new Promise((resolve, reject) => {
|
|
139
|
+
const onAbort = () => reject(abortReason(signal));
|
|
140
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
141
|
+
promise.then(() => {
|
|
142
|
+
signal.removeEventListener("abort", onAbort);
|
|
143
|
+
resolve();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
export { KeyedSequencer, indexLockRetryDelays, isConfigCommand, isConfigLockHeldError, isIndexLockHeldError, isIndexWriteCommand, retryWhileConfigLocked, retryWhileIndexLocked };
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=indexLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexLock.js","names":["wait"],"sources":["../../../../git-cli/src/exec/indexLock.ts"],"sourcesContent":["import { setTimeout as wait } from 'timers/promises';\nimport { leadingCommand } from './commandClassifier.js';\nimport { CancelledRunError, RunError } from './exec.errors.js';\nimport { abortReason } from './gitQueue.js';\n\n/**\n * Verbs that take `<gitdir>/index.lock` before changing anything, so a run that loses the race fails at once\n * with nothing half-done and is safe to retry. `stash` is left out because it is multi-step and can fail part\n * way, and the long or interactive verbs (`merge`, `rebase`, `cherry-pick`, `revert`, `am`, `pull`) because\n * holding the lock-ordering chain for their whole run would stall every other write to the checkout.\n */\nconst indexWritingVerbs: ReadonlySet<string> = new Set([\n\t'add',\n\t'checkout',\n\t'commit',\n\t'mv',\n\t'read-tree',\n\t'reset',\n\t'restore',\n\t'rm',\n\t'switch',\n\t'update-index',\n]);\n\nconst applyIndexOptions: ReadonlySet<string> = new Set(['--index', '--cached', '--3way', '-3']);\n\n/** Matches git's `Unable to create '<path>/index.lock': File exists.` — the wording varies a little by version. */\nconst indexLockHeldRegex = /index\\.lock': File exists/i;\n\n/** How long to wait before each retry of a run that found `index.lock` held; its length is the retry count. */\nexport const indexLockRetryDelays: readonly number[] = [100, 200, 400];\n\n/**\n * Whether the argv is a command that writes the index (`apply` only does with `--index`, `--cached` or `--3way`,\n * which implies `--index`, and never with `--check`).\n */\nexport function isIndexWriteCommand(args: readonly string[]): boolean {\n\tconst { verb, rest } = leadingCommand(args);\n\tif (verb === 'apply') return !rest.includes('--check') && rest.some(a => applyIndexOptions.has(a));\n\n\treturn indexWritingVerbs.has(verb);\n}\n\n/** Whether a run failed because another process held `index.lock`. */\nexport function isIndexLockHeldError(ex: unknown): boolean {\n\treturn ex instanceof RunError && !(ex instanceof CancelledRunError) && indexLockHeldRegex.test(ex.stderr);\n}\n\n/** Resolves after `ms`, or rejects as soon as `signal` aborts. */\nasync function delay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n\ttry {\n\t\tawait wait(ms, undefined, { signal: signal });\n\t} catch (ex) {\n\t\tif (signal?.aborted) throw abortReason(signal);\n\n\t\tthrow ex;\n\t}\n}\n\n/**\n * Captures the rest of git's `error: could not lock config file <path>[: <reason>]` line. A held lock reads\n * `: File exists`, except `--remove-section`/`--rename-section` print no reason (for any failure); permanent\n * failures (`Permission denied`, `No such file or directory`, ...) must not be retried.\n */\nconst configLockFailureRegex = /could not lock config file ([^\\r\\n]*)/i;\n\n/**\n * Whether the argv is `git config`. Only that verb: others that write config (`branch -m`, `remote add`) can\n * fail after a partial change, so a blind retry isn't safe.\n */\nexport function isConfigCommand(args: readonly string[]): boolean {\n\treturn leadingCommand(args).verb === 'config';\n}\n\n/** Whether a run failed because another process may hold the config file's `.lock`: the reason is `File exists` or absent. */\nexport function isConfigLockHeldError(ex: unknown): boolean {\n\tif (!(ex instanceof RunError) || ex instanceof CancelledRunError) return false;\n\n\tconst rest = configLockFailureRegex.exec(ex.stderr)?.[1];\n\tif (rest == null) return false;\n\n\treturn /: File exists\\s*$/i.test(rest) || !rest.includes(': ');\n}\n\n/**\n * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails with an error\n * `isHeldError` accepts. Any other failure, or the last lock failure, is rethrown as is.\n */\nasync function retryWhileHeld<T>(\n\tattempt: () => Promise<T>,\n\tsignal: AbortSignal | undefined,\n\tisHeldError: (ex: unknown) => boolean,\n): Promise<T> {\n\tfor (let retry = 0; ; retry++) {\n\t\ttry {\n\t\t\treturn await attempt();\n\t\t} catch (ex) {\n\t\t\tif (retry >= indexLockRetryDelays.length || !isHeldError(ex)) throw ex;\n\t\t}\n\n\t\tawait delay(indexLockRetryDelays[retry], signal);\n\t}\n}\n\n/**\n * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails on a held\n * `index.lock`. Any other failure, or the last lock failure, is rethrown as is.\n */\nexport function retryWhileIndexLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isIndexLockHeldError);\n}\n\n/**\n * Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another\n * process — git fails at once rather than waiting.\n */\nexport function retryWhileConfigLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isConfigLockHeldError);\n}\n\n/**\n * Runs the work for each key one at a time, in arrival order. A failing run doesn't hold up the next one, and a\n * run whose signal aborts while it waits rejects at once without ever starting.\n */\nexport class KeyedSequencer {\n\t/** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */\n\tprivate readonly _tails = new Map<string, Promise<void>>();\n\n\tasync run<T>(key: string, signal: AbortSignal | undefined, fn: () => Promise<T>): Promise<T> {\n\t\tconst previous = this._tails.get(key);\n\n\t\tlet release!: () => void;\n\t\tconst tail = new Promise<void>(resolve => (release = resolve));\n\t\tthis._tails.set(key, tail);\n\n\t\tconst finish = () => {\n\t\t\trelease();\n\t\t\tif (this._tails.get(key) === tail) {\n\t\t\t\tthis._tails.delete(key);\n\t\t\t}\n\t\t};\n\n\t\tlet started = false;\n\t\ttry {\n\t\t\tif (previous != null) {\n\t\t\t\tawait waitFor(previous, signal);\n\t\t\t}\n\n\t\t\tstarted = true;\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tif (started || previous == null) {\n\t\t\t\tfinish();\n\t\t\t} else {\n\t\t\t\t// Dropped while waiting: whoever is queued behind still has to wait for the run ahead of us,\n\t\t\t\t// so the chain can only be released once that run is done.\n\t\t\t\tvoid previous.then(finish);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction waitFor(promise: Promise<void>, signal: AbortSignal | undefined): Promise<void> {\n\tif (signal == null) return promise;\n\tif (signal.aborted) return Promise.reject(abortReason(signal));\n\n\treturn new Promise<void>((resolve, reject) => {\n\t\tconst onAbort = () => reject(abortReason(signal));\n\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\t\tvoid promise.then(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\tresolve();\n\t\t});\n\t});\n}\n"],"mappings":"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"}
|
|
@@ -59,9 +59,9 @@ function parseGitWorktrees(data, repoPath, branches, getWorkspaceFolder) {
|
|
|
59
59
|
entry.detached = true;
|
|
60
60
|
break;
|
|
61
61
|
case "locked":
|
|
62
|
-
entry.locked = value.trim() || true;
|
|
62
|
+
entry.locked = unquoteCString(value.trim()) || true;
|
|
63
63
|
break;
|
|
64
|
-
case "prunable": entry.prunable = value.trim() || true;
|
|
64
|
+
case "prunable": entry.prunable = unquoteCString(value.trim()) || true;
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
sw?.stop({ suffix: ` parsed ${worktrees.length} worktrees` });
|
|
@@ -72,6 +72,46 @@ function parseGitWorktrees(data, repoPath, branches, getWorkspaceFolder) {
|
|
|
72
72
|
_usingCtx$1.d();
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
+
const cEscapes = {
|
|
76
|
+
a: 7,
|
|
77
|
+
b: 8,
|
|
78
|
+
f: 12,
|
|
79
|
+
n: 10,
|
|
80
|
+
r: 13,
|
|
81
|
+
t: 9,
|
|
82
|
+
v: 11,
|
|
83
|
+
"\\": 92,
|
|
84
|
+
"\"": 34
|
|
85
|
+
};
|
|
86
|
+
/** Reverses Git's C-style quoting (`"a\nb"`, `\303\251` octal UTF-8 bytes); returns unquoted values as-is */
|
|
87
|
+
function unquoteCString(value) {
|
|
88
|
+
if (!value.startsWith("\"")) return value;
|
|
89
|
+
const end = value.length > 1 && value.endsWith("\"") ? value.length - 1 : value.length;
|
|
90
|
+
const encoder = new TextEncoder();
|
|
91
|
+
const bytes = [];
|
|
92
|
+
let i = 1;
|
|
93
|
+
while (i < end) {
|
|
94
|
+
if (value[i] !== "\\" || i + 1 >= end) {
|
|
95
|
+
const cp = String.fromCodePoint(value.codePointAt(i));
|
|
96
|
+
bytes.push(...encoder.encode(cp));
|
|
97
|
+
i += cp.length;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const next = value[i + 1];
|
|
101
|
+
if (next >= "0" && next <= "7") {
|
|
102
|
+
let j = i + 1;
|
|
103
|
+
while (j < i + 4 && j < end && value[j] >= "0" && value[j] <= "7") j++;
|
|
104
|
+
bytes.push(Number.parseInt(value.substring(i + 1, j), 8) & 255);
|
|
105
|
+
i = j;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const code = cEscapes[next];
|
|
109
|
+
if (code != null) bytes.push(code);
|
|
110
|
+
else bytes.push(...encoder.encode(next));
|
|
111
|
+
i += 2;
|
|
112
|
+
}
|
|
113
|
+
return new TextDecoder().decode(new Uint8Array(bytes));
|
|
114
|
+
}
|
|
75
115
|
//#endregion
|
|
76
116
|
export { parseGitWorktrees };
|
|
77
117
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeParser.js","names":[],"sources":["../../../../git-cli/src/parsers/worktreeParser.ts"],"sourcesContent":["import type { GitBranch } from '@gitlens/git/models/branch.js';\nimport type { WorkspaceFolderResolver } from '@gitlens/git/models/worktree.js';\nimport { GitWorktree } from '@gitlens/git/models/worktree.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\n\ninterface WorktreeEntry {\n\tpath: string;\n\tsha?: string;\n\tbranch?: string;\n\tbare: boolean;\n\tdetached: boolean;\n\tlocked?: boolean | string;\n\tprunable?: boolean | string;\n}\n\nexport function parseGitWorktrees(\n\tdata: string | undefined,\n\trepoPath: string,\n\tbranches: GitBranch[],\n\tgetWorkspaceFolder?: WorkspaceFolderResolver,\n): GitWorktree[] {\n\tusing sw = maybeStopWatch(`Git.parseWorktrees(${repoPath})`, { log: { onlyExit: true, level: 'debug' } });\n\n\tconst worktrees: GitWorktree[] = [];\n\tif (!data) {\n\t\tsw?.stop({ suffix: ` no data` });\n\t\treturn worktrees;\n\t}\n\n\tif (repoPath != null) {\n\t\trepoPath = normalizePath(repoPath);\n\t}\n\n\tlet entry: Partial<WorktreeEntry> | undefined = undefined;\n\tlet line: string;\n\tlet index: number;\n\tlet key: string;\n\tlet value: string;\n\tlet main = true; // the first worktree is the main worktree\n\n\tfor (line of iterateByDelimiter(data, '\\n')) {\n\t\tindex = line.indexOf(' ');\n\t\tif (index === -1) {\n\t\t\tkey = line;\n\t\t\tvalue = '';\n\t\t} else {\n\t\t\tkey = line.substring(0, index);\n\t\t\tvalue = line.substring(index + 1);\n\t\t}\n\n\t\tif (!key && entry != null) {\n\t\t\t// oxlint-disable-next-line no-loop-func\n\t\t\tconst branch = entry.branch ? branches?.find(b => b.name === entry!.branch) : undefined;\n\t\t\tconst uri = fileUri(entry.path!);\n\n\t\t\tworktrees.push(\n\t\t\t\tnew GitWorktree(\n\t\t\t\t\tmain,\n\t\t\t\t\tentry.bare ? 'bare' : entry.detached ? 'detached' : 'branch',\n\t\t\t\t\trepoPath,\n\t\t\t\t\turi,\n\t\t\t\t\tentry.locked ?? false,\n\t\t\t\t\tentry.prunable ?? false,\n\t\t\t\t\tentry.sha,\n\t\t\t\t\tbranch,\n\t\t\t\t\tgetWorkspaceFolder?.(uri),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tentry = undefined;\n\t\t\tmain = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tentry ??= {};\n\n\t\tswitch (key) {\n\t\t\tcase 'worktree':\n\t\t\t\tentry.path = value;\n\t\t\t\tbreak;\n\t\t\tcase 'bare':\n\t\t\t\tentry.bare = true;\n\t\t\t\tbreak;\n\t\t\tcase 'HEAD':\n\t\t\t\tentry.sha = value;\n\t\t\t\tbreak;\n\t\t\tcase 'branch':\n\t\t\t\t// Strip off refs/heads/\n\t\t\t\tentry.branch = value.substring(11);\n\t\t\t\tbreak;\n\t\t\tcase 'detached':\n\t\t\t\tentry.detached = true;\n\t\t\t\tbreak;\n\t\t\t// `value` is the whole reason, which is only present when the locker/Git provides one
|
|
1
|
+
{"version":3,"file":"worktreeParser.js","names":[],"sources":["../../../../git-cli/src/parsers/worktreeParser.ts"],"sourcesContent":["import type { GitBranch } from '@gitlens/git/models/branch.js';\nimport type { WorkspaceFolderResolver } from '@gitlens/git/models/worktree.js';\nimport { GitWorktree } from '@gitlens/git/models/worktree.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\n\ninterface WorktreeEntry {\n\tpath: string;\n\tsha?: string;\n\tbranch?: string;\n\tbare: boolean;\n\tdetached: boolean;\n\tlocked?: boolean | string;\n\tprunable?: boolean | string;\n}\n\nexport function parseGitWorktrees(\n\tdata: string | undefined,\n\trepoPath: string,\n\tbranches: GitBranch[],\n\tgetWorkspaceFolder?: WorkspaceFolderResolver,\n): GitWorktree[] {\n\tusing sw = maybeStopWatch(`Git.parseWorktrees(${repoPath})`, { log: { onlyExit: true, level: 'debug' } });\n\n\tconst worktrees: GitWorktree[] = [];\n\tif (!data) {\n\t\tsw?.stop({ suffix: ` no data` });\n\t\treturn worktrees;\n\t}\n\n\tif (repoPath != null) {\n\t\trepoPath = normalizePath(repoPath);\n\t}\n\n\tlet entry: Partial<WorktreeEntry> | undefined = undefined;\n\tlet line: string;\n\tlet index: number;\n\tlet key: string;\n\tlet value: string;\n\tlet main = true; // the first worktree is the main worktree\n\n\tfor (line of iterateByDelimiter(data, '\\n')) {\n\t\tindex = line.indexOf(' ');\n\t\tif (index === -1) {\n\t\t\tkey = line;\n\t\t\tvalue = '';\n\t\t} else {\n\t\t\tkey = line.substring(0, index);\n\t\t\tvalue = line.substring(index + 1);\n\t\t}\n\n\t\tif (!key && entry != null) {\n\t\t\t// oxlint-disable-next-line no-loop-func\n\t\t\tconst branch = entry.branch ? branches?.find(b => b.name === entry!.branch) : undefined;\n\t\t\tconst uri = fileUri(entry.path!);\n\n\t\t\tworktrees.push(\n\t\t\t\tnew GitWorktree(\n\t\t\t\t\tmain,\n\t\t\t\t\tentry.bare ? 'bare' : entry.detached ? 'detached' : 'branch',\n\t\t\t\t\trepoPath,\n\t\t\t\t\turi,\n\t\t\t\t\tentry.locked ?? false,\n\t\t\t\t\tentry.prunable ?? false,\n\t\t\t\t\tentry.sha,\n\t\t\t\t\tbranch,\n\t\t\t\t\tgetWorkspaceFolder?.(uri),\n\t\t\t\t),\n\t\t\t);\n\n\t\t\tentry = undefined;\n\t\t\tmain = false;\n\t\t\tcontinue;\n\t\t}\n\n\t\tentry ??= {};\n\n\t\tswitch (key) {\n\t\t\tcase 'worktree':\n\t\t\t\tentry.path = value;\n\t\t\t\tbreak;\n\t\t\tcase 'bare':\n\t\t\t\tentry.bare = true;\n\t\t\t\tbreak;\n\t\t\tcase 'HEAD':\n\t\t\t\tentry.sha = value;\n\t\t\t\tbreak;\n\t\t\tcase 'branch':\n\t\t\t\t// Strip off refs/heads/\n\t\t\t\tentry.branch = value.substring(11);\n\t\t\t\tbreak;\n\t\t\tcase 'detached':\n\t\t\t\tentry.detached = true;\n\t\t\t\tbreak;\n\t\t\t// `value` is the whole reason, which is only present when the locker/Git provides one (Git < 2.31\n\t\t\t// prints a bare `locked` regardless). Without `-z`, a reason with unusual characters (e.g. newlines)\n\t\t\t// comes through C-quoted, with UTF-8 bytes as octal escapes unless `core.quotePath` is off (as core runs it)\n\t\t\tcase 'locked':\n\t\t\t\tentry.locked = unquoteCString(value.trim()) || true;\n\t\t\t\tbreak;\n\t\t\tcase 'prunable':\n\t\t\t\tentry.prunable = unquoteCString(value.trim()) || true;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tsw?.stop({ suffix: ` parsed ${worktrees.length} worktrees` });\n\n\treturn worktrees;\n}\n\nconst cEscapes: Record<string, number> = { a: 7, b: 8, f: 12, n: 10, r: 13, t: 9, v: 11, '\\\\': 92, '\"': 34 };\n\n/** Reverses Git's C-style quoting (`\"a\\nb\"`, `\\303\\251` octal UTF-8 bytes); returns unquoted values as-is */\nfunction unquoteCString(value: string): string {\n\tif (!value.startsWith('\"')) return value;\n\n\tconst end = value.length > 1 && value.endsWith('\"') ? value.length - 1 : value.length;\n\tconst encoder = new TextEncoder();\n\tconst bytes: number[] = [];\n\n\tlet i = 1;\n\twhile (i < end) {\n\t\tconst ch = value[i];\n\t\tif (ch !== '\\\\' || i + 1 >= end) {\n\t\t\t// A whole code point, so surrogate pairs encode correctly\n\t\t\tconst cp = String.fromCodePoint(value.codePointAt(i)!);\n\t\t\tbytes.push(...encoder.encode(cp));\n\t\t\ti += cp.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst next = value[i + 1];\n\t\tif (next >= '0' && next <= '7') {\n\t\t\tlet j = i + 1;\n\t\t\twhile (j < i + 4 && j < end && value[j] >= '0' && value[j] <= '7') {\n\t\t\t\tj++;\n\t\t\t}\n\t\t\tbytes.push(Number.parseInt(value.substring(i + 1, j), 8) & 0xff);\n\t\t\ti = j;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst code = cEscapes[next];\n\t\tif (code != null) {\n\t\t\tbytes.push(code);\n\t\t} else {\n\t\t\tbytes.push(...encoder.encode(next));\n\t\t}\n\t\ti += 2;\n\t}\n\n\treturn new TextDecoder().decode(new Uint8Array(bytes));\n}\n"],"mappings":"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"}
|
|
@@ -15,6 +15,23 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
15
15
|
private readonly git;
|
|
16
16
|
private readonly cache;
|
|
17
17
|
private readonly provider;
|
|
18
|
+
/** Ahead/behind counts by `<baseSha>...<tipSha>` range, from the per-tip fallback; a pair of commits always counts the same */
|
|
19
|
+
private readonly aheadBehindBySha;
|
|
20
|
+
/**
|
|
21
|
+
* When each `commonPath\0branch\0key` was last stamped by this process. Bounded: a dropped entry only costs a
|
|
22
|
+
* read of the stored stamp, which still holds off a write under 5 minutes old
|
|
23
|
+
*/
|
|
24
|
+
private readonly _branchActivityStamps;
|
|
25
|
+
/** One call per `commonPath\0branch` at a time, so a call held off by another's write in flight settles after it */
|
|
26
|
+
private readonly _branchActivityCalls;
|
|
27
|
+
/**
|
|
28
|
+
* What each `commonPath\0branch`'s reflog says it was created from, as git wrote it; `undefined` when it says
|
|
29
|
+
* nothing. A branch's creation never changes, so the base read, which reruns on every commit and ref move, reads
|
|
30
|
+
* it once
|
|
31
|
+
*/
|
|
32
|
+
private readonly _branchCreatedFrom;
|
|
33
|
+
/** Bumped each time a key's created-from is forgotten, so a reflog read in flight across that doesn't store it */
|
|
34
|
+
private readonly _branchCreatedFromGenerations;
|
|
18
35
|
constructor(context: GitServiceContext, git: Git, cache: Cache, provider: CliGitProviderInternal);
|
|
19
36
|
getBranch(repoPath: string, name?: string, options?: {
|
|
20
37
|
force?: boolean;
|
|
@@ -55,6 +72,12 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
55
72
|
priority?: GitCommandPriority;
|
|
56
73
|
local?: boolean;
|
|
57
74
|
}, cancellation?: AbortSignal): Promise<string | undefined>;
|
|
75
|
+
/**
|
|
76
|
+
* Writes the missing `refs/remotes/<remote>/HEAD` symref from the remote (`remote set-head -a`) in the background,
|
|
77
|
+
* never prompting for credentials; nothing runs past the cached local check when the symref exists. Rejects when
|
|
78
|
+
* the remote can't be read.
|
|
79
|
+
*/
|
|
80
|
+
setRemoteHeadIfMissing(repoPath: string, remote: string): Promise<void>;
|
|
58
81
|
createBranch(repoPath: string, name: string, ref: string, options?: {
|
|
59
82
|
noTracking?: boolean;
|
|
60
83
|
}): Promise<void>;
|
|
@@ -99,16 +122,65 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
99
122
|
* caches the former forever and must not cache the latter.
|
|
100
123
|
*/
|
|
101
124
|
private getBaseBranchFromReflog;
|
|
102
|
-
|
|
125
|
+
/**
|
|
126
|
+
* The start point `ref`'s own reflog says it was created from (`branch: Created from <name>`), as git wrote it:
|
|
127
|
+
* a branch, `HEAD`, a sha, a tag or any other revision. `undefined` unless exactly one entry says so. Rejects
|
|
128
|
+
* when the reflog can't be read, including for a ref that doesn't exist.
|
|
129
|
+
*/
|
|
130
|
+
private getReflogCreatedFrom;
|
|
131
|
+
/**
|
|
132
|
+
* The branch `branch` was created from, as a base for {@link getAheadOfBaseInOrder}: the name its own reflog
|
|
133
|
+
* gives, when that is a local or remote-tracking branch in the cached ref list and not `branch` itself or its
|
|
134
|
+
* copy on a remote (`git switch <branch>` creates it from `<remote>/<branch>`). Never the checkout history in
|
|
135
|
+
* HEAD's reflog, which on a long-lived checkout names whatever was checked out last.
|
|
136
|
+
*/
|
|
137
|
+
private getCreatedFromTarget;
|
|
138
|
+
getAheadOfBase(repoPath: string, branch: string | undefined, options?: {
|
|
139
|
+
skipUserTarget?: boolean;
|
|
140
|
+
preferredTarget?: string;
|
|
141
|
+
}, cancellation?: AbortSignal): Promise<AheadOfBase>;
|
|
142
|
+
/**
|
|
143
|
+
* Resolves the base from the recorded candidates, then the branch it was created from or the default branch, for
|
|
144
|
+
* {@link getAheadOfBase}
|
|
145
|
+
*/
|
|
146
|
+
private getAheadOfBaseInOrder;
|
|
147
|
+
/**
|
|
148
|
+
* The base for a branch created from `createdFrom`: that branch, unless the default branch forked strictly nearer
|
|
149
|
+
* HEAD, as it does once `createdFrom` merged into it and the branch was rebased onto it. A stacked branch's
|
|
150
|
+
* unmerged parent forked nearer, and stays its base. `undefined` when neither is usable.
|
|
151
|
+
*/
|
|
152
|
+
private getCreatedFromForkTarget;
|
|
153
|
+
/** Counts HEAD's commits past `target` for {@link getAheadOfBase} */
|
|
154
|
+
private measureAheadOfBase;
|
|
155
|
+
/** Counts the default branch's own commits past its upstream (see {@link getSelfUpstreamTarget}) for {@link getAheadOfBase} */
|
|
156
|
+
private measureAheadOfSelfUpstream;
|
|
157
|
+
/**
|
|
158
|
+
* HEAD's count against `ref` from their merge base: `left` only `ref` has, `right` only HEAD has. `undefined`
|
|
159
|
+
* before HEAD's first commit, when there is nothing to count; any other failure rejects with git's error.
|
|
160
|
+
*/
|
|
161
|
+
private countAgainstBase;
|
|
162
|
+
getAheadBehindCounts(repoPath: string, base: string, options?: {
|
|
163
|
+
remotes?: boolean;
|
|
164
|
+
refs?: readonly string[];
|
|
165
|
+
}, cancellation?: AbortSignal): Promise<Map<string, {
|
|
166
|
+
ahead: number;
|
|
167
|
+
behind: number;
|
|
168
|
+
}>>;
|
|
169
|
+
getMergedBranches(repoPath: string, into: string, options?: {
|
|
170
|
+
remotes?: boolean;
|
|
171
|
+
}, cancellation?: AbortSignal): Promise<Set<string>>;
|
|
103
172
|
/** Counts `range` with a fresh `rev-list`, bypassing the cached count. Rejects on any failure, with git's error */
|
|
104
173
|
private getLeftRightCountUncached;
|
|
105
174
|
/** Whether HEAD names a branch with no commit yet. Rejects when that can't be read, so a failure isn't taken for an empty branch */
|
|
106
175
|
private isUnbornHead;
|
|
107
176
|
/**
|
|
108
|
-
* Whether `ref` names `branch` itself: the same name, or `<remote>/<branch>` for one of the repo's actual remotes
|
|
109
|
-
* Splitting at the first slash instead would call a local `release/main` the same branch
|
|
177
|
+
* Whether `ref` names `branch` itself: the same name, or `<remote>/<branch>` for one of the repo's actual remotes,
|
|
178
|
+
* short or as a full ref. Splitting at the first slash instead would call a local `release/main` the same branch
|
|
179
|
+
* as `main`.
|
|
110
180
|
*/
|
|
111
181
|
private isSelfRef;
|
|
182
|
+
/** The repo's remote names from the cached remote list; empty when it can't be read */
|
|
183
|
+
private getRemoteNames;
|
|
112
184
|
/** Whether `ref` names `branch` itself (see {@link isSelfRef}) and sits at HEAD's commit; tips are read only after the name matched */
|
|
113
185
|
private isSelfAtHead;
|
|
114
186
|
/**
|
|
@@ -118,16 +190,41 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
118
190
|
*/
|
|
119
191
|
private getRecordedBaseCandidates;
|
|
120
192
|
/**
|
|
121
|
-
* The repo's default branch from local refs only: the `
|
|
122
|
-
* `
|
|
193
|
+
* The repo's default branch from local refs only, never the network: the `HEAD` symref of the `upstream` remote,
|
|
194
|
+
* else of `origin`, else of the first remote, as its local branch or else as the remote ref; then, in the same
|
|
195
|
+
* remote order, `<remote>/main` or `<remote>/master`, again as the local branch of that name or else the remote
|
|
196
|
+
* ref; then `init.defaultBranch`, `main`, `master` — each only if it names a commit.
|
|
123
197
|
*/
|
|
124
198
|
private getLocalDefaultBranchName;
|
|
199
|
+
/**
|
|
200
|
+
* `upstream/<name>` for a created-from or default base `<name>` (or `<remote>/<name>`), as another ref that base
|
|
201
|
+
* is measured against: in a fork whose `main` tracks `origin/main`, a branch taken off (or since rebased onto) a
|
|
202
|
+
* newer `upstream/main` would otherwise count the commits `origin` hasn't caught up on. Read from the cached
|
|
203
|
+
* remote and ref lists; `undefined` when there is no `upstream` remote, it lacks the branch, or the base is that
|
|
204
|
+
* ref itself.
|
|
205
|
+
*/
|
|
206
|
+
private getUpstreamContender;
|
|
207
|
+
/** {@link getNearestForkTarget} with the `upstream` remote's copy of `candidate` as its contender */
|
|
208
|
+
private getUpstreamNearestForkTarget;
|
|
125
209
|
/**
|
|
126
210
|
* A candidate base as the ref HEAD is measured against: the candidate or its tracking ref, whichever forked
|
|
127
211
|
* nearer HEAD, so a stale local base defers to its upstream while one carrying unpushed work wins over it. A
|
|
128
|
-
*
|
|
212
|
+
* `contender` other than the tracking ref is taken only when it forked strictly nearer than both, so a tie keeps
|
|
213
|
+
* the candidate's own pick. A candidate none forks from is used as-is if it names a commit. `undefined` when the
|
|
214
|
+
* candidate is unusable.
|
|
129
215
|
*/
|
|
130
216
|
private getNearestForkTarget;
|
|
217
|
+
/**
|
|
218
|
+
* The base for a branch that is itself the default branch, with its tip: its own upstream, `upstream/<branch>`
|
|
219
|
+
* when an `upstream` remote has it, else `<branch>@{u}`. Read from the cached ref and branch lists; only a
|
|
220
|
+
* `<branch>` that isn't checked out spawns, for its `@{u}`. `undefined` when there is neither, or its ref is gone.
|
|
221
|
+
*/
|
|
222
|
+
private getSelfUpstreamTarget;
|
|
223
|
+
/**
|
|
224
|
+
* {@link findRefTip} in `records`, the cached ref list. `getRefs` answers a failed enumeration with an empty list,
|
|
225
|
+
* so an empty one is asked of git instead, rather than read as every ref being absent.
|
|
226
|
+
*/
|
|
227
|
+
private getRefTip;
|
|
131
228
|
/** The merge base of HEAD and `ref`, preferring git's fork-point reading of `ref`'s reflog when it has one */
|
|
132
229
|
private getForkPoint;
|
|
133
230
|
/**
|
|
@@ -142,12 +239,15 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
142
239
|
private resolveStoredMergeTarget;
|
|
143
240
|
getStoredDetectedMergeTargetBranchName(repoPath: string, ref: string): Promise<string | undefined>;
|
|
144
241
|
getStoredUserMergeTargetBranchName(repoPath: string, ref: string): Promise<string | undefined>;
|
|
145
|
-
/** Updates the last accessed timestamp for the current branch */
|
|
146
242
|
onCurrentBranchAccessed(repoPath: string): Promise<void>;
|
|
147
|
-
/** Updates the last accessed and modified timestamp for the current branch */
|
|
148
243
|
onCurrentBranchModified(repoPath: string): Promise<void>;
|
|
149
|
-
/** Updates the agent last activity timestamp for the current branch */
|
|
150
244
|
onCurrentBranchAgentActivity(repoPath: string): Promise<void>;
|
|
245
|
+
private getBranchActivityKey;
|
|
246
|
+
private forgetBranchCreatedFrom;
|
|
247
|
+
/** Forgets this process's throttle marks for `name`, so its next activity re-reads the stored stamps */
|
|
248
|
+
private clearBranchActivityStamps;
|
|
249
|
+
/** Never rejects — activity stamps are bookkeeping and callers fire them without a catch */
|
|
250
|
+
private recordBranchActivity;
|
|
151
251
|
renameBranch(repoPath: string, oldName: string, newName: string): Promise<void>;
|
|
152
252
|
setUpstreamBranch(repoPath: string, name: string, upstream: string | undefined): Promise<void>;
|
|
153
253
|
storeBaseBranchName(repoPath: string, ref: string, base: string, options?: {
|
|
@@ -162,7 +262,6 @@ export declare class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
162
262
|
* @returns A map of branch name to metadata.
|
|
163
263
|
*/
|
|
164
264
|
private getBranchMetadataMap;
|
|
165
|
-
private storeBranchMetadata;
|
|
166
265
|
setBranchDisposition(repoPath: string, branchName: string, disposition: BranchDisposition | undefined): Promise<void>;
|
|
167
266
|
}
|
|
168
267
|
//#endregion
|