@gitkraken/core-gitlens 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/CHANGELOG.md +78 -1
  2. package/dist/git/cache.d.ts +84 -29
  3. package/dist/git/cache.js +188 -54
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/features.d.ts +1 -1
  6. package/dist/git/features.js +5 -0
  7. package/dist/git/features.js.map +1 -1
  8. package/dist/git/models/branch.d.ts +20 -2
  9. package/dist/git/models/branch.js +28 -6
  10. package/dist/git/models/branch.js.map +1 -1
  11. package/dist/git/models/reference.d.ts +3 -0
  12. package/dist/git/models/status.js +2 -2
  13. package/dist/git/models/worktree.d.ts +4 -1
  14. package/dist/git/models/worktree.js +4 -2
  15. package/dist/git/models/worktree.js.map +1 -1
  16. package/dist/git/parsers/diffParser.js +1 -1
  17. package/dist/git/providers/branches.d.ts +73 -8
  18. package/dist/git/providers/operations.d.ts +3 -2
  19. package/dist/git/providers/pausedOperations.d.ts +6 -0
  20. package/dist/git/providers/provider.d.ts +45 -2
  21. package/dist/git/providers/revision.d.ts +17 -3
  22. package/dist/git/providers/status.d.ts +8 -2
  23. package/dist/git/remotes/azure-devops.js +61 -53
  24. package/dist/git/remotes/azure-devops.js.map +1 -1
  25. package/dist/git/run.types.d.ts +12 -0
  26. package/dist/git/service.d.ts +8 -5
  27. package/dist/git/service.js +16 -8
  28. package/dist/git/service.js.map +1 -1
  29. package/dist/git/utils/branch.utils.d.ts +12 -2
  30. package/dist/git/utils/branch.utils.js +49 -5
  31. package/dist/git/utils/branch.utils.js.map +1 -1
  32. package/dist/git/utils/reference.utils.d.ts +1 -0
  33. package/dist/git/utils/reference.utils.js.map +1 -1
  34. package/dist/git/watching/changeEvent.d.ts +13 -2
  35. package/dist/git/watching/changeEvent.js +17 -3
  36. package/dist/git/watching/changeEvent.js.map +1 -1
  37. package/dist/git/watching/gitDirPoller.d.ts +111 -0
  38. package/dist/git/watching/gitDirPoller.js +266 -0
  39. package/dist/git/watching/gitDirPoller.js.map +1 -0
  40. package/dist/git/watching/provider.d.ts +16 -0
  41. package/dist/git/watching/watchGroup.d.ts +40 -4
  42. package/dist/git/watching/watchGroup.js +143 -17
  43. package/dist/git/watching/watchGroup.js.map +1 -1
  44. package/dist/git/watching/watchService.d.ts +70 -1
  45. package/dist/git/watching/watchService.js +158 -13
  46. package/dist/git/watching/watchService.js.map +1 -1
  47. package/dist/git/watching/watchSession.d.ts +44 -0
  48. package/dist/git/watching/watchSession.js +91 -6
  49. package/dist/git/watching/watchSession.js.map +1 -1
  50. package/dist/git/watching/watcherPatterns.d.ts +21 -0
  51. package/dist/git/watching/watcherPatterns.js +67 -1
  52. package/dist/git/watching/watcherPatterns.js.map +1 -1
  53. package/dist/git/watching/worktreeSummary.d.ts +34 -7
  54. package/dist/git/watching/worktreeSummary.js +39 -12
  55. package/dist/git/watching/worktreeSummary.js.map +1 -1
  56. package/dist/git-cli/cliGitProvider.d.ts +40 -2
  57. package/dist/git-cli/cliGitProvider.js +182 -18
  58. package/dist/git-cli/cliGitProvider.js.map +1 -1
  59. package/dist/git-cli/exec/commandClassifier.d.ts +15 -0
  60. package/dist/git-cli/exec/commandClassifier.js +48 -2
  61. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  62. package/dist/git-cli/exec/git.d.ts +20 -3
  63. package/dist/git-cli/exec/git.js +63 -19
  64. package/dist/git-cli/exec/git.js.map +1 -1
  65. package/dist/git-cli/exec/gitQueue.d.ts +2 -0
  66. package/dist/git-cli/exec/gitQueue.js +2 -1
  67. package/dist/git-cli/exec/gitQueue.js.map +1 -1
  68. package/dist/git-cli/exec/indexLock.d.ts +39 -0
  69. package/dist/git-cli/exec/indexLock.js +160 -0
  70. package/dist/git-cli/exec/indexLock.js.map +1 -0
  71. package/dist/git-cli/parsers/blameParser.js +2 -2
  72. package/dist/git-cli/parsers/refParser.js +4 -2
  73. package/dist/git-cli/parsers/refParser.js.map +1 -1
  74. package/dist/git-cli/parsers/statusParser.d.ts +6 -0
  75. package/dist/git-cli/parsers/statusParser.js +33 -3
  76. package/dist/git-cli/parsers/statusParser.js.map +1 -1
  77. package/dist/git-cli/parsers/worktreeParser.js +42 -2
  78. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  79. package/dist/git-cli/providers/branchRemote.d.ts +14 -0
  80. package/dist/git-cli/providers/branchRemote.js +36 -0
  81. package/dist/git-cli/providers/branchRemote.js.map +1 -0
  82. package/dist/git-cli/providers/branches.d.ts +109 -10
  83. package/dist/git-cli/providers/branches.js +630 -134
  84. package/dist/git-cli/providers/branches.js.map +1 -1
  85. package/dist/git-cli/providers/commits.js +2 -2
  86. package/dist/git-cli/providers/config.d.ts +9 -5
  87. package/dist/git-cli/providers/config.js +58 -25
  88. package/dist/git-cli/providers/config.js.map +1 -1
  89. package/dist/git-cli/providers/contributors.js +1 -1
  90. package/dist/git-cli/providers/graph.js +9 -6
  91. package/dist/git-cli/providers/graph.js.map +1 -1
  92. package/dist/git-cli/providers/operations.d.ts +17 -0
  93. package/dist/git-cli/providers/operations.js +172 -111
  94. package/dist/git-cli/providers/operations.js.map +1 -1
  95. package/dist/git-cli/providers/pausedOperations.d.ts +8 -0
  96. package/dist/git-cli/providers/pausedOperations.js +59 -39
  97. package/dist/git-cli/providers/pausedOperations.js.map +1 -1
  98. package/dist/git-cli/providers/refs.js +11 -8
  99. package/dist/git-cli/providers/refs.js.map +1 -1
  100. package/dist/git-cli/providers/remotes.js +19 -10
  101. package/dist/git-cli/providers/remotes.js.map +1 -1
  102. package/dist/git-cli/providers/revision.d.ts +6 -2
  103. package/dist/git-cli/providers/revision.js +38 -12
  104. package/dist/git-cli/providers/revision.js.map +1 -1
  105. package/dist/git-cli/providers/staging.js +39 -14
  106. package/dist/git-cli/providers/staging.js.map +1 -1
  107. package/dist/git-cli/providers/stash.d.ts +2 -0
  108. package/dist/git-cli/providers/stash.js +33 -18
  109. package/dist/git-cli/providers/stash.js.map +1 -1
  110. package/dist/git-cli/providers/status.d.ts +3 -1
  111. package/dist/git-cli/providers/status.js +40 -22
  112. package/dist/git-cli/providers/status.js.map +1 -1
  113. package/dist/git-cli/providers/tags.js +6 -4
  114. package/dist/git-cli/providers/tags.js.map +1 -1
  115. package/dist/git-cli/providers/worktrees.d.ts +1 -0
  116. package/dist/git-cli/providers/worktrees.js +60 -31
  117. package/dist/git-cli/providers/worktrees.js.map +1 -1
  118. package/dist/git-cli/service.d.ts +6 -0
  119. package/dist/git-cli/service.js +5 -2
  120. package/dist/git-cli/service.js.map +1 -1
  121. package/dist/plus/git-github/providers/github/revision.d.ts +6 -2
  122. package/dist/plus/git-github/providers/github/revision.js +15 -10
  123. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  124. package/dist/plus/git-github/providers/github/status.d.ts +3 -1
  125. package/dist/plus/git-github/providers/github/status.js +2 -2
  126. package/dist/plus/git-github/providers/github/status.js.map +1 -1
  127. package/dist/plus/integrations/collectionMetadata.d.ts +13 -2
  128. package/dist/plus/integrations/collectionMetadata.js +14 -2
  129. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  130. package/dist/plus/integrations/index.d.ts +2 -2
  131. package/dist/plus/integrations/index.js.map +1 -1
  132. package/dist/plus/integrations/integrationService.d.ts +1 -0
  133. package/dist/plus/integrations/integrationService.js +1 -1
  134. package/dist/plus/integrations/integrationService.js.map +1 -1
  135. package/dist/plus/integrations/manager.d.ts +1 -0
  136. package/dist/plus/integrations/models/gitHostIntegration.d.ts +41 -5
  137. package/dist/plus/integrations/models/gitHostIntegration.js +106 -29
  138. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  139. package/dist/plus/integrations/models/issueReads.d.ts +17 -4
  140. package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
  141. package/dist/plus/integrations/providers/apiClients.js +7 -1
  142. package/dist/plus/integrations/providers/apiClients.js.map +1 -1
  143. package/dist/plus/integrations/providers/azure/azure.d.ts +5 -4
  144. package/dist/plus/integrations/providers/azure/azure.js +36 -76
  145. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  146. package/dist/plus/integrations/providers/azure/azure.utils.js +20 -17
  147. package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
  148. package/dist/plus/integrations/providers/azure/models.d.ts +2 -2
  149. package/dist/plus/integrations/providers/azure/models.js +4 -4
  150. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  151. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +21 -12
  152. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +74 -17
  153. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  154. package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
  155. package/dist/plus/integrations/providers/azureDevOps.js +108 -17
  156. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  157. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +45 -1
  158. package/dist/plus/integrations/providers/azureDevOpsServer.js +121 -6
  159. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  160. package/dist/plus/integrations/providers/jira-server.js +18 -14
  161. package/dist/plus/integrations/providers/jira-server.js.map +1 -1
  162. package/dist/plus/integrations/providers/jira.js +18 -14
  163. package/dist/plus/integrations/providers/jira.js.map +1 -1
  164. package/dist/plus/integrations/providers/models.d.ts +1 -2
  165. package/dist/plus/integrations/providers/models.js +9 -2
  166. package/dist/plus/integrations/providers/models.js.map +1 -1
  167. package/dist/plus/integrations/providers/providersApi.d.ts +7 -17
  168. package/dist/plus/integrations/providers/providersApi.js +85 -78
  169. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  170. package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +22 -0
  171. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +69 -9
  172. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
  173. package/dist/plus/integrations/providers/utils/providerPaging.js +8 -2
  174. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  175. package/dist/plus/integrations/reads/filters.d.ts +3 -3
  176. package/dist/plus/integrations/reads/filters.js.map +1 -1
  177. package/dist/plus/integrations/reads/issueBatch.d.ts +2 -1
  178. package/dist/plus/integrations/reads/issueBatch.js +1 -1
  179. package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
  180. package/dist/plus/integrations/reads/issueTracker.js +23 -14
  181. package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
  182. package/dist/plus/integrations/reads/issues.d.ts +2 -1
  183. package/dist/plus/integrations/reads/issues.js +18 -3
  184. package/dist/plus/integrations/reads/issues.js.map +1 -1
  185. package/dist/plus/integrations/reads/pullRequestBatch.js +1 -1
  186. package/dist/plus/integrations/reads/resolveRepository.js +12 -3
  187. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  188. package/dist/plus/integrations/reads/warnings.d.ts +11 -4
  189. package/dist/plus/integrations/reads/warnings.js +15 -5
  190. package/dist/plus/integrations/reads/warnings.js.map +1 -1
  191. package/dist/plus/integrations/results.d.ts +36 -2
  192. package/dist/plus/integrations/results.js +33 -13
  193. package/dist/plus/integrations/results.js.map +1 -1
  194. package/dist/utils/promiseCache.d.ts +10 -0
  195. package/dist/utils/promiseCache.js +17 -2
  196. package/dist/utils/promiseCache.js.map +1 -1
  197. package/docs/integrations.md +52 -20
  198. package/package.json +2 -2
@@ -0,0 +1,39 @@
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. `beforeRetry` runs after each
21
+ * wait, right before the next attempt; whatever it throws ends the retrying and is rethrown.
22
+ */
23
+ export declare function retryWhileIndexLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined, beforeRetry?: (lockError: Error) => void | Promise<void>): Promise<T>;
24
+ /**
25
+ * Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another
26
+ * process — git fails at once rather than waiting.
27
+ */
28
+ export declare function retryWhileConfigLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T>;
29
+ /**
30
+ * 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
31
+ * run whose signal aborts while it waits rejects at once without ever starting.
32
+ */
33
+ export declare class KeyedSequencer {
34
+ /** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */
35
+ private readonly _tails;
36
+ run<T>(key: string, signal: AbortSignal | undefined, fn: () => Promise<T>): Promise<T>;
37
+ }
38
+ //#endregion
39
+ //# sourceMappingURL=indexLock.d.ts.map
@@ -0,0 +1,160 @@
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
+ /**
31
+ * Matches git's `fatal: Unable to create '<path>/index.lock': File exists.` — the wording varies a little by version.
32
+ * Anchored to the start of stderr: git takes the lock before doing anything else, so its own failure is the first
33
+ * thing it prints past any `warning:`/`hint:` lines from parsing its arguments (`checkout` warns of an ambiguous
34
+ * refname first). The same line after other output came from something the run started, such as a hook running its
35
+ * own git command, after the run had already done its work — retrying would run it all again.
36
+ */
37
+ const indexLockHeldRegex = /^(?:\s*(?:warning|hint): [^\r\n]*\r?\n)*\s*(?:fatal|error): Unable to create '[^\r\n]*index\.lock': File exists/i;
38
+ /** How long to wait before each retry of a run that found `index.lock` held; its length is the retry count. */
39
+ const indexLockRetryDelays = [
40
+ 100,
41
+ 200,
42
+ 400
43
+ ];
44
+ /**
45
+ * Whether the argv is a command that writes the index (`apply` only does with `--index`, `--cached` or `--3way`,
46
+ * which implies `--index`, and never with `--check`).
47
+ */
48
+ function isIndexWriteCommand(args) {
49
+ const { verb, rest } = leadingCommand(args);
50
+ if (verb === "apply") return !rest.includes("--check") && rest.some((a) => applyIndexOptions.has(a));
51
+ return indexWritingVerbs.has(verb);
52
+ }
53
+ /** Whether a run failed because another process held `index.lock`. */
54
+ function isIndexLockHeldError(ex) {
55
+ return ex instanceof RunError && !(ex instanceof CancelledRunError) && indexLockHeldRegex.test(ex.stderr);
56
+ }
57
+ /** Resolves after `ms`, or rejects as soon as `signal` aborts. */
58
+ async function delay(ms, signal) {
59
+ try {
60
+ await setTimeout(ms, void 0, { signal });
61
+ } catch (ex) {
62
+ if (signal?.aborted) throw abortReason(signal);
63
+ throw ex;
64
+ }
65
+ }
66
+ /**
67
+ * Captures the rest of git's `error: could not lock config file <path>[: <reason>]` line. A held lock reads
68
+ * `: File exists`, except `--remove-section`/`--rename-section` print no reason (for any failure); permanent
69
+ * failures (`Permission denied`, `No such file or directory`, ...) must not be retried.
70
+ */
71
+ const configLockFailureRegex = /could not lock config file ([^\r\n]*)/i;
72
+ /**
73
+ * Whether the argv is `git config`. Only that verb: others that write config (`branch -m`, `remote add`) can
74
+ * fail after a partial change, so a blind retry isn't safe.
75
+ */
76
+ function isConfigCommand(args) {
77
+ return leadingCommand(args).verb === "config";
78
+ }
79
+ /** Whether a run failed because another process may hold the config file's `.lock`: the reason is `File exists` or absent. */
80
+ function isConfigLockHeldError(ex) {
81
+ if (!(ex instanceof RunError) || ex instanceof CancelledRunError) return false;
82
+ const rest = configLockFailureRegex.exec(ex.stderr)?.[1];
83
+ if (rest == null) return false;
84
+ return /: File exists\s*$/i.test(rest) || !rest.includes(": ");
85
+ }
86
+ /**
87
+ * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails with an error
88
+ * `isHeldError` accepts. Any other failure, or the last lock failure, is rethrown as is.
89
+ */
90
+ async function retryWhileHeld(attempt, signal, isHeldError, beforeRetry) {
91
+ for (let retry = 0;; retry++) {
92
+ let lockError;
93
+ try {
94
+ return await attempt();
95
+ } catch (ex) {
96
+ if (retry >= indexLockRetryDelays.length || !isHeldError(ex)) throw ex;
97
+ lockError = ex;
98
+ }
99
+ await delay(indexLockRetryDelays[retry], signal);
100
+ await beforeRetry?.(lockError);
101
+ }
102
+ }
103
+ /**
104
+ * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails on a held
105
+ * `index.lock`. Any other failure, or the last lock failure, is rethrown as is. `beforeRetry` runs after each
106
+ * wait, right before the next attempt; whatever it throws ends the retrying and is rethrown.
107
+ */
108
+ function retryWhileIndexLocked(attempt, signal, beforeRetry) {
109
+ return retryWhileHeld(attempt, signal, isIndexLockHeldError, beforeRetry);
110
+ }
111
+ /**
112
+ * Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another
113
+ * process — git fails at once rather than waiting.
114
+ */
115
+ function retryWhileConfigLocked(attempt, signal) {
116
+ return retryWhileHeld(attempt, signal, isConfigLockHeldError);
117
+ }
118
+ /**
119
+ * 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
120
+ * run whose signal aborts while it waits rejects at once without ever starting.
121
+ */
122
+ var KeyedSequencer = class {
123
+ /** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */
124
+ _tails = /* @__PURE__ */ new Map();
125
+ async run(key, signal, fn) {
126
+ const previous = this._tails.get(key);
127
+ let release;
128
+ const tail = new Promise((resolve) => release = resolve);
129
+ this._tails.set(key, tail);
130
+ const finish = () => {
131
+ release();
132
+ if (this._tails.get(key) === tail) this._tails.delete(key);
133
+ };
134
+ let started = false;
135
+ try {
136
+ if (previous != null) await waitFor(previous, signal);
137
+ started = true;
138
+ return await fn();
139
+ } finally {
140
+ if (started || previous == null) finish();
141
+ else previous.then(finish);
142
+ }
143
+ }
144
+ };
145
+ function waitFor(promise, signal) {
146
+ if (signal == null) return promise;
147
+ if (signal.aborted) return Promise.reject(abortReason(signal));
148
+ return new Promise((resolve, reject) => {
149
+ const onAbort = () => reject(abortReason(signal));
150
+ signal.addEventListener("abort", onAbort, { once: true });
151
+ promise.then(() => {
152
+ signal.removeEventListener("abort", onAbort);
153
+ resolve();
154
+ });
155
+ });
156
+ }
157
+ //#endregion
158
+ export { KeyedSequencer, indexLockRetryDelays, isConfigCommand, isConfigLockHeldError, isIndexLockHeldError, isIndexWriteCommand, retryWhileConfigLocked, retryWhileIndexLocked };
159
+
160
+ //# 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/**\n * Matches git's `fatal: Unable to create '<path>/index.lock': File exists.` — the wording varies a little by version.\n * Anchored to the start of stderr: git takes the lock before doing anything else, so its own failure is the first\n * thing it prints past any `warning:`/`hint:` lines from parsing its arguments (`checkout` warns of an ambiguous\n * refname first). The same line after other output came from something the run started, such as a hook running its\n * own git command, after the run had already done its work — retrying would run it all again.\n */\nconst indexLockHeldRegex =\n\t/^(?:\\s*(?:warning|hint): [^\\r\\n]*\\r?\\n)*\\s*(?:fatal|error): Unable to create '[^\\r\\n]*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\tbeforeRetry?: (lockError: Error) => void | Promise<void>,\n): Promise<T> {\n\tfor (let retry = 0; ; retry++) {\n\t\tlet lockError: Error;\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\n\t\t\tlockError = ex as Error;\n\t\t}\n\n\t\tawait delay(indexLockRetryDelays[retry], signal);\n\t\tawait beforeRetry?.(lockError);\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. `beforeRetry` runs after each\n * wait, right before the next attempt; whatever it throws ends the retrying and is rethrown.\n */\nexport function retryWhileIndexLocked<T>(\n\tattempt: () => Promise<T>,\n\tsignal: AbortSignal | undefined,\n\tbeforeRetry?: (lockError: Error) => void | Promise<void>,\n): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isIndexLockHeldError, beforeRetry);\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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"}
@@ -3,11 +3,11 @@ import { fileUri, joinUriPath } from "../../utils/uri.js";
3
3
  import { normalizePath } from "../../utils/path.js";
4
4
  import { uncommitted } from "../../git/models/revision.js";
5
5
  import { GitFileIndexStatus } from "../../git/models/fileStatus.js";
6
+ import { maybeStopWatch } from "../../utils/stopwatch.js";
7
+ import _usingCtx from "../../_virtual/@oxc-project+runtime/helpers/esm/usingCtx.js";
6
8
  import { GitFileChange } from "../../git/models/fileChange.js";
7
9
  import { GitCommit, GitCommitIdentity } from "../../git/models/commit.js";
8
10
  import { isUserMatch } from "../../git/utils/user.utils.js";
9
- import { maybeStopWatch } from "../../utils/stopwatch.js";
10
- import _usingCtx from "../../_virtual/@oxc-project+runtime/helpers/esm/usingCtx.js";
11
11
  //#region ../git-cli/src/parsers/blameParser.ts
12
12
  function parseGitBlame(repoPath, data, currentUser, modifiedTime) {
13
13
  try {
@@ -22,11 +22,13 @@ const refMapping = {
22
22
  creatorDate: `%(creatordate:iso8601)`,
23
23
  authorDate: `%(authordate:iso8601)`,
24
24
  subject: `%(subject)`,
25
- worktreePath: void 0
25
+ worktreePath: void 0,
26
+ upstreamRemote: void 0
26
27
  };
27
28
  const refMappingSupportsWorktreePath = {
28
29
  ...refMapping,
29
- worktreePath: "%(worktreepath)"
30
+ worktreePath: "%(worktreepath)",
31
+ upstreamRemote: "%(upstream:remotename)"
30
32
  };
31
33
  let _refParser;
32
34
  let _refParserWithWorktree;
@@ -1 +1 @@
1
- {"version":3,"file":"refParser.js","names":[],"sources":["../../../../git-cli/src/parsers/refParser.ts"],"sourcesContent":["import type { FilteredGitFeatures } from '@gitlens/git/features.js';\nimport type { RefRecord } from '@gitlens/git/models/reference.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { ExtractAll } from '@gitlens/utils/types.js';\nimport type { Parser } from './logParser.js';\n\n/**\n * Unified `for-each-ref` mapping covering branches, remotes, and tags in a single pass.\n * Fields inapplicable to a given ref-type come back as empty strings from git.\n *\n * NOTE: When adding a new field that is gated on a git feature flag, mirror the entry in\n * `refMappingSupportsWorktreePath` (and any future variants) — both maps MUST stay aligned\n * so callers can switch parsers based on `git.supported('git:for-each-ref')` without losing\n * field positions.\n */\nconst refMapping = {\n\tcurrent: `%(HEAD)`,\n\tname: `%(refname)`,\n\tobjectname: `%(objectname)`,\n\tpeeledObjectname: `%(*objectname)`,\n\tupstream: `%(upstream)`,\n\tupstreamTracking: `%(upstream:track)`,\n\tcommitterDate: `%(committerdate:iso8601)`,\n\tcreatorDate: `%(creatordate:iso8601)`,\n\tauthorDate: `%(authordate:iso8601)`,\n\tsubject: `%(subject)`,\n\tworktreePath: undefined,\n};\n\nconst refMappingSupportsWorktreePath = {\n\t...refMapping,\n\tworktreePath: '%(worktreepath)',\n};\n\ntype RefParser = Parser<RefRecord>;\n\nlet _refParser: RefParser | undefined;\nlet _refParserWithWorktree: RefParser | undefined;\n\nexport function getRefParser(supportedFeatures: FilteredGitFeatures<'git:for-each-ref'>[]): RefParser {\n\tif (supportedFeatures.includes('git:for-each-ref:worktreePath')) {\n\t\t_refParserWithWorktree ??= createRefParser(refMappingSupportsWorktreePath);\n\t\treturn _refParserWithWorktree;\n\t}\n\n\t_refParser ??= createRefParser(refMapping);\n\treturn _refParser;\n}\n\nconst recordSep = '\\x1E'; // ASCII Record Separator character\nconst recordFormatSep = '%1E';\nconst fieldSep = '\\x1D'; // ASCII Group Separator character\nconst fieldFormatSep = '%1D';\n\nfunction createRefParser<T extends Record<string, string | undefined>>(\n\tmapping: ExtractAll<T, string | undefined>,\n): Parser<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tconst value = mapping[key];\n\t\tif (!value) continue;\n\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<T> {\n\t\tusing sw = maybeStopWatch('Git.RefParser.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: T;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as T;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue; // Handle extra newlines at the end\n\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn { arguments: args, separators: { record: recordSep, field: fieldSep }, parse: parse };\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,aAAa;CAClB,SAAS;CACT,MAAM;CACN,YAAY;CACZ,kBAAkB;CAClB,UAAU;CACV,kBAAkB;CAClB,eAAe;CACf,aAAa;CACb,YAAY;CACZ,SAAS;CACT,cAAc,KAAA;AACf;AAEA,MAAM,iCAAiC;CACtC,GAAG;CACH,cAAc;AACf;AAIA,IAAI;AACJ,IAAI;AAEJ,SAAgB,aAAa,mBAAyE;CACrG,IAAI,kBAAkB,SAAS,+BAA+B,GAAG;EAChE,2BAA2B,gBAAgB,8BAA8B;EACzE,OAAO;CACR;CAEA,eAAe,gBAAgB,UAAU;CACzC,OAAO;AACR;AAEA,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,WAAW;AACjB,MAAM,iBAAiB;AAEvB,SAAS,gBACR,SACY;CACZ,IAAI,SAAS;CACb,MAAM,OAAwC,CAAC;CAC/C,KAAK,MAAM,OAAO,SAAS;EAE1B,IAAI,CADU,QAAQ,MACV;EAEZ,KAAK,KAAK,GAAG;EACb,UAAU,GAAG,QAAQ,OAAO;CAC7B;CAEA,MAAM,OAAO,CAAC,YAAY,QAAQ;CAElC,UAAU,MAAM,MAA2D;;;GAC1E,MAAM,KAAA,YAAA,EAAK,eAAe,uBAAuB,EAAE,KAAK;IAAE,UAAU;IAAM,OAAO;GAAQ,EAAE,CAAC,CAAA;GAE5F,IAAI,CAAC,MAAM;IACV,IAAI,KAAK,EAAE,QAAQ,WAAW,CAAC;IAC/B;GACD;GAEA,MAAM,UAAU,mBAAmB,MAAM,SAAS;GAElD,IAAI,QAAQ;GACZ,IAAI;GACJ,IAAI;GAEJ,KAAK,MAAM,UAAU,SAAS;IAC7B,IAAI,CAAC,OAAO,QAAQ;IAEpB;IACA,QAAQ,CAAC;IACT,SAAS,mBAAmB,QAAQ,QAAQ;IAE5C,IAAI,aAAa;IACjB,IAAI;IAEJ,OAAO,MAAM;KACZ,QAAQ,OAAO,KAAK;KACpB,IAAI,MAAM,MAAM;KAChB,IAAI,cAAc,KAAK,QAAQ;KAE/B,MAAM,KAAK,iBAAiB,MAAM;IACnC;IAEA,MAAM;GACP;GAEA,IAAI,KAAK,EAAE,QAAQ,WAAW,MAAM,UAAU,CAAC;;;;;;CAChD;CAEA,OAAO;EAAE,WAAW;EAAM,YAAY;GAAE,QAAQ;GAAW,OAAO;EAAS;EAAU;CAAM;AAC5F"}
1
+ {"version":3,"file":"refParser.js","names":[],"sources":["../../../../git-cli/src/parsers/refParser.ts"],"sourcesContent":["import type { FilteredGitFeatures } from '@gitlens/git/features.js';\nimport type { RefRecord } from '@gitlens/git/models/reference.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { ExtractAll } from '@gitlens/utils/types.js';\nimport type { Parser } from './logParser.js';\n\n/**\n * Unified `for-each-ref` mapping covering branches, remotes, and tags in a single pass.\n * Fields inapplicable to a given ref-type come back as empty strings from git.\n *\n * NOTE: When adding a new field that is gated on a git feature flag, mirror the entry in\n * `refMappingSupportsWorktreePath` (and any future variants) — both maps MUST stay aligned\n * so callers can switch parsers based on `git.supported('git:for-each-ref')` without losing\n * field positions.\n */\nconst refMapping = {\n\tcurrent: `%(HEAD)`,\n\tname: `%(refname)`,\n\tobjectname: `%(objectname)`,\n\tpeeledObjectname: `%(*objectname)`,\n\tupstream: `%(upstream)`,\n\tupstreamTracking: `%(upstream:track)`,\n\tcommitterDate: `%(committerdate:iso8601)`,\n\tcreatorDate: `%(creatordate:iso8601)`,\n\tauthorDate: `%(authordate:iso8601)`,\n\tsubject: `%(subject)`,\n\tworktreePath: undefined,\n\tupstreamRemote: undefined,\n};\n\nconst refMappingSupportsWorktreePath = {\n\t...refMapping,\n\tworktreePath: '%(worktreepath)',\n\t// `%(upstream:remotename)` needs git 2.16, which the 2.23 `worktreePath` gate already implies\n\tupstreamRemote: '%(upstream:remotename)',\n};\n\ntype RefParser = Parser<RefRecord>;\n\nlet _refParser: RefParser | undefined;\nlet _refParserWithWorktree: RefParser | undefined;\n\nexport function getRefParser(supportedFeatures: FilteredGitFeatures<'git:for-each-ref'>[]): RefParser {\n\tif (supportedFeatures.includes('git:for-each-ref:worktreePath')) {\n\t\t_refParserWithWorktree ??= createRefParser(refMappingSupportsWorktreePath);\n\t\treturn _refParserWithWorktree;\n\t}\n\n\t_refParser ??= createRefParser(refMapping);\n\treturn _refParser;\n}\n\nconst recordSep = '\\x1E'; // ASCII Record Separator character\nconst recordFormatSep = '%1E';\nconst fieldSep = '\\x1D'; // ASCII Group Separator character\nconst fieldFormatSep = '%1D';\n\nfunction createRefParser<T extends Record<string, string | undefined>>(\n\tmapping: ExtractAll<T, string | undefined>,\n): Parser<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tconst value = mapping[key];\n\t\tif (!value) continue;\n\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<T> {\n\t\tusing sw = maybeStopWatch('Git.RefParser.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: T;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as T;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue; // Handle extra newlines at the end\n\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn { arguments: args, separators: { record: recordSep, field: fieldSep }, parse: parse };\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,aAAa;CAClB,SAAS;CACT,MAAM;CACN,YAAY;CACZ,kBAAkB;CAClB,UAAU;CACV,kBAAkB;CAClB,eAAe;CACf,aAAa;CACb,YAAY;CACZ,SAAS;CACT,cAAc,KAAA;CACd,gBAAgB,KAAA;AACjB;AAEA,MAAM,iCAAiC;CACtC,GAAG;CACH,cAAc;CAEd,gBAAgB;AACjB;AAIA,IAAI;AACJ,IAAI;AAEJ,SAAgB,aAAa,mBAAyE;CACrG,IAAI,kBAAkB,SAAS,+BAA+B,GAAG;EAChE,2BAA2B,gBAAgB,8BAA8B;EACzE,OAAO;CACR;CAEA,eAAe,gBAAgB,UAAU;CACzC,OAAO;AACR;AAEA,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,WAAW;AACjB,MAAM,iBAAiB;AAEvB,SAAS,gBACR,SACY;CACZ,IAAI,SAAS;CACb,MAAM,OAAwC,CAAC;CAC/C,KAAK,MAAM,OAAO,SAAS;EAE1B,IAAI,CADU,QAAQ,MACV;EAEZ,KAAK,KAAK,GAAG;EACb,UAAU,GAAG,QAAQ,OAAO;CAC7B;CAEA,MAAM,OAAO,CAAC,YAAY,QAAQ;CAElC,UAAU,MAAM,MAA2D;;;GAC1E,MAAM,KAAA,YAAA,EAAK,eAAe,uBAAuB,EAAE,KAAK;IAAE,UAAU;IAAM,OAAO;GAAQ,EAAE,CAAC,CAAA;GAE5F,IAAI,CAAC,MAAM;IACV,IAAI,KAAK,EAAE,QAAQ,WAAW,CAAC;IAC/B;GACD;GAEA,MAAM,UAAU,mBAAmB,MAAM,SAAS;GAElD,IAAI,QAAQ;GACZ,IAAI;GACJ,IAAI;GAEJ,KAAK,MAAM,UAAU,SAAS;IAC7B,IAAI,CAAC,OAAO,QAAQ;IAEpB;IACA,QAAQ,CAAC;IACT,SAAS,mBAAmB,QAAQ,QAAQ;IAE5C,IAAI,aAAa;IACjB,IAAI;IAEJ,OAAO,MAAM;KACZ,QAAQ,OAAO,KAAK;KACpB,IAAI,MAAM,MAAM;KAChB,IAAI,cAAc,KAAK,QAAQ;KAE/B,MAAM,KAAK,iBAAiB,MAAM;IACnC;IAEA,MAAM;GACP;GAEA,IAAI,KAAK,EAAE,QAAQ,WAAW,MAAM,UAAU,CAAC;;;;;;CAChD;CAEA,OAAO;EAAE,WAAW;EAAM,YAAY;GAAE,QAAQ;GAAW,OAAO;EAAS;EAAU;CAAM;AAC5F"}
@@ -3,5 +3,11 @@ import { GitStatus } from "../../git/models/status.js";
3
3
  //#region ../git-cli/src/parsers/statusParser.d.ts
4
4
  /** Without `getUri`, each file builds its uri on first read rather than during the parse */
5
5
  export declare function parseGitStatus(data: string, repoPath: string, porcelainVersion: number, getUri?: (path: string) => Uri): GitStatus | undefined;
6
+ /**
7
+ * Folds `status --ignored=matching --untracked-files=all` records of new untracked folders into the entries that
8
+ * `--ignored=traditional --untracked-files=normal` reports. Inside a new untracked folder nothing is tracked, so a
9
+ * folder holding no untracked content is wholly ignored, and is reported in place of the ignored paths beneath it.
10
+ */
11
+ export declare function foldMatchingIgnored(records: Iterable<string>): string[];
6
12
  //#endregion
7
13
  //# sourceMappingURL=statusParser.d.ts.map
@@ -1,8 +1,8 @@
1
1
  import { normalizePath } from "../../utils/path.js";
2
- import { maybeStopWatch } from "../../utils/stopwatch.js";
3
- import _usingCtx from "../../_virtual/@oxc-project+runtime/helpers/esm/usingCtx.js";
4
2
  import { GitStatus } from "../../git/models/status.js";
5
3
  import { GitStatusFile } from "../../git/models/statusFile.js";
4
+ import { maybeStopWatch } from "../../utils/stopwatch.js";
5
+ import _usingCtx from "../../_virtual/@oxc-project+runtime/helpers/esm/usingCtx.js";
6
6
  //#region ../git-cli/src/parsers/statusParser.ts
7
7
  const aheadStatusV1Regex = /(?:ahead ([0-9]+))/;
8
8
  const behindStatusV1Regex = /(?:behind ([0-9]+))/;
@@ -179,7 +179,37 @@ function parseStatusFile(repoPath, rawStatus, fileName, getUri, originalFileName
179
179
  }
180
180
  return new GitStatusFile(repoPath, x, y, fileName, getUri?.(fileName), originalFileName, submodule);
181
181
  }
182
+ /**
183
+ * Folds `status --ignored=matching --untracked-files=all` records of new untracked folders into the entries that
184
+ * `--ignored=traditional --untracked-files=normal` reports. Inside a new untracked folder nothing is tracked, so a
185
+ * folder holding no untracked content is wholly ignored, and is reported in place of the ignored paths beneath it.
186
+ */
187
+ function foldMatchingIgnored(records) {
188
+ const dirty = /* @__PURE__ */ new Set();
189
+ const ignored = [];
190
+ for (const record of records) {
191
+ if (record.length < 4) continue;
192
+ if (record.startsWith("!! ")) ignored.push(record.substring(3));
193
+ else if (record.startsWith("?? ")) {
194
+ const path = record.substring(3);
195
+ for (let i = path.indexOf("/"); i !== -1; i = path.indexOf("/", i + 1)) dirty.add(path.substring(0, i + 1));
196
+ }
197
+ }
198
+ const entries = /* @__PURE__ */ new Set();
199
+ for (const path of ignored) {
200
+ let entry = path;
201
+ for (let i = path.indexOf("/"); i !== -1 && i + 1 < path.length; i = path.indexOf("/", i + 1)) {
202
+ const folder = path.substring(0, i + 1);
203
+ if (!dirty.has(folder)) {
204
+ entry = folder;
205
+ break;
206
+ }
207
+ }
208
+ entries.add(entry);
209
+ }
210
+ return [...entries];
211
+ }
182
212
  //#endregion
183
- export { parseGitStatus };
213
+ export { foldMatchingIgnored, parseGitStatus };
184
214
 
185
215
  //# sourceMappingURL=statusParser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"statusParser.js","names":[],"sources":["../../../../git-cli/src/parsers/statusParser.ts"],"sourcesContent":["import { GitStatus } from '@gitlens/git/models/status.js';\nimport { GitStatusFile } from '@gitlens/git/models/statusFile.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\n\nconst aheadStatusV1Regex = /(?:ahead ([0-9]+))/;\nconst behindStatusV1Regex = /(?:behind ([0-9]+))/;\nconst quoteRegex = /\"/g;\nconst submoduleCharCode = 'S'.charCodeAt(0);\n\n/** Returns the index just after the `n`th space, or -1 if the line has fewer than `n` spaces. */\nfunction indexAfterNthSpace(line: string, n: number): number {\n\tlet index = -1;\n\tfor (let i = 0; i < n; i++) {\n\t\tindex = line.indexOf(' ', index + 1);\n\t\tif (index === -1) return -1;\n\t}\n\n\treturn index + 1;\n}\n\n/** Without `getUri`, each file builds its uri on first read rather than during the parse */\nexport function parseGitStatus(\n\tdata: string,\n\trepoPath: string,\n\tporcelainVersion: number,\n\tgetUri?: (path: string) => Uri,\n): GitStatus | undefined {\n\tusing sw = maybeStopWatch(`Git.parseStatus(${repoPath}, v=${porcelainVersion})`, {\n\t\tlog: { onlyExit: true, level: 'debug' },\n\t});\n\tif (!data) {\n\t\tsw?.stop({ suffix: ` no data` });\n\t\treturn undefined;\n\t}\n\n\tconst lines = data.split('\\n').filter(<T>(i?: T): i is T => Boolean(i));\n\tif (lines.length === 0) {\n\t\tsw?.stop({ suffix: ` parsed no files` });\n\t\treturn undefined;\n\t}\n\n\tconst status =\n\t\tporcelainVersion < 2 ? parseStatusV1(lines, repoPath, getUri) : parseStatusV2(lines, repoPath, getUri);\n\n\tsw?.stop({ suffix: ` parsed ${status.files.length} files` });\n\n\treturn status;\n}\n\nfunction parseStatusV1(lines: string[], repoPath: string, getUri: ((path: string) => Uri) | undefined): GitStatus {\n\tlet branch: string | undefined;\n\tconst files = [];\n\tconst state = {\n\t\tahead: 0,\n\t\tbehind: 0,\n\t};\n\tlet upstream;\n\tlet missing = false;\n\n\tlet position = -1;\n\twhile (++position < lines.length) {\n\t\tconst line = lines[position];\n\t\t// Header\n\t\tif (line.startsWith('##')) {\n\t\t\tconst lineParts = line.split(' ');\n\t\t\t[branch, upstream] = lineParts[1].split('...');\n\t\t\tif (lineParts.length > 2) {\n\t\t\t\tconst upstreamStatus = lineParts.slice(2).join(' ');\n\t\t\t\tif (upstreamStatus === '[gone]') {\n\t\t\t\t\tmissing = true;\n\t\t\t\t\tstate.ahead = 0;\n\t\t\t\t\tstate.behind = 0;\n\t\t\t\t} else {\n\t\t\t\t\tconst aheadStatus = aheadStatusV1Regex.exec(upstreamStatus);\n\t\t\t\t\tstate.ahead = aheadStatus == null ? 0 : Number(aheadStatus[1]) || 0;\n\n\t\t\t\t\tconst behindStatus = behindStatusV1Regex.exec(upstreamStatus);\n\t\t\t\t\tstate.behind = behindStatus == null ? 0 : Number(behindStatus[1]) || 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst rawStatus = line.substring(0, 2);\n\t\t\tconst fileName = line.substring(3);\n\t\t\tif (rawStatus.startsWith('R') || rawStatus.startsWith('C')) {\n\t\t\t\tconst [file1, file2] = fileName.replace(quoteRegex, '').split(' -> ');\n\t\t\t\tfiles.push(parseStatusFile(repoPath, rawStatus, file2.trim(), getUri, file1.trim()));\n\t\t\t} else {\n\t\t\t\tfiles.push(parseStatusFile(repoPath, rawStatus, fileName, getUri));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new GitStatus(\n\t\tnormalizePath(repoPath),\n\t\tbranch ?? '',\n\t\t'',\n\t\tfiles,\n\t\tupstream ? { name: upstream, missing: missing, state: state } : undefined,\n\t);\n}\n\nfunction parseStatusV2(lines: string[], repoPath: string, getUri: ((path: string) => Uri) | undefined): GitStatus {\n\tlet branch: string | undefined;\n\tconst files = [];\n\tlet sha: string | undefined;\n\tconst state = {\n\t\tahead: 0,\n\t\tbehind: 0,\n\t};\n\tlet missing = true;\n\tlet upstream;\n\n\tlet position = -1;\n\twhile (++position < lines.length) {\n\t\tconst line = lines[position];\n\t\t// Headers\n\t\tif (line.startsWith('#')) {\n\t\t\tconst lineParts = line.split(' ');\n\t\t\tswitch (lineParts[1]) {\n\t\t\t\tcase 'branch.oid':\n\t\t\t\t\tsha = lineParts[2];\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'branch.head':\n\t\t\t\t\tbranch = lineParts[2];\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'branch.upstream':\n\t\t\t\t\tupstream = lineParts[2];\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'branch.ab':\n\t\t\t\t\tmissing = false;\n\t\t\t\t\tstate.ahead = Number(lineParts[2].substring(1));\n\t\t\t\t\tstate.behind = Number(lineParts[3].substring(1));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (line[0]) {\n\t\t\t\tcase '1': {\n\t\t\t\t\t// normal: 1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>\n\t\t\t\t\tconst pathStart = indexAfterNthSpace(line, 8);\n\t\t\t\t\tif (pathStart === -1) break;\n\n\t\t\t\t\t// <sub> starts with 'S' if submodule, 'N' if not\n\t\t\t\t\tlet submodule;\n\t\t\t\t\tif (line.charCodeAt(5) === submoduleCharCode) {\n\t\t\t\t\t\tconst parts = line.split(' ');\n\t\t\t\t\t\tsubmodule = { oid: parts[7], previousOid: parts[6] };\n\t\t\t\t\t}\n\n\t\t\t\t\tfiles.push(\n\t\t\t\t\t\tparseStatusFile(\n\t\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\t\tline.substring(2, 4),\n\t\t\t\t\t\t\tline.substring(pathStart),\n\t\t\t\t\t\t\tgetUri,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tsubmodule,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase '2': {\n\t\t\t\t\t// rename: 2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path>\\t<origPath>\n\t\t\t\t\tconst pathStart = indexAfterNthSpace(line, 9);\n\t\t\t\t\tif (pathStart === -1) break;\n\n\t\t\t\t\tlet submodule;\n\t\t\t\t\tif (line.charCodeAt(5) === submoduleCharCode) {\n\t\t\t\t\t\tconst parts = line.split(' ');\n\t\t\t\t\t\tsubmodule = { oid: parts[7], previousOid: parts[6] };\n\t\t\t\t\t}\n\n\t\t\t\t\tconst tab = line.indexOf('\\t', pathStart);\n\t\t\t\t\tfiles.push(\n\t\t\t\t\t\tparseStatusFile(\n\t\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\t\tline.substring(2, 4),\n\t\t\t\t\t\t\ttab === -1 ? line.substring(pathStart) : line.substring(pathStart, tab),\n\t\t\t\t\t\t\tgetUri,\n\t\t\t\t\t\t\ttab === -1 ? undefined : line.substring(tab + 1),\n\t\t\t\t\t\t\tsubmodule,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'u': {\n\t\t\t\t\t// unmerged: u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>\n\t\t\t\t\tconst pathStart = indexAfterNthSpace(line, 10);\n\t\t\t\t\tif (pathStart === -1) break;\n\n\t\t\t\t\tlet submodule;\n\t\t\t\t\tif (line.charCodeAt(5) === submoduleCharCode) {\n\t\t\t\t\t\tconst parts = line.split(' ');\n\t\t\t\t\t\tsubmodule = { oid: parts[9], previousOid: parts[7] };\n\t\t\t\t\t}\n\n\t\t\t\t\tfiles.push(\n\t\t\t\t\t\tparseStatusFile(\n\t\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\t\tline.substring(2, 4),\n\t\t\t\t\t\t\tline.substring(pathStart),\n\t\t\t\t\t\t\tgetUri,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tsubmodule,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase '?': // untracked\n\t\t\t\t\tfiles.push(parseStatusFile(repoPath, '??', line.substring(2), getUri));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new GitStatus(\n\t\tnormalizePath(repoPath),\n\t\tbranch ?? '',\n\t\tsha ?? '',\n\t\tfiles,\n\t\tupstream ? { name: upstream, missing: missing, state: state } : undefined,\n\t);\n}\n\nfunction parseStatusFile(\n\trepoPath: string,\n\trawStatus: string,\n\tfileName: string,\n\tgetUri: ((path: string) => Uri) | undefined,\n\toriginalFileName?: string,\n\tsubmodule?: { readonly oid: string; readonly previousOid?: string },\n): GitStatusFile {\n\tlet x = !rawStatus.startsWith('.') ? rawStatus[0].trim() : undefined;\n\tif (x == null || x.length === 0) {\n\t\tx = undefined;\n\t}\n\n\tlet y = undefined;\n\tif (rawStatus.length > 1) {\n\t\ty = rawStatus[1] !== '.' ? rawStatus[1].trim() : undefined;\n\t\tif (y == null || y.length === 0) {\n\t\t\ty = undefined;\n\t\t}\n\t}\n\n\treturn new GitStatusFile(repoPath, x, y, fileName, getUri?.(fileName), originalFileName, submodule);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"statusParser.js","names":[],"sources":["../../../../git-cli/src/parsers/statusParser.ts"],"sourcesContent":["import { GitStatus } from '@gitlens/git/models/status.js';\nimport { GitStatusFile } from '@gitlens/git/models/statusFile.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\n\nconst aheadStatusV1Regex = /(?:ahead ([0-9]+))/;\nconst behindStatusV1Regex = /(?:behind ([0-9]+))/;\nconst quoteRegex = /\"/g;\nconst submoduleCharCode = 'S'.charCodeAt(0);\n\n/** Returns the index just after the `n`th space, or -1 if the line has fewer than `n` spaces. */\nfunction indexAfterNthSpace(line: string, n: number): number {\n\tlet index = -1;\n\tfor (let i = 0; i < n; i++) {\n\t\tindex = line.indexOf(' ', index + 1);\n\t\tif (index === -1) return -1;\n\t}\n\n\treturn index + 1;\n}\n\n/** Without `getUri`, each file builds its uri on first read rather than during the parse */\nexport function parseGitStatus(\n\tdata: string,\n\trepoPath: string,\n\tporcelainVersion: number,\n\tgetUri?: (path: string) => Uri,\n): GitStatus | undefined {\n\tusing sw = maybeStopWatch(`Git.parseStatus(${repoPath}, v=${porcelainVersion})`, {\n\t\tlog: { onlyExit: true, level: 'debug' },\n\t});\n\tif (!data) {\n\t\tsw?.stop({ suffix: ` no data` });\n\t\treturn undefined;\n\t}\n\n\tconst lines = data.split('\\n').filter(<T>(i?: T): i is T => Boolean(i));\n\tif (lines.length === 0) {\n\t\tsw?.stop({ suffix: ` parsed no files` });\n\t\treturn undefined;\n\t}\n\n\tconst status =\n\t\tporcelainVersion < 2 ? parseStatusV1(lines, repoPath, getUri) : parseStatusV2(lines, repoPath, getUri);\n\n\tsw?.stop({ suffix: ` parsed ${status.files.length} files` });\n\n\treturn status;\n}\n\nfunction parseStatusV1(lines: string[], repoPath: string, getUri: ((path: string) => Uri) | undefined): GitStatus {\n\tlet branch: string | undefined;\n\tconst files = [];\n\tconst state = {\n\t\tahead: 0,\n\t\tbehind: 0,\n\t};\n\tlet upstream;\n\tlet missing = false;\n\n\tlet position = -1;\n\twhile (++position < lines.length) {\n\t\tconst line = lines[position];\n\t\t// Header\n\t\tif (line.startsWith('##')) {\n\t\t\tconst lineParts = line.split(' ');\n\t\t\t[branch, upstream] = lineParts[1].split('...');\n\t\t\tif (lineParts.length > 2) {\n\t\t\t\tconst upstreamStatus = lineParts.slice(2).join(' ');\n\t\t\t\tif (upstreamStatus === '[gone]') {\n\t\t\t\t\tmissing = true;\n\t\t\t\t\tstate.ahead = 0;\n\t\t\t\t\tstate.behind = 0;\n\t\t\t\t} else {\n\t\t\t\t\tconst aheadStatus = aheadStatusV1Regex.exec(upstreamStatus);\n\t\t\t\t\tstate.ahead = aheadStatus == null ? 0 : Number(aheadStatus[1]) || 0;\n\n\t\t\t\t\tconst behindStatus = behindStatusV1Regex.exec(upstreamStatus);\n\t\t\t\t\tstate.behind = behindStatus == null ? 0 : Number(behindStatus[1]) || 0;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst rawStatus = line.substring(0, 2);\n\t\t\tconst fileName = line.substring(3);\n\t\t\tif (rawStatus.startsWith('R') || rawStatus.startsWith('C')) {\n\t\t\t\tconst [file1, file2] = fileName.replace(quoteRegex, '').split(' -> ');\n\t\t\t\tfiles.push(parseStatusFile(repoPath, rawStatus, file2.trim(), getUri, file1.trim()));\n\t\t\t} else {\n\t\t\t\tfiles.push(parseStatusFile(repoPath, rawStatus, fileName, getUri));\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new GitStatus(\n\t\tnormalizePath(repoPath),\n\t\tbranch ?? '',\n\t\t'',\n\t\tfiles,\n\t\tupstream ? { name: upstream, missing: missing, state: state } : undefined,\n\t);\n}\n\nfunction parseStatusV2(lines: string[], repoPath: string, getUri: ((path: string) => Uri) | undefined): GitStatus {\n\tlet branch: string | undefined;\n\tconst files = [];\n\tlet sha: string | undefined;\n\tconst state = {\n\t\tahead: 0,\n\t\tbehind: 0,\n\t};\n\tlet missing = true;\n\tlet upstream;\n\n\tlet position = -1;\n\twhile (++position < lines.length) {\n\t\tconst line = lines[position];\n\t\t// Headers\n\t\tif (line.startsWith('#')) {\n\t\t\tconst lineParts = line.split(' ');\n\t\t\tswitch (lineParts[1]) {\n\t\t\t\tcase 'branch.oid':\n\t\t\t\t\tsha = lineParts[2];\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'branch.head':\n\t\t\t\t\tbranch = lineParts[2];\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'branch.upstream':\n\t\t\t\t\tupstream = lineParts[2];\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'branch.ab':\n\t\t\t\t\tmissing = false;\n\t\t\t\t\tstate.ahead = Number(lineParts[2].substring(1));\n\t\t\t\t\tstate.behind = Number(lineParts[3].substring(1));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t} else {\n\t\t\tswitch (line[0]) {\n\t\t\t\tcase '1': {\n\t\t\t\t\t// normal: 1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>\n\t\t\t\t\tconst pathStart = indexAfterNthSpace(line, 8);\n\t\t\t\t\tif (pathStart === -1) break;\n\n\t\t\t\t\t// <sub> starts with 'S' if submodule, 'N' if not\n\t\t\t\t\tlet submodule;\n\t\t\t\t\tif (line.charCodeAt(5) === submoduleCharCode) {\n\t\t\t\t\t\tconst parts = line.split(' ');\n\t\t\t\t\t\tsubmodule = { oid: parts[7], previousOid: parts[6] };\n\t\t\t\t\t}\n\n\t\t\t\t\tfiles.push(\n\t\t\t\t\t\tparseStatusFile(\n\t\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\t\tline.substring(2, 4),\n\t\t\t\t\t\t\tline.substring(pathStart),\n\t\t\t\t\t\t\tgetUri,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tsubmodule,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase '2': {\n\t\t\t\t\t// rename: 2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path>\\t<origPath>\n\t\t\t\t\tconst pathStart = indexAfterNthSpace(line, 9);\n\t\t\t\t\tif (pathStart === -1) break;\n\n\t\t\t\t\tlet submodule;\n\t\t\t\t\tif (line.charCodeAt(5) === submoduleCharCode) {\n\t\t\t\t\t\tconst parts = line.split(' ');\n\t\t\t\t\t\tsubmodule = { oid: parts[7], previousOid: parts[6] };\n\t\t\t\t\t}\n\n\t\t\t\t\tconst tab = line.indexOf('\\t', pathStart);\n\t\t\t\t\tfiles.push(\n\t\t\t\t\t\tparseStatusFile(\n\t\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\t\tline.substring(2, 4),\n\t\t\t\t\t\t\ttab === -1 ? line.substring(pathStart) : line.substring(pathStart, tab),\n\t\t\t\t\t\t\tgetUri,\n\t\t\t\t\t\t\ttab === -1 ? undefined : line.substring(tab + 1),\n\t\t\t\t\t\t\tsubmodule,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase 'u': {\n\t\t\t\t\t// unmerged: u <XY> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>\n\t\t\t\t\tconst pathStart = indexAfterNthSpace(line, 10);\n\t\t\t\t\tif (pathStart === -1) break;\n\n\t\t\t\t\tlet submodule;\n\t\t\t\t\tif (line.charCodeAt(5) === submoduleCharCode) {\n\t\t\t\t\t\tconst parts = line.split(' ');\n\t\t\t\t\t\tsubmodule = { oid: parts[9], previousOid: parts[7] };\n\t\t\t\t\t}\n\n\t\t\t\t\tfiles.push(\n\t\t\t\t\t\tparseStatusFile(\n\t\t\t\t\t\t\trepoPath,\n\t\t\t\t\t\t\tline.substring(2, 4),\n\t\t\t\t\t\t\tline.substring(pathStart),\n\t\t\t\t\t\t\tgetUri,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\tsubmodule,\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcase '?': // untracked\n\t\t\t\t\tfiles.push(parseStatusFile(repoPath, '??', line.substring(2), getUri));\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new GitStatus(\n\t\tnormalizePath(repoPath),\n\t\tbranch ?? '',\n\t\tsha ?? '',\n\t\tfiles,\n\t\tupstream ? { name: upstream, missing: missing, state: state } : undefined,\n\t);\n}\n\nfunction parseStatusFile(\n\trepoPath: string,\n\trawStatus: string,\n\tfileName: string,\n\tgetUri: ((path: string) => Uri) | undefined,\n\toriginalFileName?: string,\n\tsubmodule?: { readonly oid: string; readonly previousOid?: string },\n): GitStatusFile {\n\tlet x = !rawStatus.startsWith('.') ? rawStatus[0].trim() : undefined;\n\tif (x == null || x.length === 0) {\n\t\tx = undefined;\n\t}\n\n\tlet y = undefined;\n\tif (rawStatus.length > 1) {\n\t\ty = rawStatus[1] !== '.' ? rawStatus[1].trim() : undefined;\n\t\tif (y == null || y.length === 0) {\n\t\t\ty = undefined;\n\t\t}\n\t}\n\n\treturn new GitStatusFile(repoPath, x, y, fileName, getUri?.(fileName), originalFileName, submodule);\n}\n\n/**\n * Folds `status --ignored=matching --untracked-files=all` records of new untracked folders into the entries that\n * `--ignored=traditional --untracked-files=normal` reports. Inside a new untracked folder nothing is tracked, so a\n * folder holding no untracked content is wholly ignored, and is reported in place of the ignored paths beneath it.\n */\nexport function foldMatchingIgnored(records: Iterable<string>): string[] {\n\tconst dirty = new Set<string>();\n\tconst ignored: string[] = [];\n\n\tfor (const record of records) {\n\t\tif (record.length < 4) continue;\n\n\t\tif (record.startsWith('!! ')) {\n\t\t\tignored.push(record.substring(3));\n\t\t} else if (record.startsWith('?? ')) {\n\t\t\tconst path = record.substring(3);\n\t\t\tfor (let i = path.indexOf('/'); i !== -1; i = path.indexOf('/', i + 1)) {\n\t\t\t\tdirty.add(path.substring(0, i + 1));\n\t\t\t}\n\t\t}\n\t}\n\n\tconst entries = new Set<string>();\n\tfor (const path of ignored) {\n\t\tlet entry = path;\n\t\tfor (let i = path.indexOf('/'); i !== -1 && i + 1 < path.length; i = path.indexOf('/', i + 1)) {\n\t\t\tconst folder = path.substring(0, i + 1);\n\t\t\tif (!dirty.has(folder)) {\n\t\t\t\tentry = folder;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tentries.add(entry);\n\t}\n\treturn [...entries];\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.\n\t\t\t// Reasons with unusual characters (e.g. newlines) come through C-quoted, per `core.quotePath`\n\t\t\tcase 'locked':\n\t\t\t\tentry.locked = value.trim() || true;\n\t\t\t\tbreak;\n\t\t\tcase 'prunable':\n\t\t\t\tentry.prunable = 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"],"mappings":";;;;;;;AAkBA,SAAgB,kBACf,MACA,UACA,UACA,oBACgB;;;EAChB,MAAM,KAAA,YAAA,EAAK,eAAe,sBAAsB,SAAS,IAAI,EAAE,KAAK;GAAE,UAAU;GAAM,OAAO;EAAQ,EAAE,CAAC,CAAA;EAExG,MAAM,YAA2B,CAAC;EAClC,IAAI,CAAC,MAAM;GACV,IAAI,KAAK,EAAE,QAAQ,WAAW,CAAC;GAC/B,OAAO;EACR;EAEA,IAAI,YAAY,MACf,WAAW,cAAc,QAAQ;EAGlC,IAAI,QAA4C,KAAA;EAChD,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,OAAO;EAEX,KAAK,QAAQ,mBAAmB,MAAM,IAAI,GAAG;GAC5C,QAAQ,KAAK,QAAQ,GAAG;GACxB,IAAI,UAAU,IAAI;IACjB,MAAM;IACN,QAAQ;GACT,OAAO;IACN,MAAM,KAAK,UAAU,GAAG,KAAK;IAC7B,QAAQ,KAAK,UAAU,QAAQ,CAAC;GACjC;GAEA,IAAI,CAAC,OAAO,SAAS,MAAM;IAE1B,MAAM,SAAS,MAAM,SAAS,UAAU,MAAK,MAAK,EAAE,SAAS,MAAO,MAAM,IAAI,KAAA;IAC9E,MAAM,MAAM,QAAQ,MAAM,IAAK;IAE/B,UAAU,KACT,IAAI,YACH,MACA,MAAM,OAAO,SAAS,MAAM,WAAW,aAAa,UACpD,UACA,KACA,MAAM,UAAU,OAChB,MAAM,YAAY,OAClB,MAAM,KACN,QACA,qBAAqB,GAAG,CACzB,CACD;IAEA,QAAQ,KAAA;IACR,OAAO;IACP;GACD;GAEA,UAAU,CAAC;GAEX,QAAQ,KAAR;IACC,KAAK;KACJ,MAAM,OAAO;KACb;IACD,KAAK;KACJ,MAAM,OAAO;KACb;IACD,KAAK;KACJ,MAAM,MAAM;KACZ;IACD,KAAK;KAEJ,MAAM,SAAS,MAAM,UAAU,EAAE;KACjC;IACD,KAAK;KACJ,MAAM,WAAW;KACjB;IAGD,KAAK;KACJ,MAAM,SAAS,MAAM,KAAK,KAAK;KAC/B;IACD,KAAK,YACJ,MAAM,WAAW,MAAM,KAAK,KAAK;GAEnC;EACD;EAEA,IAAI,KAAK,EAAE,QAAQ,WAAW,UAAU,OAAO,YAAY,CAAC;EAE5D,OAAO;;;;;;AACR"}
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":";;;;;;;AAkBA,SAAgB,kBACf,MACA,UACA,UACA,oBACgB;;;EAChB,MAAM,KAAA,YAAA,EAAK,eAAe,sBAAsB,SAAS,IAAI,EAAE,KAAK;GAAE,UAAU;GAAM,OAAO;EAAQ,EAAE,CAAC,CAAA;EAExG,MAAM,YAA2B,CAAC;EAClC,IAAI,CAAC,MAAM;GACV,IAAI,KAAK,EAAE,QAAQ,WAAW,CAAC;GAC/B,OAAO;EACR;EAEA,IAAI,YAAY,MACf,WAAW,cAAc,QAAQ;EAGlC,IAAI,QAA4C,KAAA;EAChD,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,OAAO;EAEX,KAAK,QAAQ,mBAAmB,MAAM,IAAI,GAAG;GAC5C,QAAQ,KAAK,QAAQ,GAAG;GACxB,IAAI,UAAU,IAAI;IACjB,MAAM;IACN,QAAQ;GACT,OAAO;IACN,MAAM,KAAK,UAAU,GAAG,KAAK;IAC7B,QAAQ,KAAK,UAAU,QAAQ,CAAC;GACjC;GAEA,IAAI,CAAC,OAAO,SAAS,MAAM;IAE1B,MAAM,SAAS,MAAM,SAAS,UAAU,MAAK,MAAK,EAAE,SAAS,MAAO,MAAM,IAAI,KAAA;IAC9E,MAAM,MAAM,QAAQ,MAAM,IAAK;IAE/B,UAAU,KACT,IAAI,YACH,MACA,MAAM,OAAO,SAAS,MAAM,WAAW,aAAa,UACpD,UACA,KACA,MAAM,UAAU,OAChB,MAAM,YAAY,OAClB,MAAM,KACN,QACA,qBAAqB,GAAG,CACzB,CACD;IAEA,QAAQ,KAAA;IACR,OAAO;IACP;GACD;GAEA,UAAU,CAAC;GAEX,QAAQ,KAAR;IACC,KAAK;KACJ,MAAM,OAAO;KACb;IACD,KAAK;KACJ,MAAM,OAAO;KACb;IACD,KAAK;KACJ,MAAM,MAAM;KACZ;IACD,KAAK;KAEJ,MAAM,SAAS,MAAM,UAAU,EAAE;KACjC;IACD,KAAK;KACJ,MAAM,WAAW;KACjB;IAID,KAAK;KACJ,MAAM,SAAS,eAAe,MAAM,KAAK,CAAC,KAAK;KAC/C;IACD,KAAK,YACJ,MAAM,WAAW,eAAe,MAAM,KAAK,CAAC,KAAK;GAEnD;EACD;EAEA,IAAI,KAAK,EAAE,QAAQ,WAAW,UAAU,OAAO,YAAY,CAAC;EAE5D,OAAO;;;;;;AACR;AAEA,MAAM,WAAmC;CAAE,GAAG;CAAG,GAAG;CAAG,GAAG;CAAI,GAAG;CAAI,GAAG;CAAI,GAAG;CAAG,GAAG;CAAI,MAAM;CAAI,MAAK;AAAG;;AAG3G,SAAS,eAAe,OAAuB;CAC9C,IAAI,CAAC,MAAM,WAAW,IAAG,GAAG,OAAO;CAEnC,MAAM,MAAM,MAAM,SAAS,KAAK,MAAM,SAAS,IAAG,IAAI,MAAM,SAAS,IAAI,MAAM;CAC/E,MAAM,UAAU,IAAI,YAAY;CAChC,MAAM,QAAkB,CAAC;CAEzB,IAAI,IAAI;CACR,OAAO,IAAI,KAAK;EAEf,IADW,MAAM,OACN,QAAQ,IAAI,KAAK,KAAK;GAEhC,MAAM,KAAK,OAAO,cAAc,MAAM,YAAY,CAAC,CAAE;GACrD,MAAM,KAAK,GAAG,QAAQ,OAAO,EAAE,CAAC;GAChC,KAAK,GAAG;GACR;EACD;EAEA,MAAM,OAAO,MAAM,IAAI;EACvB,IAAI,QAAQ,OAAO,QAAQ,KAAK;GAC/B,IAAI,IAAI,IAAI;GACZ,OAAO,IAAI,IAAI,KAAK,IAAI,OAAO,MAAM,MAAM,OAAO,MAAM,MAAM,KAC7D;GAED,MAAM,KAAK,OAAO,SAAS,MAAM,UAAU,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,GAAI;GAC/D,IAAI;GACJ;EACD;EAEA,MAAM,OAAO,SAAS;EACtB,IAAI,QAAQ,MACX,MAAM,KAAK,IAAI;OAEf,MAAM,KAAK,GAAG,QAAQ,OAAO,IAAI,CAAC;EAEnC,KAAK;CACN;CAEA,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI,WAAW,KAAK,CAAC;AACtD"}
@@ -0,0 +1,14 @@
1
+ import { GitBranchReference } from "../../git/models/reference.js";
2
+ import { CliGitProviderInternal } from "../cliGitProvider.js";
3
+ //#region ../git-cli/src/providers/branchRemote.d.ts
4
+ /**
5
+ * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain
6
+ * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.
7
+ */
8
+ export declare function resolveBranchRemote(provider: Pick<CliGitProviderInternal, 'remotes'>, repoPath: string, ref: GitBranchReference, cancellation?: AbortSignal): Promise<{
9
+ name: string;
10
+ remote: string | undefined;
11
+ upstream: string | undefined;
12
+ }>;
13
+ //#endregion
14
+ //# sourceMappingURL=branchRemote.d.ts.map
@@ -0,0 +1,36 @@
1
+ import { getBranchNameAndRemote, getBranchTrackingWithoutRemote, splitRemoteBranchName } from "../../git/utils/branch.utils.js";
2
+ //#region ../git-cli/src/providers/branchRemote.ts
3
+ /**
4
+ * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain
5
+ * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.
6
+ */
7
+ async function resolveBranchRemote(provider, repoPath, ref, cancellation) {
8
+ let [name, remote] = getBranchNameAndRemote(ref);
9
+ let upstream = getBranchTrackingWithoutRemote(ref);
10
+ if (ref.upstream?.remote || !(ref.remote ? name : upstream)?.includes("/")) return {
11
+ name,
12
+ remote,
13
+ upstream
14
+ };
15
+ const remoteNames = (await provider.remotes.getRemotes(repoPath, void 0, cancellation)).map((r) => r.name);
16
+ if (ref.remote) {
17
+ const split = splitRemoteBranchName(ref.name, remoteNames);
18
+ if (split != null) [remote, name] = split;
19
+ }
20
+ if (ref.upstream != null) {
21
+ const split = splitRemoteBranchName(ref.upstream.name, remoteNames);
22
+ if (split != null) {
23
+ if (!ref.remote) remote = split[0];
24
+ upstream = split[1];
25
+ }
26
+ }
27
+ return {
28
+ name,
29
+ remote,
30
+ upstream
31
+ };
32
+ }
33
+ //#endregion
34
+ export { resolveBranchRemote };
35
+
36
+ //# sourceMappingURL=branchRemote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branchRemote.js","names":[],"sources":["../../../../git-cli/src/providers/branchRemote.ts"],"sourcesContent":["import type { GitBranchReference } from '@gitlens/git/models/reference.js';\nimport {\n\tgetBranchNameAndRemote,\n\tgetBranchTrackingWithoutRemote,\n\tsplitRemoteBranchName,\n} from '@gitlens/git/utils/branch.utils.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\n\n/**\n * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain\n * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.\n */\nexport async function resolveBranchRemote(\n\tprovider: Pick<CliGitProviderInternal, 'remotes'>,\n\trepoPath: string,\n\tref: GitBranchReference,\n\tcancellation?: AbortSignal,\n): Promise<{ name: string; remote: string | undefined; upstream: string | undefined }> {\n\tlet [name, remote] = getBranchNameAndRemote(ref);\n\tlet upstream = getBranchTrackingWithoutRemote(ref);\n\n\tif (ref.upstream?.remote || !(ref.remote ? name : upstream)?.includes('/')) {\n\t\treturn { name: name, remote: remote, upstream: upstream };\n\t}\n\n\tconst remoteNames = (await provider.remotes.getRemotes(repoPath, undefined, cancellation)).map(r => r.name);\n\n\tif (ref.remote) {\n\t\tconst split = splitRemoteBranchName(ref.name, remoteNames);\n\t\tif (split != null) {\n\t\t\t[remote, name] = split;\n\t\t}\n\t}\n\n\tif (ref.upstream != null) {\n\t\tconst split = splitRemoteBranchName(ref.upstream.name, remoteNames);\n\t\tif (split != null) {\n\t\t\tif (!ref.remote) {\n\t\t\t\tremote = split[0];\n\t\t\t}\n\t\t\tupstream = split[1];\n\t\t}\n\t}\n\n\treturn { name: name, remote: remote, upstream: upstream };\n}\n"],"mappings":";;;;;;AAYA,eAAsB,oBACrB,UACA,UACA,KACA,cACsF;CACtF,IAAI,CAAC,MAAM,UAAU,uBAAuB,GAAG;CAC/C,IAAI,WAAW,+BAA+B,GAAG;CAEjD,IAAI,IAAI,UAAU,UAAU,EAAE,IAAI,SAAS,OAAO,SAAA,EAAW,SAAS,GAAG,GACxE,OAAO;EAAQ;EAAc;EAAkB;CAAS;CAGzD,MAAM,eAAe,MAAM,SAAS,QAAQ,WAAW,UAAU,KAAA,GAAW,YAAY,EAAA,CAAG,KAAI,MAAK,EAAE,IAAI;CAE1G,IAAI,IAAI,QAAQ;EACf,MAAM,QAAQ,sBAAsB,IAAI,MAAM,WAAW;EACzD,IAAI,SAAS,MACZ,CAAC,QAAQ,QAAQ;CAEnB;CAEA,IAAI,IAAI,YAAY,MAAM;EACzB,MAAM,QAAQ,sBAAsB,IAAI,SAAS,MAAM,WAAW;EAClE,IAAI,SAAS,MAAM;GAClB,IAAI,CAAC,IAAI,QACR,SAAS,MAAM;GAEhB,WAAW,MAAM;EAClB;CACD;CAEA,OAAO;EAAQ;EAAc;EAAkB;CAAS;AACzD"}