@gitkraken/core-gitlens 0.16.5 → 0.17.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 (63) hide show
  1. package/CHANGELOG.md +32 -1
  2. package/dist/git/cache.d.ts +8 -2
  3. package/dist/git/cache.js +15 -6
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/models/repository.d.ts +4 -2
  6. package/dist/git/models/repository.js +1 -0
  7. package/dist/git/models/repository.js.map +1 -1
  8. package/dist/git/models/worktree.d.ts +5 -1
  9. package/dist/git/models/worktree.js +5 -3
  10. package/dist/git/models/worktree.js.map +1 -1
  11. package/dist/git/providers/provider.d.ts +17 -0
  12. package/dist/git/providers/worktrees.d.ts +17 -2
  13. package/dist/git/run.types.d.ts +7 -0
  14. package/dist/git/service.d.ts +5 -2
  15. package/dist/git/service.js +11 -5
  16. package/dist/git/service.js.map +1 -1
  17. package/dist/git/watching/classifyChange.d.ts +4 -3
  18. package/dist/git/watching/classifyChange.js +7 -4
  19. package/dist/git/watching/classifyChange.js.map +1 -1
  20. package/dist/git/watching/provider.d.ts +6 -0
  21. package/dist/git/watching/watchGroup.d.ts +142 -3
  22. package/dist/git/watching/watchGroup.js +424 -19
  23. package/dist/git/watching/watchGroup.js.map +1 -1
  24. package/dist/git/watching/watchService.d.ts +25 -0
  25. package/dist/git/watching/watchService.js +27 -7
  26. package/dist/git/watching/watchService.js.map +1 -1
  27. package/dist/git/watching/watchSession.d.ts +13 -8
  28. package/dist/git/watching/watchSession.js +50 -10
  29. package/dist/git/watching/watchSession.js.map +1 -1
  30. package/dist/git/watching/watcherPatterns.d.ts +14 -0
  31. package/dist/git/watching/watcherPatterns.js +38 -1
  32. package/dist/git/watching/watcherPatterns.js.map +1 -1
  33. package/dist/git/watching/worktreeSummary.d.ts +64 -2
  34. package/dist/git/watching/worktreeSummary.js +326 -13
  35. package/dist/git/watching/worktreeSummary.js.map +1 -1
  36. package/dist/git-cli/cliGitProvider.d.ts +26 -0
  37. package/dist/git-cli/cliGitProvider.js +66 -5
  38. package/dist/git-cli/cliGitProvider.js.map +1 -1
  39. package/dist/git-cli/exec/commandClassifier.d.ts +9 -3
  40. package/dist/git-cli/exec/commandClassifier.js +98 -17
  41. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  42. package/dist/git-cli/exec/git.d.ts +20 -0
  43. package/dist/git-cli/exec/git.js +69 -7
  44. package/dist/git-cli/exec/git.js.map +1 -1
  45. package/dist/git-cli/exec/headState.d.ts +5 -0
  46. package/dist/git-cli/exec/headState.js +14 -1
  47. package/dist/git-cli/exec/headState.js.map +1 -1
  48. package/dist/git-cli/parsers/worktreeParser.js +2 -2
  49. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  50. package/dist/git-cli/providers/branches.js +8 -4
  51. package/dist/git-cli/providers/branches.js.map +1 -1
  52. package/dist/git-cli/providers/graph.js +1 -1
  53. package/dist/git-cli/providers/graph.js.map +1 -1
  54. package/dist/git-cli/providers/operations.d.ts +3 -0
  55. package/dist/git-cli/providers/operations.js +14 -5
  56. package/dist/git-cli/providers/operations.js.map +1 -1
  57. package/dist/git-cli/providers/worktrees.d.ts +25 -3
  58. package/dist/git-cli/providers/worktrees.js +125 -32
  59. package/dist/git-cli/providers/worktrees.js.map +1 -1
  60. package/dist/git-cli/service.d.ts +2 -0
  61. package/dist/git-cli/service.js +4 -2
  62. package/dist/git-cli/service.js.map +1 -1
  63. package/package.json +1 -1
@@ -3,7 +3,7 @@ import { getScopedLogger } from "../../utils/logger.scoped.js";
3
3
  import { getSettledValue } from "../../utils/promise.js";
4
4
  import { debug } from "../../utils/decorators/log.js";
5
5
  import { fileUri, toFsPath } from "../../utils/uri.js";
6
- import { basename as basename$1, normalizePath } from "../../utils/path.js";
6
+ import { basename as basename$1, isDescendant, normalizePath } from "../../utils/path.js";
7
7
  import __decorate from "../../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
8
8
  import { WorktreeCreateError, WorktreeDeleteError } from "../../git/errors.js";
9
9
  import { mayWriteTracking } from "../exec/commandClassifier.js";
@@ -27,16 +27,62 @@ function getLockReason(ex) {
27
27
  function getNewWorktreeBranch(stderr) {
28
28
  return stderr ? /^Preparing worktree \(new branch '(.+)'\)$/m.exec(stderr)?.[1] : void 0;
29
29
  }
30
+ const unbornSha = /^0+$/;
31
+ /**
32
+ * Where `git worktree list` contradicts the branch list it was paired with, or `undefined` when they agree. `switched`
33
+ * holds the worktrees whose checked-out branch differs from the one the branch list places there.
34
+ */
35
+ function findStaleBranches(worktrees, branches, knowsPaths) {
36
+ if (!worktrees.length) return void 0;
37
+ const byName = /* @__PURE__ */ new Map();
38
+ const byPath = /* @__PURE__ */ new Map();
39
+ for (const b of branches) {
40
+ if (b.remote || b.detached) continue;
41
+ byName.set(b.name, b);
42
+ if (b.worktree && !b.rebasing) byPath.set(normalizePath(b.worktree.path), b);
43
+ }
44
+ const listedBranches = new Map(worktrees.map((w) => [w.path, w.branchName]));
45
+ let stale = false;
46
+ const switched = [];
47
+ for (const w of worktrees) {
48
+ if (w.type === "bare") {
49
+ byPath.delete(w.path);
50
+ continue;
51
+ }
52
+ const listed = byPath.get(w.path);
53
+ if (listed != null) {
54
+ byPath.delete(w.path);
55
+ if (listed.name !== w.branchName) {
56
+ switched.push(w.path);
57
+ stale = true;
58
+ }
59
+ } else if (knowsPaths && w.branchName != null) switched.push(w.path);
60
+ if (w.branchName == null || !w.sha || unbornSha.test(w.sha)) continue;
61
+ const branch = byName.get(w.branchName);
62
+ if (branch == null || branch.sha != null && branch.sha !== w.sha || branch.worktree === false || branch.worktree != null && listedBranches.get(normalizePath(branch.worktree.path)) !== w.branchName) stale = true;
63
+ }
64
+ if (byPath.size) stale = true;
65
+ return stale ? { switched } : void 0;
66
+ }
30
67
  var WorktreesGitSubProvider = class {
31
68
  context;
32
69
  git;
33
70
  cache;
34
71
  provider;
35
- constructor(context, git, cache, provider) {
72
+ readIgnoredWorktreeRoots;
73
+ /** Per common path, the read whose `git worktree list` hasn't started yet */
74
+ _unspawnedReads = /* @__PURE__ */ new Map();
75
+ /**
76
+ * Per common path, the `git worktree list` output a fresh branch list still contradicted: a layout where git's two
77
+ * listings never agree, which re-reading the branches again can't fix until the list changes
78
+ */
79
+ _lastingDisagreements = /* @__PURE__ */ new Map();
80
+ constructor(context, git, cache, provider, readIgnoredWorktreeRoots) {
36
81
  this.context = context;
37
82
  this.git = git;
38
83
  this.cache = cache;
39
84
  this.provider = provider;
85
+ this.readIgnoredWorktreeRoots = readIgnoredWorktreeRoots;
40
86
  }
41
87
  /**
42
88
  * Creates a worktree (`git worktree add`). `runOptions` are spread into the underlying `git.run` call, so a
@@ -81,45 +127,92 @@ var WorktreesGitSubProvider = class {
81
127
  "config",
82
128
  "worktrees"
83
129
  ] : ["branches", "worktrees"]);
84
- this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees", "heads"], [`refs/heads/${createdBranch}`]);
130
+ this.context.hooks?.repository?.onChanged?.(repoPath, [
131
+ "worktrees",
132
+ "worktreeHead",
133
+ "heads"
134
+ ], [`refs/heads/${createdBranch}`]);
85
135
  } else if (mayHaveCreatedBranch) {
86
136
  this.context.hooks?.cache?.onReset?.(repoPath, "branches", "config", "worktrees");
87
- this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees", "heads"]);
137
+ this.context.hooks?.repository?.onChanged?.(repoPath, [
138
+ "worktrees",
139
+ "worktreeHead",
140
+ "heads"
141
+ ]);
88
142
  } else {
89
143
  this.context.hooks?.cache?.onReset?.(repoPath, "worktrees");
90
- this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees"]);
144
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees", "worktreeHead"]);
91
145
  }
92
146
  }
93
147
  }
94
148
  async createWorktreeWithResult(repoPath, path, options, runOptions) {
95
149
  await this.createWorktree(repoPath, path, options, runOptions);
96
150
  const normalized = normalizePath(path);
97
- return this.getWorktree(repoPath, (w) => normalizePath(w.path) === normalized, runOptions?.cancellation);
151
+ return this.getWorktree(repoPath, (w) => normalizePath(w.path) === normalized, void 0, runOptions?.cancellation);
98
152
  }
99
- async getWorktree(repoPath, predicate, cancellation) {
100
- return (await this.getWorktrees(repoPath, cancellation)).find(predicate);
153
+ async getWorktree(repoPath, predicate, options, cancellation) {
154
+ return (await this.getWorktrees(repoPath, options, cancellation)).find(predicate);
101
155
  }
102
- async getWorktrees(repoPath, cancellation) {
156
+ async getWorktrees(repoPath, options, cancellation) {
157
+ let force = options?.force === true && !this._unspawnedReads.has(this.cache.getCommonPath(repoPath));
103
158
  await this.git.ensureSupports("git:worktrees", (requiredVersion, installedVersion) => l10n.t("Displaying worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.", requiredVersion, installedVersion));
159
+ if (force && this._unspawnedReads.has(this.cache.getCommonPath(repoPath))) force = false;
104
160
  return this.cache.getWorktrees(repoPath, async (commonPath, cacheable, signal) => {
105
- signal ??= cancellation;
106
- const [dataResult, branchesResult] = await Promise.allSettled([this.git.run({
107
- cwd: commonPath,
108
- cancellation: signal
109
- }, "worktree", "list", "--porcelain"), this.provider.branches.getBranches(commonPath, void 0, signal)]);
110
- if (dataResult.status === "rejected") cacheable.invalidate();
111
- const getWorkspaceFolder = this.context.workspace ? (uri) => {
112
- const folder = this.context.workspace.getFolder(uri.fsPath);
113
- if (folder == null) return void 0;
114
- return {
115
- uri: fileUri(folder.path),
116
- name: basename$1(folder.path)
117
- };
118
- } : void 0;
119
- const worktrees = parseGitWorktrees(getSettledValue(dataResult)?.stdout, commonPath, getSettledValue(branchesResult)?.values ?? [], getWorkspaceFolder);
120
- if (await this.git.supports("git:worktree:list:lock-reason")) return worktrees;
121
- return Promise.all(worktrees.map((w) => this.withLockReason(w)));
122
- }, cancellation);
161
+ const unspawned = {};
162
+ this._unspawnedReads.set(commonPath, unspawned);
163
+ const onSpawned = () => {
164
+ if (this._unspawnedReads.get(commonPath) === unspawned) this._unspawnedReads.delete(commonPath);
165
+ };
166
+ try {
167
+ return await this.readWorktrees(repoPath, commonPath, cacheable, signal ?? cancellation, onSpawned);
168
+ } finally {
169
+ onSpawned();
170
+ }
171
+ }, force ? { force: true } : void 0, cancellation);
172
+ }
173
+ async readWorktrees(repoPath, commonPath, cacheable, signal, onSpawn) {
174
+ const rootsRead = this.readIgnoredWorktreeRoots?.(repoPath).catch(() => void 0);
175
+ const [dataResult, branchesResult] = await Promise.allSettled([this.git.run({
176
+ cwd: commonPath,
177
+ cancellation: signal,
178
+ onSpawn
179
+ }, "worktree", "list", "--porcelain"), this.provider.branches.getBranches(commonPath, void 0, signal)]);
180
+ if (dataResult.status === "rejected") cacheable.invalidate();
181
+ const getWorkspaceFolder = this.context.workspace ? (uri) => {
182
+ const folder = this.context.workspace.getFolder(uri.fsPath);
183
+ if (folder == null) return void 0;
184
+ return {
185
+ uri: fileUri(folder.path),
186
+ name: basename$1(folder.path)
187
+ };
188
+ } : void 0;
189
+ const data = getSettledValue(dataResult)?.stdout;
190
+ let worktrees = parseGitWorktrees(data, commonPath, getSettledValue(branchesResult)?.values ?? [], getWorkspaceFolder);
191
+ const knowsPaths = await this.git.supports("git:for-each-ref:worktreePath");
192
+ const stale = branchesResult.status === "fulfilled" ? findStaleBranches(worktrees, branchesResult.value.values, knowsPaths) : void 0;
193
+ if (stale == null) this._lastingDisagreements.delete(commonPath);
194
+ else if (this._lastingDisagreements.get(commonPath) !== data) {
195
+ this.context.hooks?.cache?.onReset?.(commonPath, "branches");
196
+ for (const path of stale.switched) this.context.hooks?.cache?.onReset?.(path, "branch");
197
+ let branches;
198
+ try {
199
+ ({values: branches} = await this.provider.branches.getBranches(commonPath, void 0, signal));
200
+ } catch {
201
+ cacheable.invalidate();
202
+ }
203
+ if (branches != null) {
204
+ worktrees = parseGitWorktrees(data, commonPath, branches, getWorkspaceFolder);
205
+ if (data != null && findStaleBranches(worktrees, branches, knowsPaths) != null) this._lastingDisagreements.set(commonPath, data);
206
+ for (const path of /* @__PURE__ */ new Set([commonPath, ...this.cache.getWorktreePaths(commonPath)])) this.provider.notifyHost(path, ["heads"]);
207
+ }
208
+ }
209
+ if (rootsRead != null) {
210
+ const roots = await rootsRead;
211
+ if (roots == null) cacheable.invalidate();
212
+ else if (roots.length) worktrees = worktrees.filter((w) => !roots.some((root) => isDescendant(normalizePath(w.path), root)));
213
+ }
214
+ if (await this.git.supports("git:worktree:list:lock-reason")) return worktrees;
215
+ return Promise.all(worktrees.map((w) => this.withLockReason(w)));
123
216
  }
124
217
  async withLockReason(worktree) {
125
218
  if (worktree.locked !== true || worktree.isDefault || worktree.type === "bare") return worktree;
@@ -129,7 +222,7 @@ var WorktreesGitSubProvider = class {
129
222
  if (match == null) return worktree;
130
223
  const reason = (await promises.readFile(join(resolve(worktree.uri.fsPath, match[1]), "locked"), "utf8")).trim();
131
224
  if (!reason) return worktree;
132
- return new GitWorktree(worktree.isDefault, worktree.type, worktree.repoPath, worktree.uri, reason, worktree.prunable, worktree.sha, worktree.branch, worktree.workspaceFolder);
225
+ return new GitWorktree(worktree.isDefault, worktree.type, worktree.repoPath, worktree.uri, reason, worktree.prunable, worktree.sha, worktree.branch, worktree.workspaceFolder, worktree.branchName);
133
226
  } catch {
134
227
  return worktree;
135
228
  }
@@ -222,7 +315,7 @@ var WorktreesGitSubProvider = class {
222
315
  } finally {
223
316
  if (deleted) this.cache.unregisterRepoPath(pathStr);
224
317
  this.context.hooks?.cache?.onReset?.(repoPath, "worktrees");
225
- this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees"]);
318
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees", "worktreeHead"]);
226
319
  }
227
320
  }
228
321
  async pruneWorktrees(repoPath, options) {
@@ -241,7 +334,7 @@ var WorktreesGitSubProvider = class {
241
334
  }
242
335
  } finally {
243
336
  this.context.hooks?.cache?.onReset?.(repoPath, "worktrees");
244
- this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees"]);
337
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees", "worktreeHead"]);
245
338
  }
246
339
  }
247
340
  /**
@@ -271,7 +364,7 @@ var WorktreesGitSubProvider = class {
271
364
  errors: "throw"
272
365
  }, ...args);
273
366
  } finally {
274
- this.context.hooks?.cache?.onReset?.(repoPath, "worktrees");
367
+ this.context.hooks?.cache?.onReset?.(repoPath, "worktreeList");
275
368
  this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees"]);
276
369
  }
277
370
  }
@@ -284,7 +377,7 @@ var WorktreesGitSubProvider = class {
284
377
  errors: "throw"
285
378
  }, "worktree", "unlock", pathStr);
286
379
  } finally {
287
- this.context.hooks?.cache?.onReset?.(repoPath, "worktrees");
380
+ this.context.hooks?.cache?.onReset?.(repoPath, "worktreeList");
288
381
  this.context.hooks?.repository?.onChanged?.(repoPath, ["worktrees"]);
289
382
  }
290
383
  }
@@ -1 +1 @@
1
- {"version":3,"file":"worktrees.js","names":["basename","fs"],"sources":["../../../../git-cli/src/providers/worktrees.ts"],"sourcesContent":["import { exec } from 'child_process';\nimport { promises as fs } from 'fs';\nimport { join, resolve } from 'path';\nimport * as l10n from '@vscode/l10n';\nimport type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { WorktreeCreateError, WorktreeDeleteError } from '@gitlens/git/errors.js';\nimport type { WorkspaceFolderResolver } from '@gitlens/git/models/worktree.js';\nimport { GitWorktree } from '@gitlens/git/models/worktree.js';\nimport type { GitOperationRunOptions } from '@gitlens/git/providers/operations.js';\nimport type { GitWorktreesSubProvider } from '@gitlens/git/providers/worktrees.js';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport { isWindows } from '@gitlens/utils/env/node/platform.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { basename, normalizePath } from '@gitlens/utils/path.js';\nimport { getSettledValue } from '@gitlens/utils/promise.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { fileUri, toFsPath } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport { mayWriteTracking } from '../exec/commandClassifier.js';\nimport type { Git, GitError } from '../exec/git.js';\nimport { getGitCommandError, GitErrors, isRunHookFailure } from '../exec/git.js';\nimport { parseGitWorktrees } from '../parsers/worktreeParser.js';\n\n/** Extracts the lock reason from Git's `cannot remove a locked working tree` output, when it provides one */\nfunction getLockReason(ex: GitError): string | undefined {\n\treturn GitErrors.worktreeLockedReason.exec(ex.stderr || ex.stdout || ex.message || '')?.[1].trim() || undefined;\n}\n\n/**\n * The branch `git worktree add` says it created, from the `Preparing worktree (new branch '<name>')` line it writes to\n * stderr (git 2.17+, in English since every run sets `LC_ALL=C`)\n */\nfunction getNewWorktreeBranch(stderr: string | undefined): string | undefined {\n\treturn stderr ? /^Preparing worktree \\(new branch '(.+)'\\)$/m.exec(stderr)?.[1] : undefined;\n}\n\nexport class WorktreesGitSubProvider implements GitWorktreesSubProvider {\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t) {}\n\n\t/**\n\t * Creates a worktree (`git worktree add`). `runOptions` are spread into the underlying `git.run` call, so a\n\t * large checkout can run without the default command timeout (`{ timeout: 0 }`) and still be cancelled.\n\t */\n\t@debug()\n\tasync createWorktree(\n\t\trepoPath: string,\n\t\tpath: string,\n\t\toptions?: {\n\t\t\tcommitish?: string;\n\t\t\tcreateBranch?: string;\n\t\t\tdetach?: boolean;\n\t\t\tforce?: boolean;\n\t\t\tnoTracking?: boolean;\n\t\t},\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst args = ['worktree', 'add'];\n\t\tif (options?.force) {\n\t\t\targs.push('--force');\n\t\t}\n\t\tif (options?.createBranch) {\n\t\t\targs.push('-b', options.createBranch);\n\t\t}\n\t\tif (options?.createBranch && options.noTracking) {\n\t\t\targs.push('--no-track');\n\t\t}\n\t\tif (options?.detach) {\n\t\t\targs.push('--detach');\n\t\t}\n\t\targs.push(path);\n\t\tif (options?.commitish) {\n\t\t\targs.push(options.commitish);\n\t\t}\n\n\t\t// Without `-b`, git may still create a branch: from a remote branch of the commitish's name, or named after the\n\t\t// path when no commitish is given\n\t\tlet createdBranch = options?.createBranch;\n\t\t// A cancelled or timed-out run, or a host-rejected retry, leaves no output to name the branch git may have created\n\t\tlet mayHaveCreatedBranch = false;\n\t\ttry {\n\t\t\tconst result = await this.git.run({ cwd: repoPath, ...runOptions }, ...args);\n\t\t\tcreatedBranch ??= getNewWorktreeBranch(result.stderr);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\t// `?.`: a host's index-lock retry hook may reject with anything, `undefined` included\n\t\t\tcreatedBranch ??= getNewWorktreeBranch((ex as GitError | undefined)?.stderr);\n\n\t\t\t// A cancelled or timed-out checkout, or a host-rejected index-lock retry, is not git refusing the worktree\n\t\t\tif (isCancellationError(ex) || isRunHookFailure(ex)) {\n\t\t\t\tmayHaveCreatedBranch = createdBranch == null && !options?.detach;\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tthrow getGitCommandError(\n\t\t\t\t'worktree-create',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew WorktreeCreateError(\n\t\t\t\t\t\t{ reason: reason, gitCommand: { repoPath: repoPath, args: args } },\n\t\t\t\t\t\tex as GitError,\n\t\t\t\t\t),\n\t\t\t);\n\t\t} finally {\n\t\t\t// A failed or cancelled add may already have created the branch (and its tracking in `.git/config`) or\n\t\t\t// the worktree directory.\n\t\t\t//\n\t\t\t// The tracking isn't announced as a `config` change, only evicted: git may also write repository-wide\n\t\t\t// keys (`extensions.relativeWorktrees` under `worktree.useRelativePaths`), so the write can't claim its\n\t\t\t// config change is confined to the new branch, and an unconfined one would refresh every worktree on each\n\t\t\t// add. The watcher's report of the file says what changed in it instead.\n\t\t\tif (createdBranch) {\n\t\t\t\t// Evict the cached base only — see `checkout -b` for why the persisted keys are left alone.\n\t\t\t\tthis.cache.deleteBaseBranchName(repoPath, createdBranch);\n\t\t\t\t// A branch git created on its own may track a remote branch, whatever the commitish looks like\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(\n\t\t\t\t\trepoPath,\n\t\t\t\t\t...(!options?.createBranch || mayWriteTracking(options.commitish, options.noTracking)\n\t\t\t\t\t\t? (['branches', 'config', 'worktrees'] as const)\n\t\t\t\t\t\t: (['branches', 'worktrees'] as const)),\n\t\t\t\t);\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(\n\t\t\t\t\trepoPath,\n\t\t\t\t\t['worktrees', 'heads'],\n\t\t\t\t\t[`refs/heads/${createdBranch}`],\n\t\t\t\t);\n\t\t\t} else if (mayHaveCreatedBranch) {\n\t\t\t\t// Git creates the branch before checking it out, so a run cut short may have left one it never named\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'config', 'worktrees');\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees', 'heads']);\n\t\t\t} else {\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync createWorktreeWithResult(\n\t\trepoPath: string,\n\t\tpath: string,\n\t\toptions?: {\n\t\t\tcommitish?: string;\n\t\t\tcreateBranch?: string;\n\t\t\tdetach?: boolean;\n\t\t\tforce?: boolean;\n\t\t\tnoTracking?: boolean;\n\t\t},\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<GitWorktree | undefined> {\n\t\tawait this.createWorktree(repoPath, path, options, runOptions);\n\t\tconst normalized = normalizePath(path);\n\t\treturn this.getWorktree(repoPath, w => normalizePath(w.path) === normalized, runOptions?.cancellation);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getWorktree(\n\t\trepoPath: string,\n\t\tpredicate: (w: GitWorktree) => boolean,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitWorktree | undefined> {\n\t\treturn (await this.getWorktrees(repoPath, cancellation)).find(predicate);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getWorktrees(repoPath: string, cancellation?: AbortSignal): Promise<GitWorktree[]> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Displaying worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\treturn this.cache.getWorktrees(\n\t\t\trepoPath,\n\t\t\tasync (commonPath, cacheable, signal) => {\n\t\t\t\t// Prefer the aggregate signal from the cache; fall back to the caller's cancellation.\n\t\t\t\tsignal ??= cancellation;\n\t\t\t\tconst [dataResult, branchesResult] = await Promise.allSettled([\n\t\t\t\t\tthis.git.run({ cwd: commonPath, cancellation: signal }, 'worktree', 'list', '--porcelain'),\n\t\t\t\t\tthis.provider.branches.getBranches(commonPath, undefined, signal),\n\t\t\t\t]);\n\n\t\t\t\tif (dataResult.status === 'rejected') {\n\t\t\t\t\t// Don't cache a bogus empty result for a transient command failure — retry on next access.\n\t\t\t\t\tcacheable.invalidate();\n\t\t\t\t}\n\n\t\t\t\tconst getWorkspaceFolder: WorkspaceFolderResolver | undefined = this.context.workspace\n\t\t\t\t\t? uri => {\n\t\t\t\t\t\t\tconst folder = this.context.workspace!.getFolder(uri.fsPath);\n\t\t\t\t\t\t\tif (folder == null) return undefined;\n\t\t\t\t\t\t\treturn { uri: fileUri(folder.path), name: basename(folder.path) };\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined;\n\n\t\t\t\tconst worktrees = parseGitWorktrees(\n\t\t\t\t\tgetSettledValue(dataResult)?.stdout,\n\t\t\t\t\tcommonPath,\n\t\t\t\t\tgetSettledValue(branchesResult)?.values ?? [],\n\t\t\t\t\tgetWorkspaceFolder,\n\t\t\t\t);\n\n\t\t\t\t// Older git prints a bare `locked` even when a reason exists; recover it from the admin dir\n\t\t\t\tif (await this.git.supports('git:worktree:list:lock-reason')) return worktrees;\n\n\t\t\t\treturn Promise.all(worktrees.map(w => this.withLockReason(w)));\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async withLockReason(worktree: GitWorktree): Promise<GitWorktree> {\n\t\tif (worktree.locked !== true || worktree.isDefault || worktree.type === 'bare') return worktree;\n\n\t\ttry {\n\t\t\t// A linked worktree's `.git` is a file: `gitdir: <admin dir>`, relative to the worktree when not absolute\n\t\t\tconst dotGit = join(worktree.uri.fsPath, '.git');\n\t\t\tconst match = /^gitdir:\\s*(.+?)\\s*$/m.exec(await fs.readFile(dotGit, 'utf8'));\n\t\t\tif (match == null) return worktree;\n\n\t\t\tconst reason = (await fs.readFile(join(resolve(worktree.uri.fsPath, match[1]), 'locked'), 'utf8')).trim();\n\t\t\tif (!reason) return worktree;\n\n\t\t\treturn new GitWorktree(\n\t\t\t\tworktree.isDefault,\n\t\t\t\tworktree.type,\n\t\t\t\tworktree.repoPath,\n\t\t\t\tworktree.uri,\n\t\t\t\treason,\n\t\t\t\tworktree.prunable,\n\t\t\t\tworktree.sha,\n\t\t\t\tworktree.branch,\n\t\t\t\tworktree.workspaceFolder,\n\t\t\t);\n\t\t} catch {\n\t\t\treturn worktree;\n\t\t}\n\t}\n\n\t@debug({ quiet: true })\n\tgetWorktreesDefaultUri(repoPath: string): Uri | undefined {\n\t\treturn this.context.workspace?.getWorktreeDefaultUri?.(repoPath);\n\t}\n\n\t/**\n\t * Deletes a worktree (`git worktree remove`). Pass `force: 'locked'` to also override a locked worktree.\n\t *\n\t * `runOptions` are spread into the underlying `git.run` call (e.g. `{ timeout: 0 }` for a large worktree\n\t * whose removal may exceed the default command timeout).\n\t *\n\t * On failure, the thrown {@link WorktreeDeleteError}'s `original` property carries the raw\n\t * {@link GitError} (stderr/stdout/exit code) untouched — a caller that needs to classify a\n\t * platform-specific failure (e.g. a Windows file-lock retry) can read it directly rather than parsing\n\t * the typed error's message.\n\t */\n\t@debug()\n\tasync deleteWorktree(\n\t\trepoPath: string,\n\t\tpath: string | Uri,\n\t\toptions?: { force?: boolean | 'locked' },\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Deleting worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst args = ['worktree', 'remove'];\n\t\tif (options?.force) {\n\t\t\targs.push('--force');\n\t\t\t// Git requires a second `--force` to remove a locked worktree\n\t\t\tif (options.force === 'locked') {\n\t\t\t\targs.push('--force');\n\t\t\t}\n\t\t}\n\n\t\tconst pathStr = normalizePath(toFsPath(path));\n\t\targs.push(pathStr);\n\n\t\tlet deleted = false;\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw', ...runOptions }, ...args);\n\t\t\tdeleted = true;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\t// A cancelled or timed-out removal (`runOptions.cancellation`/`timeout`), or a host-rejected index-lock\n\t\t\t// retry, is not git refusing it, and wrapping it would hand the caller a `WorktreeDeleteError` whose\n\t\t\t// `original` is no `GitError` at all\n\t\t\tif (isCancellationError(ex) || isRunHookFailure(ex)) throw ex;\n\n\t\t\tconst gitError = getGitCommandError(\n\t\t\t\t'worktree-delete',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew WorktreeDeleteError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treason: reason,\n\t\t\t\t\t\t\tlockReason: reason === 'locked' ? getLockReason(ex as GitError) : undefined,\n\t\t\t\t\t\t\tgitCommand: { repoPath: repoPath, args: args },\n\t\t\t\t\t\t},\n\t\t\t\t\t\tex as GitError,\n\t\t\t\t\t),\n\t\t\t);\n\n\t\t\tif (gitError.details.reason === 'directoryNotEmpty') {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`Failed to fully delete worktree '${pathStr}' because it is not empty. Attempting to delete it manually.`,\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\tawait fs.rm(pathStr, { force: true, recursive: true });\n\t\t\t\t\tdeleted = true;\n\t\t\t\t\treturn;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tif (isWindows) {\n\t\t\t\t\t\tconst match = /EPERM: operation not permitted, unlink '(.*?)'/i.exec((ex as GitError).message);\n\t\t\t\t\t\tif (match != null) {\n\t\t\t\t\t\t\tscope?.warn(\n\t\t\t\t\t\t\t\t`Failed to manually delete '${pathStr}' because it is in use. Attempting to forcefully delete it.`,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Windows-specific: `del` via cmd.exe to force-remove a locked symlink.\n\t\t\t\t\t\t\t// Node's fs.rm/unlink fails with EPERM when the symlink target is in use.\n\t\t\t\t\t\t\tfunction deleteInUseSymlink(symlink: string) {\n\t\t\t\t\t\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t\t\t\t\t\texec(`del \"${symlink}\"`, (ex, stdout, stderr) => {\n\t\t\t\t\t\t\t\t\t\tif (ex) {\n\t\t\t\t\t\t\t\t\t\t\treject(ex instanceof Error ? ex : new Error(String(ex)));\n\t\t\t\t\t\t\t\t\t\t} else if (stderr) {\n\t\t\t\t\t\t\t\t\t\t\treject(new Error(stderr));\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tresolve(stdout);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst [, file] = match;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait deleteInUseSymlink(file);\n\t\t\t\t\t\t\t\tawait fs.rm(pathStr, { force: true, recursive: true, maxRetries: 1, retryDelay: 1 });\n\t\t\t\t\t\t\t\tdeleted = true;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\t\tscope?.error(ex, `Failed to forcefully delete '${pathStr}' because it is in use.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow gitError;\n\t\t} finally {\n\t\t\tif (deleted) {\n\t\t\t\t// Clean up Cache registry + per-worktree cache entries for the deleted worktree.\n\t\t\t\t// Order matters: unregister before emitting the repo-level change so any listener\n\t\t\t\t// that re-queries worktree-aware state sees the registry already updated.\n\t\t\t\tthis.cache.unregisterRepoPath(pathStr);\n\t\t\t}\n\t\t\t// A failed or cancelled removal may already have taken the worktree's files or admin entry\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t}\n\t}\n\n\t@debug()\n\tasync pruneWorktrees(repoPath: string, options?: { expire?: string }): Promise<void> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Pruning worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst before = await this.listWorktreePaths(repoPath);\n\n\t\tconst args = ['worktree', 'prune'];\n\t\tif (options?.expire) {\n\t\t\targs.push('--expire', options.expire);\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw' }, ...args);\n\n\t\t\tconst after = await this.listWorktreePaths(repoPath);\n\n\t\t\t// Order matters (see `deleteWorktree`): unregister before emitting the repo-level change so any\n\t\t\t// listener that re-queries worktree-aware state sees the registry already updated.\n\t\t\tif (before != null && after != null) {\n\t\t\t\tfor (const path of before) {\n\t\t\t\t\tif (!after.has(path)) {\n\t\t\t\t\t\tthis.cache.unregisterRepoPath(path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t}\n\t}\n\n\t/**\n\t * Raw worktree paths currently known to git (`worktree <path>` lines from `--porcelain`), normalized the\n\t * same way {@link deleteWorktree} does. A failed listing (`errors: 'ignore'`) yields an empty set rather\n\t * than throwing — used by `pruneWorktrees` to diff before/after, where a transient read failure must not\n\t * be mistaken for every worktree having vanished.\n\t */\n\tprivate async listWorktreePaths(repoPath: string): Promise<Set<string> | undefined> {\n\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'worktree', 'list', '--porcelain');\n\t\t// A failed listing is not an empty one: read as empty, the after-prune one would unregister everything\n\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0) return undefined;\n\n\t\tconst paths = new Set<string>();\n\t\tfor (const line of result.stdout.split('\\n')) {\n\t\t\tif (line.startsWith('worktree ')) {\n\t\t\t\tpaths.add(normalizePath(toFsPath(line.slice('worktree '.length).trim())));\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\t@debug()\n\tasync lockWorktree(repoPath: string, path: string | Uri, options?: { reason?: string }): Promise<void> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Locking worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst args = ['worktree', 'lock'];\n\t\tif (options?.reason) {\n\t\t\targs.push('--reason', options.reason);\n\t\t}\n\t\targs.push(normalizePath(toFsPath(path)));\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw' }, ...args);\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t}\n\t}\n\n\t@debug()\n\tasync unlockWorktree(repoPath: string, path: string | Uri): Promise<void> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Unlocking worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst pathStr = normalizePath(toFsPath(path));\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw' }, 'worktree', 'unlock', pathStr);\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"worktrees.js","names":["basename","fs"],"sources":["../../../../git-cli/src/providers/worktrees.ts"],"sourcesContent":["import { exec } from 'child_process';\nimport { promises as fs } from 'fs';\nimport { join, resolve } from 'path';\nimport * as l10n from '@vscode/l10n';\nimport type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { WorktreeCreateError, WorktreeDeleteError } from '@gitlens/git/errors.js';\nimport 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 type { GitOperationRunOptions } from '@gitlens/git/providers/operations.js';\nimport type { GitWorktreesSubProvider } from '@gitlens/git/providers/worktrees.js';\nimport { isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { debug } from '@gitlens/utils/decorators/log.js';\nimport { isWindows } from '@gitlens/utils/env/node/platform.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { basename, isDescendant, normalizePath } from '@gitlens/utils/path.js';\nimport { getSettledValue } from '@gitlens/utils/promise.js';\nimport type { CacheController } from '@gitlens/utils/promiseCache.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { fileUri, toFsPath } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport { mayWriteTracking } from '../exec/commandClassifier.js';\nimport type { Git, GitError } from '../exec/git.js';\nimport { getGitCommandError, GitErrors, isRunHookFailure } from '../exec/git.js';\nimport { parseGitWorktrees } from '../parsers/worktreeParser.js';\n\n/** Extracts the lock reason from Git's `cannot remove a locked working tree` output, when it provides one */\nfunction getLockReason(ex: GitError): string | undefined {\n\treturn GitErrors.worktreeLockedReason.exec(ex.stderr || ex.stdout || ex.message || '')?.[1].trim() || undefined;\n}\n\n/**\n * The branch `git worktree add` says it created, from the `Preparing worktree (new branch '<name>')` line it writes to\n * stderr (git 2.17+, in English since every run sets `LC_ALL=C`)\n */\nfunction getNewWorktreeBranch(stderr: string | undefined): string | undefined {\n\treturn stderr ? /^Preparing worktree \\(new branch '(.+)'\\)$/m.exec(stderr)?.[1] : undefined;\n}\n\nconst unbornSha = /^0+$/;\n\n/**\n * Where `git worktree list` contradicts the branch list it was paired with, or `undefined` when they agree. `switched`\n * holds the worktrees whose checked-out branch differs from the one the branch list places there.\n */\nfunction findStaleBranches(\n\tworktrees: readonly GitWorktree[],\n\tbranches: readonly GitBranch[],\n\t/** Whether git says where each branch is checked out (`%(worktreepath)`); before it, only the current branch's is known */\n\tknowsPaths: boolean,\n): { switched: string[] } | undefined {\n\tif (!worktrees.length) return undefined;\n\n\tconst byName = new Map<string, GitBranch>();\n\tconst byPath = new Map<string, GitBranch>();\n\tfor (const b of branches) {\n\t\t// A detached HEAD's stand-in branch names no ref\n\t\tif (b.remote || b.detached) continue;\n\n\t\tbyName.set(b.name, b);\n\t\t// A rebasing branch's worktree lists as detached\n\t\tif (b.worktree && !b.rebasing) {\n\t\t\tbyPath.set(normalizePath(b.worktree.path), b);\n\t\t}\n\t}\n\n\t// Git places a branch checked out in several worktrees (`worktree add --force`) at only one of them\n\tconst listedBranches = new Map(worktrees.map(w => [w.path, w.branchName]));\n\n\tlet stale = false;\n\tconst switched: string[] = [];\n\tfor (const w of worktrees) {\n\t\t// A bare repository checks nothing out, though git places the branch its HEAD names there\n\t\tif (w.type === 'bare') {\n\t\t\tbyPath.delete(w.path);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst listed = byPath.get(w.path);\n\t\tif (listed != null) {\n\t\t\tbyPath.delete(w.path);\n\t\t\tif (listed.name !== w.branchName) {\n\t\t\t\tswitched.push(w.path);\n\t\t\t\tstale = true;\n\t\t\t}\n\t\t} else if (knowsPaths && w.branchName != null) {\n\t\t\t// Detached, or not yet a worktree, when the branches were read\n\t\t\tswitched.push(w.path);\n\t\t}\n\n\t\t// An unborn branch has no ref for the branch list to know\n\t\tif (w.branchName == null || !w.sha || unbornSha.test(w.sha)) continue;\n\n\t\tconst branch = byName.get(w.branchName);\n\t\tif (\n\t\t\tbranch == null ||\n\t\t\t(branch.sha != null && branch.sha !== w.sha) ||\n\t\t\tbranch.worktree === false ||\n\t\t\t(branch.worktree != null && listedBranches.get(normalizePath(branch.worktree.path)) !== w.branchName)\n\t\t) {\n\t\t\tstale = true;\n\t\t}\n\t}\n\n\t// A branch placed in a worktree that no longer lists it was removed or switched away\n\tif (byPath.size) {\n\t\tstale = true;\n\t}\n\n\treturn stale ? { switched: switched } : undefined;\n}\n\nexport class WorktreesGitSubProvider implements GitWorktreesSubProvider {\n\t/** Per common path, the read whose `git worktree list` hasn't started yet */\n\tprivate readonly _unspawnedReads = new Map<string, object>();\n\t/**\n\t * Per common path, the `git worktree list` output a fresh branch list still contradicted: a layout where git's two\n\t * listings never agree, which re-reading the branches again can't fix until the list changes\n\t */\n\tprivate readonly _lastingDisagreements = new Map<string, string>();\n\n\tconstructor(\n\t\tprivate readonly context: GitServiceContext,\n\t\tprivate readonly git: Git,\n\t\tprivate readonly cache: Cache,\n\t\tprivate readonly provider: CliGitProviderInternal,\n\t\t/**\n\t\t * Reads the absolute folders whose worktrees a repository's list leaves out, rejecting (or answering `undefined`)\n\t\t * when they can't be read; given only when folders are registered\n\t\t */\n\t\tprivate readonly readIgnoredWorktreeRoots?: (repoPath: string) => Promise<readonly string[] | undefined>,\n\t) {}\n\n\t/**\n\t * Creates a worktree (`git worktree add`). `runOptions` are spread into the underlying `git.run` call, so a\n\t * large checkout can run without the default command timeout (`{ timeout: 0 }`) and still be cancelled.\n\t */\n\t@debug()\n\tasync createWorktree(\n\t\trepoPath: string,\n\t\tpath: string,\n\t\toptions?: {\n\t\t\tcommitish?: string;\n\t\t\tcreateBranch?: string;\n\t\t\tdetach?: boolean;\n\t\t\tforce?: boolean;\n\t\t\tnoTracking?: boolean;\n\t\t},\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst args = ['worktree', 'add'];\n\t\tif (options?.force) {\n\t\t\targs.push('--force');\n\t\t}\n\t\tif (options?.createBranch) {\n\t\t\targs.push('-b', options.createBranch);\n\t\t}\n\t\tif (options?.createBranch && options.noTracking) {\n\t\t\targs.push('--no-track');\n\t\t}\n\t\tif (options?.detach) {\n\t\t\targs.push('--detach');\n\t\t}\n\t\targs.push(path);\n\t\tif (options?.commitish) {\n\t\t\targs.push(options.commitish);\n\t\t}\n\n\t\t// Without `-b`, git may still create a branch: from a remote branch of the commitish's name, or named after the\n\t\t// path when no commitish is given\n\t\tlet createdBranch = options?.createBranch;\n\t\t// A cancelled or timed-out run, or a host-rejected retry, leaves no output to name the branch git may have created\n\t\tlet mayHaveCreatedBranch = false;\n\t\ttry {\n\t\t\tconst result = await this.git.run({ cwd: repoPath, ...runOptions }, ...args);\n\t\t\tcreatedBranch ??= getNewWorktreeBranch(result.stderr);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\t// `?.`: a host's index-lock retry hook may reject with anything, `undefined` included\n\t\t\tcreatedBranch ??= getNewWorktreeBranch((ex as GitError | undefined)?.stderr);\n\n\t\t\t// A cancelled or timed-out checkout, or a host-rejected index-lock retry, is not git refusing the worktree\n\t\t\tif (isCancellationError(ex) || isRunHookFailure(ex)) {\n\t\t\t\tmayHaveCreatedBranch = createdBranch == null && !options?.detach;\n\t\t\t\tthrow ex;\n\t\t\t}\n\n\t\t\tthrow getGitCommandError(\n\t\t\t\t'worktree-create',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew WorktreeCreateError(\n\t\t\t\t\t\t{ reason: reason, gitCommand: { repoPath: repoPath, args: args } },\n\t\t\t\t\t\tex as GitError,\n\t\t\t\t\t),\n\t\t\t);\n\t\t} finally {\n\t\t\t// A failed or cancelled add may already have created the branch (and its tracking in `.git/config`) or\n\t\t\t// the worktree directory.\n\t\t\t//\n\t\t\t// The tracking isn't announced as a `config` change, only evicted: git may also write repository-wide\n\t\t\t// keys (`extensions.relativeWorktrees` under `worktree.useRelativePaths`), so the write can't claim its\n\t\t\t// config change is confined to the new branch, and an unconfined one would refresh every worktree on each\n\t\t\t// add. The watcher's report of the file says what changed in it instead.\n\t\t\tif (createdBranch) {\n\t\t\t\t// Evict the cached base only — see `checkout -b` for why the persisted keys are left alone.\n\t\t\t\tthis.cache.deleteBaseBranchName(repoPath, createdBranch);\n\t\t\t\t// A branch git created on its own may track a remote branch, whatever the commitish looks like\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(\n\t\t\t\t\trepoPath,\n\t\t\t\t\t...(!options?.createBranch || mayWriteTracking(options.commitish, options.noTracking)\n\t\t\t\t\t\t? (['branches', 'config', 'worktrees'] as const)\n\t\t\t\t\t\t: (['branches', 'worktrees'] as const)),\n\t\t\t\t);\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(\n\t\t\t\t\trepoPath,\n\t\t\t\t\t['worktrees', 'worktreeHead', 'heads'],\n\t\t\t\t\t[`refs/heads/${createdBranch}`],\n\t\t\t\t);\n\t\t\t} else if (mayHaveCreatedBranch) {\n\t\t\t\t// Git creates the branch before checking it out, so a run cut short may have left one it never named\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'config', 'worktrees');\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees', 'worktreeHead', 'heads']);\n\t\t\t} else {\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees', 'worktreeHead']);\n\t\t\t}\n\t\t}\n\t}\n\n\tasync createWorktreeWithResult(\n\t\trepoPath: string,\n\t\tpath: string,\n\t\toptions?: {\n\t\t\tcommitish?: string;\n\t\t\tcreateBranch?: string;\n\t\t\tdetach?: boolean;\n\t\t\tforce?: boolean;\n\t\t\tnoTracking?: boolean;\n\t\t},\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<GitWorktree | undefined> {\n\t\tawait this.createWorktree(repoPath, path, options, runOptions);\n\t\tconst normalized = normalizePath(path);\n\t\treturn this.getWorktree(\n\t\t\trepoPath,\n\t\t\tw => normalizePath(w.path) === normalized,\n\t\t\tundefined,\n\t\t\trunOptions?.cancellation,\n\t\t);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getWorktree(\n\t\trepoPath: string,\n\t\tpredicate: (w: GitWorktree) => boolean,\n\t\toptions?: { force?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitWorktree | undefined> {\n\t\treturn (await this.getWorktrees(repoPath, options, cancellation)).find(predicate);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getWorktrees(\n\t\trepoPath: string,\n\t\toptions?: { force?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitWorktree[]> {\n\t\t// A pending read that hasn't started `git worktree list` will see the state as of this call, so it is joined,\n\t\t// whether it was pending as the call started or is once the call would evict (another forced read's, say)\n\t\tlet force = options?.force === true && !this._unspawnedReads.has(this.cache.getCommonPath(repoPath));\n\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Displaying worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tif (force && this._unspawnedReads.has(this.cache.getCommonPath(repoPath))) {\n\t\t\tforce = false;\n\t\t}\n\n\t\treturn this.cache.getWorktrees(\n\t\t\trepoPath,\n\t\t\tasync (commonPath, cacheable, signal) => {\n\t\t\t\tconst unspawned = {};\n\t\t\t\tthis._unspawnedReads.set(commonPath, unspawned);\n\t\t\t\tconst onSpawned = () => {\n\t\t\t\t\tif (this._unspawnedReads.get(commonPath) === unspawned) {\n\t\t\t\t\t\tthis._unspawnedReads.delete(commonPath);\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\ttry {\n\t\t\t\t\treturn await this.readWorktrees(repoPath, commonPath, cacheable, signal ?? cancellation, onSpawned);\n\t\t\t\t} finally {\n\t\t\t\t\tonSpawned();\n\t\t\t\t}\n\t\t\t},\n\t\t\tforce ? { force: true } : undefined,\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\tprivate async readWorktrees(\n\t\trepoPath: string,\n\t\tcommonPath: string,\n\t\tcacheable: CacheController,\n\t\tsignal: AbortSignal | undefined,\n\t\tonSpawn: () => void,\n\t): Promise<GitWorktree[]> {\n\t\t// Read beside the list, which it doesn't depend on. `commonPath` is the main worktree, not the git dir,\n\t\t// so the roots come from the repository's own git dir\n\t\tconst rootsRead = this.readIgnoredWorktreeRoots?.(repoPath).catch(() => undefined);\n\t\tconst [dataResult, branchesResult] = await Promise.allSettled([\n\t\t\tthis.git.run(\n\t\t\t\t{ cwd: commonPath, cancellation: signal, onSpawn: onSpawn },\n\t\t\t\t'worktree',\n\t\t\t\t'list',\n\t\t\t\t'--porcelain',\n\t\t\t),\n\t\t\tthis.provider.branches.getBranches(commonPath, undefined, signal),\n\t\t]);\n\n\t\tif (dataResult.status === 'rejected') {\n\t\t\t// Don't cache a bogus empty result for a transient command failure — retry on next access.\n\t\t\tcacheable.invalidate();\n\t\t}\n\n\t\tconst getWorkspaceFolder: WorkspaceFolderResolver | undefined = this.context.workspace\n\t\t\t? uri => {\n\t\t\t\t\tconst folder = this.context.workspace!.getFolder(uri.fsPath);\n\t\t\t\t\tif (folder == null) return undefined;\n\t\t\t\t\treturn { uri: fileUri(folder.path), name: basename(folder.path) };\n\t\t\t\t}\n\t\t\t: undefined;\n\n\t\tconst data = getSettledValue(dataResult)?.stdout;\n\t\tlet worktrees = parseGitWorktrees(\n\t\t\tdata,\n\t\t\tcommonPath,\n\t\t\tgetSettledValue(branchesResult)?.values ?? [],\n\t\t\tgetWorkspaceFolder,\n\t\t);\n\n\t\t// The branches come from the cache, which can lag the list just read (a forced read pairs a fresh list with\n\t\t// them). Where the list contradicts them it proves them stale, so they are re-read, once\n\t\tconst knowsPaths = await this.git.supports('git:for-each-ref:worktreePath');\n\t\tconst stale =\n\t\t\tbranchesResult.status === 'fulfilled'\n\t\t\t\t? findStaleBranches(worktrees, branchesResult.value.values, knowsPaths)\n\t\t\t\t: undefined;\n\t\tif (stale == null) {\n\t\t\tthis._lastingDisagreements.delete(commonPath);\n\t\t} else if (this._lastingDisagreements.get(commonPath) !== data) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(commonPath, 'branches');\n\t\t\tfor (const path of stale.switched) {\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(path, 'branch');\n\t\t\t}\n\n\t\t\tlet branches: GitBranch[] | undefined;\n\t\t\ttry {\n\t\t\t\t({ values: branches } = await this.provider.branches.getBranches(commonPath, undefined, signal));\n\t\t\t} catch {\n\t\t\t\t// Keep the branches matched from the stale list, but not in the cache\n\t\t\t\tcacheable.invalidate();\n\t\t\t}\n\n\t\t\tif (branches != null) {\n\t\t\t\tworktrees = parseGitWorktrees(data, commonPath, branches, getWorkspaceFolder);\n\t\t\t\tif (data != null && findStaleBranches(worktrees, branches, knowsPaths) != null) {\n\t\t\t\t\tthis._lastingDisagreements.set(commonPath, data);\n\t\t\t\t}\n\t\t\t\t// The host's views show the branches the watcher missed a change of; which moved is unknown. Only the\n\t\t\t\t// host hears it: announced as a write of core's, the watch layer would take an outside `heads` report\n\t\t\t\t// arriving meanwhile for its echo and drop it, though nothing of the kind was evicted for it\n\t\t\t\tfor (const path of new Set([commonPath, ...this.cache.getWorktreePaths(commonPath)])) {\n\t\t\t\t\tthis.provider.notifyHost(path, ['heads']);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (rootsRead != null) {\n\t\t\tconst roots = await rootsRead;\n\t\t\tif (roots == null) {\n\t\t\t\t// The folders couldn't be read: an unfiltered list would keep ignored worktrees in the shared\n\t\t\t\t// cache until the next change\n\t\t\t\tcacheable.invalidate();\n\t\t\t} else if (roots.length) {\n\t\t\t\tworktrees = worktrees.filter(w => !roots.some(root => isDescendant(normalizePath(w.path), root)));\n\t\t\t}\n\t\t}\n\n\t\t// Older git prints a bare `locked` even when a reason exists; recover it from the admin dir\n\t\tif (await this.git.supports('git:worktree:list:lock-reason')) return worktrees;\n\n\t\treturn Promise.all(worktrees.map(w => this.withLockReason(w)));\n\t}\n\n\tprivate async withLockReason(worktree: GitWorktree): Promise<GitWorktree> {\n\t\tif (worktree.locked !== true || worktree.isDefault || worktree.type === 'bare') return worktree;\n\n\t\ttry {\n\t\t\t// A linked worktree's `.git` is a file: `gitdir: <admin dir>`, relative to the worktree when not absolute\n\t\t\tconst dotGit = join(worktree.uri.fsPath, '.git');\n\t\t\tconst match = /^gitdir:\\s*(.+?)\\s*$/m.exec(await fs.readFile(dotGit, 'utf8'));\n\t\t\tif (match == null) return worktree;\n\n\t\t\tconst reason = (await fs.readFile(join(resolve(worktree.uri.fsPath, match[1]), 'locked'), 'utf8')).trim();\n\t\t\tif (!reason) return worktree;\n\n\t\t\treturn new GitWorktree(\n\t\t\t\tworktree.isDefault,\n\t\t\t\tworktree.type,\n\t\t\t\tworktree.repoPath,\n\t\t\t\tworktree.uri,\n\t\t\t\treason,\n\t\t\t\tworktree.prunable,\n\t\t\t\tworktree.sha,\n\t\t\t\tworktree.branch,\n\t\t\t\tworktree.workspaceFolder,\n\t\t\t\tworktree.branchName,\n\t\t\t);\n\t\t} catch {\n\t\t\treturn worktree;\n\t\t}\n\t}\n\n\t@debug({ quiet: true })\n\tgetWorktreesDefaultUri(repoPath: string): Uri | undefined {\n\t\treturn this.context.workspace?.getWorktreeDefaultUri?.(repoPath);\n\t}\n\n\t/**\n\t * Deletes a worktree (`git worktree remove`). Pass `force: 'locked'` to also override a locked worktree.\n\t *\n\t * `runOptions` are spread into the underlying `git.run` call (e.g. `{ timeout: 0 }` for a large worktree\n\t * whose removal may exceed the default command timeout).\n\t *\n\t * On failure, the thrown {@link WorktreeDeleteError}'s `original` property carries the raw\n\t * {@link GitError} (stderr/stdout/exit code) untouched — a caller that needs to classify a\n\t * platform-specific failure (e.g. a Windows file-lock retry) can read it directly rather than parsing\n\t * the typed error's message.\n\t */\n\t@debug()\n\tasync deleteWorktree(\n\t\trepoPath: string,\n\t\tpath: string | Uri,\n\t\toptions?: { force?: boolean | 'locked' },\n\t\trunOptions?: GitOperationRunOptions,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Deleting worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst args = ['worktree', 'remove'];\n\t\tif (options?.force) {\n\t\t\targs.push('--force');\n\t\t\t// Git requires a second `--force` to remove a locked worktree\n\t\t\tif (options.force === 'locked') {\n\t\t\t\targs.push('--force');\n\t\t\t}\n\t\t}\n\n\t\tconst pathStr = normalizePath(toFsPath(path));\n\t\targs.push(pathStr);\n\n\t\tlet deleted = false;\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw', ...runOptions }, ...args);\n\t\t\tdeleted = true;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\t// A cancelled or timed-out removal (`runOptions.cancellation`/`timeout`), or a host-rejected index-lock\n\t\t\t// retry, is not git refusing it, and wrapping it would hand the caller a `WorktreeDeleteError` whose\n\t\t\t// `original` is no `GitError` at all\n\t\t\tif (isCancellationError(ex) || isRunHookFailure(ex)) throw ex;\n\n\t\t\tconst gitError = getGitCommandError(\n\t\t\t\t'worktree-delete',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew WorktreeDeleteError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treason: reason,\n\t\t\t\t\t\t\tlockReason: reason === 'locked' ? getLockReason(ex as GitError) : undefined,\n\t\t\t\t\t\t\tgitCommand: { repoPath: repoPath, args: args },\n\t\t\t\t\t\t},\n\t\t\t\t\t\tex as GitError,\n\t\t\t\t\t),\n\t\t\t);\n\n\t\t\tif (gitError.details.reason === 'directoryNotEmpty') {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`Failed to fully delete worktree '${pathStr}' because it is not empty. Attempting to delete it manually.`,\n\t\t\t\t);\n\t\t\t\ttry {\n\t\t\t\t\tawait fs.rm(pathStr, { force: true, recursive: true });\n\t\t\t\t\tdeleted = true;\n\t\t\t\t\treturn;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tif (isWindows) {\n\t\t\t\t\t\tconst match = /EPERM: operation not permitted, unlink '(.*?)'/i.exec((ex as GitError).message);\n\t\t\t\t\t\tif (match != null) {\n\t\t\t\t\t\t\tscope?.warn(\n\t\t\t\t\t\t\t\t`Failed to manually delete '${pathStr}' because it is in use. Attempting to forcefully delete it.`,\n\t\t\t\t\t\t\t);\n\n\t\t\t\t\t\t\t// Windows-specific: `del` via cmd.exe to force-remove a locked symlink.\n\t\t\t\t\t\t\t// Node's fs.rm/unlink fails with EPERM when the symlink target is in use.\n\t\t\t\t\t\t\tfunction deleteInUseSymlink(symlink: string) {\n\t\t\t\t\t\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t\t\t\t\t\texec(`del \"${symlink}\"`, (ex, stdout, stderr) => {\n\t\t\t\t\t\t\t\t\t\tif (ex) {\n\t\t\t\t\t\t\t\t\t\t\treject(ex instanceof Error ? ex : new Error(String(ex)));\n\t\t\t\t\t\t\t\t\t\t} else if (stderr) {\n\t\t\t\t\t\t\t\t\t\t\treject(new Error(stderr));\n\t\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\t\tresolve(stdout);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst [, file] = match;\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tawait deleteInUseSymlink(file);\n\t\t\t\t\t\t\t\tawait fs.rm(pathStr, { force: true, recursive: true, maxRetries: 1, retryDelay: 1 });\n\t\t\t\t\t\t\t\tdeleted = true;\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t} catch (ex) {\n\t\t\t\t\t\t\t\tscope?.error(ex, `Failed to forcefully delete '${pathStr}' because it is in use.`);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthrow gitError;\n\t\t} finally {\n\t\t\tif (deleted) {\n\t\t\t\t// Clean up Cache registry + per-worktree cache entries for the deleted worktree.\n\t\t\t\t// Order matters: unregister before emitting the repo-level change so any listener\n\t\t\t\t// that re-queries worktree-aware state sees the registry already updated.\n\t\t\t\tthis.cache.unregisterRepoPath(pathStr);\n\t\t\t}\n\t\t\t// A failed or cancelled removal may already have taken the worktree's files or admin entry\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees', 'worktreeHead']);\n\t\t}\n\t}\n\n\t@debug()\n\tasync pruneWorktrees(repoPath: string, options?: { expire?: string }): Promise<void> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Pruning worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst before = await this.listWorktreePaths(repoPath);\n\n\t\tconst args = ['worktree', 'prune'];\n\t\tif (options?.expire) {\n\t\t\targs.push('--expire', options.expire);\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw' }, ...args);\n\n\t\t\tconst after = await this.listWorktreePaths(repoPath);\n\n\t\t\t// Order matters (see `deleteWorktree`): unregister before emitting the repo-level change so any\n\t\t\t// listener that re-queries worktree-aware state sees the registry already updated.\n\t\t\tif (before != null && after != null) {\n\t\t\t\tfor (const path of before) {\n\t\t\t\t\tif (!after.has(path)) {\n\t\t\t\t\t\tthis.cache.unregisterRepoPath(path);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktrees');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees', 'worktreeHead']);\n\t\t}\n\t}\n\n\t/**\n\t * Raw worktree paths currently known to git (`worktree <path>` lines from `--porcelain`), normalized the\n\t * same way {@link deleteWorktree} does. A failed listing (`errors: 'ignore'`) yields an empty set rather\n\t * than throwing — used by `pruneWorktrees` to diff before/after, where a transient read failure must not\n\t * be mistaken for every worktree having vanished.\n\t */\n\tprivate async listWorktreePaths(repoPath: string): Promise<Set<string> | undefined> {\n\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'ignore' }, 'worktree', 'list', '--porcelain');\n\t\t// A failed listing is not an empty one: read as empty, the after-prune one would unregister everything\n\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0) return undefined;\n\n\t\tconst paths = new Set<string>();\n\t\tfor (const line of result.stdout.split('\\n')) {\n\t\t\tif (line.startsWith('worktree ')) {\n\t\t\t\tpaths.add(normalizePath(toFsPath(line.slice('worktree '.length).trim())));\n\t\t\t}\n\t\t}\n\t\treturn paths;\n\t}\n\n\t@debug()\n\tasync lockWorktree(repoPath: string, path: string | Uri, options?: { reason?: string }): Promise<void> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Locking worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst args = ['worktree', 'lock'];\n\t\tif (options?.reason) {\n\t\t\targs.push('--reason', options.reason);\n\t\t}\n\t\targs.push(normalizePath(toFsPath(path)));\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw' }, ...args);\n\t\t} finally {\n\t\t\t// A lock touches no worktree's HEAD, so no worktree's git results or merge targets\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktreeList');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t}\n\t}\n\n\t@debug()\n\tasync unlockWorktree(repoPath: string, path: string | Uri): Promise<void> {\n\t\tawait this.git.ensureSupports('git:worktrees', (requiredVersion, installedVersion) =>\n\t\t\tl10n.t(\n\t\t\t\t'Unlocking worktrees requires a newer version of Git (>= {0}) than is currently installed ({1}). Please install a more recent version of Git and try again.',\n\t\t\t\trequiredVersion,\n\t\t\t\tinstalledVersion,\n\t\t\t),\n\t\t);\n\n\t\tconst pathStr = normalizePath(toFsPath(path));\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, errors: 'throw' }, 'worktree', 'unlock', pathStr);\n\t\t} finally {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'worktreeList');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['worktrees']);\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -26,6 +26,8 @@ export interface CliGitServiceOptions {
26
26
  gitOptions?: CliGitProviderOptions['gitOptions'];
27
27
  /** Use an existing Cache instance, or one will be created */
28
28
  cache?: CliGitProviderOptions['cache'];
29
+ /** Folders, relative to a repository's common git dir, whose worktrees core acts as if didn't exist */
30
+ ignoredWorktreeFolders?: CliGitProviderOptions['ignoredWorktreeFolders'];
29
31
  /**
30
32
  * Filesystem watching provider. When provided, the {@link GitService}
31
33
  * will watch repositories for changes (file creates, edits, deletes).
@@ -26,14 +26,16 @@ function createCliGitService(options) {
26
26
  const service = GitService.createSingleton(options.watchingProvider, {
27
27
  watchPolling: options.watchPolling,
28
28
  verifyBaseRemeasure: options.verifyBaseRemeasure,
29
- readFile: (path) => options.context.fs.readFile(fileUri(path))
29
+ readFile: (path) => options.context.fs.readFile(fileUri(path)),
30
+ readDirectory: (path) => options.context.fs.readDirectory(fileUri(path))
30
31
  });
31
32
  const locator = options.locator ?? (() => findGitPath(options.gitPath ?? null));
32
33
  const provider = new CliGitProvider({
33
34
  context: options.context,
34
35
  locator,
35
36
  gitOptions: options.gitOptions,
36
- cache: options.cache
37
+ cache: options.cache,
38
+ ignoredWorktreeFolders: options.ignoredWorktreeFolders
37
39
  });
38
40
  service.register(provider, () => true);
39
41
  service.watchService?.onDidChangeWorkingTree((repoPath) => provider.cache.onWorkingTreeChanged(repoPath));
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","names":[],"sources":["../../../git-cli/src/service.ts"],"sourcesContent":["import { GitService } from '@gitlens/git/service.js';\nimport type { FileWatchingProvider } from '@gitlens/git/watching/provider.js';\nimport type { WatchPollingOptions } from '@gitlens/git/watching/watchService.js';\nimport type { WorktreeSummaryServiceOptions } from '@gitlens/git/watching/worktreeSummary.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderOptions } from './cliGitProvider.js';\nimport { CliGitProvider } from './cliGitProvider.js';\nimport type { GitLocation } from './exec/locator.js';\nimport { findGitPath } from './exec/locator.js';\n\n/**\n * Options for {@link createCliGitService}.\n */\nexport interface CliGitServiceOptions {\n\t/**\n\t * Path(s) to the git binary.\n\t * Auto-detected if omitted (searches PATH, platform-specific locations).\n\t * Ignored when {@link locator} is provided.\n\t */\n\tgitPath?: string | string[];\n\n\t/**\n\t * Custom git binary resolver. When provided, used instead of the\n\t * default `findGitPath(gitPath)` lookup.\n\t */\n\tlocator?: () => Promise<GitLocation>;\n\n\t/** Host-provided context hooks (cache events, workspace, integrations, telemetry, fs) */\n\tcontext: CliGitProviderOptions['context'];\n\n\t/** Git execution options passed through to the underlying Git executor */\n\tgitOptions?: CliGitProviderOptions['gitOptions'];\n\n\t/** Use an existing Cache instance, or one will be created */\n\tcache?: CliGitProviderOptions['cache'];\n\n\t/**\n\t * Filesystem watching provider. When provided, the {@link GitService}\n\t * will watch repositories for changes (file creates, edits, deletes).\n\t */\n\twatchingProvider?: FileWatchingProvider;\n\n\t/**\n\t * Polls in place of (or beside) the watchers {@link watchingProvider} reports it can't trust. When omitted, no\n\t * watcher is asked.\n\t */\n\twatchPolling?: WatchPollingOptions;\n\n\t/** For tests only; production never passes it (see `WorktreeSummaryServiceOptions.verifyBaseRemeasure`) */\n\tverifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];\n}\n\n/**\n * Creates a ready-to-use {@link GitService} backed by the CLI git provider.\n *\n * This is the primary entry point for standalone (non-VS Code) consumers\n * of the `@gitlens/git-cli` library. It creates a singleton {@link GitService},\n * wires up a {@link CliGitProvider}, and registers it for all repository paths.\n *\n * @example\n * ```typescript\n * const git = createCliGitService({ context: {} });\n *\n * const repo = git.forRepo('/path/to/repo')!;\n * const branches = await repo.branches.getBranches();\n * const status = await repo.status.getStatus();\n * await repo.ops!.fetch({ remote: 'origin' });\n *\n * git.dispose(); // cleans up provider, cache, and module-level state\n * ```\n */\nexport function createCliGitService(options: CliGitServiceOptions): GitService {\n\tconst service = GitService.createSingleton(options.watchingProvider, {\n\t\twatchPolling: options.watchPolling,\n\t\tverifyBaseRemeasure: options.verifyBaseRemeasure,\n\t\t// The host's file system reads the shared `.git/config` the watch service compares reports of\n\t\treadFile: path => options.context.fs.readFile(fileUri(path)),\n\t});\n\n\tconst locator = options.locator ?? (() => findGitPath(options.gitPath ?? null));\n\tconst provider = new CliGitProvider({\n\t\tcontext: options.context,\n\t\tlocator: locator,\n\t\tgitOptions: options.gitOptions,\n\t\tcache: options.cache,\n\t});\n\n\tservice.register(provider, () => true);\n\n\t// Advance the provider cache's status generation clock from the watch session's global events — the standalone\n\t// analogue of what the VS Code host wires in `GitProviderService`. Without this, a consumer that passes a\n\t// `watchingProvider` and drives watch sessions gets FS events but a FROZEN clock, so `dedupeByStatusGeneration`\n\t// degrades to the pre-change/post-change stale-join this branch exists to fix. Standalone has no `Repository`\n\t// models, so there are no force-fired `closed`/`lastFetched` bypassing the session for the global repo event to\n\t// miss. Subscriptions are torn down with the service (its `watchService`'s emitters are disposed on dispose).\n\tservice.watchService?.onDidChangeWorkingTree(repoPath => provider.cache.onWorkingTreeChanged(repoPath));\n\t// Announced kinds were evicted when core announced its write; evicting them again would discard what the host\n\t// re-read since\n\tservice.watchService?.onDidChangeRepository(e => {\n\t\tconst changes = e.getUnannouncedChanges();\n\t\tif (changes.length) {\n\t\t\tprovider.cache.onRepositoryChanged(e.repoPath, changes);\n\t\t}\n\t});\n\t// FETCH_HEAD maps to no session change, and there's no `Repository` model here to force-fire `lastFetched` (as\n\t// the VS Code host does), so bridge it straight to the cache or the last-fetched time is never re-read.\n\tservice.watchService?.onDidChangeFetchHead(repoPath =>\n\t\tprovider.cache.onRepositoryChanged(repoPath, ['lastFetched']),\n\t);\n\treturn service;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAuEA,SAAgB,oBAAoB,SAA2C;CAC9E,MAAM,UAAU,WAAW,gBAAgB,QAAQ,kBAAkB;EACpE,cAAc,QAAQ;EACtB,qBAAqB,QAAQ;EAE7B,WAAU,SAAQ,QAAQ,QAAQ,GAAG,SAAS,QAAQ,IAAI,CAAC;CAC5D,CAAC;CAED,MAAM,UAAU,QAAQ,kBAAkB,YAAY,QAAQ,WAAW,IAAI;CAC7E,MAAM,WAAW,IAAI,eAAe;EACnC,SAAS,QAAQ;EACR;EACT,YAAY,QAAQ;EACpB,OAAO,QAAQ;CAChB,CAAC;CAED,QAAQ,SAAS,gBAAgB,IAAI;CAQrC,QAAQ,cAAc,wBAAuB,aAAY,SAAS,MAAM,qBAAqB,QAAQ,CAAC;CAGtG,QAAQ,cAAc,uBAAsB,MAAK;EAChD,MAAM,UAAU,EAAE,sBAAsB;EACxC,IAAI,QAAQ,QACX,SAAS,MAAM,oBAAoB,EAAE,UAAU,OAAO;CAExD,CAAC;CAGD,QAAQ,cAAc,sBAAqB,aAC1C,SAAS,MAAM,oBAAoB,UAAU,CAAC,aAAa,CAAC,CAC7D;CACA,OAAO;AACR"}
1
+ {"version":3,"file":"service.js","names":[],"sources":["../../../git-cli/src/service.ts"],"sourcesContent":["import { GitService } from '@gitlens/git/service.js';\nimport type { FileWatchingProvider } from '@gitlens/git/watching/provider.js';\nimport type { WatchPollingOptions } from '@gitlens/git/watching/watchService.js';\nimport type { WorktreeSummaryServiceOptions } from '@gitlens/git/watching/worktreeSummary.js';\nimport { fileUri } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderOptions } from './cliGitProvider.js';\nimport { CliGitProvider } from './cliGitProvider.js';\nimport type { GitLocation } from './exec/locator.js';\nimport { findGitPath } from './exec/locator.js';\n\n/**\n * Options for {@link createCliGitService}.\n */\nexport interface CliGitServiceOptions {\n\t/**\n\t * Path(s) to the git binary.\n\t * Auto-detected if omitted (searches PATH, platform-specific locations).\n\t * Ignored when {@link locator} is provided.\n\t */\n\tgitPath?: string | string[];\n\n\t/**\n\t * Custom git binary resolver. When provided, used instead of the\n\t * default `findGitPath(gitPath)` lookup.\n\t */\n\tlocator?: () => Promise<GitLocation>;\n\n\t/** Host-provided context hooks (cache events, workspace, integrations, telemetry, fs) */\n\tcontext: CliGitProviderOptions['context'];\n\n\t/** Git execution options passed through to the underlying Git executor */\n\tgitOptions?: CliGitProviderOptions['gitOptions'];\n\n\t/** Use an existing Cache instance, or one will be created */\n\tcache?: CliGitProviderOptions['cache'];\n\n\t/** Folders, relative to a repository's common git dir, whose worktrees core acts as if didn't exist */\n\tignoredWorktreeFolders?: CliGitProviderOptions['ignoredWorktreeFolders'];\n\n\t/**\n\t * Filesystem watching provider. When provided, the {@link GitService}\n\t * will watch repositories for changes (file creates, edits, deletes).\n\t */\n\twatchingProvider?: FileWatchingProvider;\n\n\t/**\n\t * Polls in place of (or beside) the watchers {@link watchingProvider} reports it can't trust. When omitted, no\n\t * watcher is asked.\n\t */\n\twatchPolling?: WatchPollingOptions;\n\n\t/** For tests only; production never passes it (see `WorktreeSummaryServiceOptions.verifyBaseRemeasure`) */\n\tverifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];\n}\n\n/**\n * Creates a ready-to-use {@link GitService} backed by the CLI git provider.\n *\n * This is the primary entry point for standalone (non-VS Code) consumers\n * of the `@gitlens/git-cli` library. It creates a singleton {@link GitService},\n * wires up a {@link CliGitProvider}, and registers it for all repository paths.\n *\n * @example\n * ```typescript\n * const git = createCliGitService({ context: {} });\n *\n * const repo = git.forRepo('/path/to/repo')!;\n * const branches = await repo.branches.getBranches();\n * const status = await repo.status.getStatus();\n * await repo.ops!.fetch({ remote: 'origin' });\n *\n * git.dispose(); // cleans up provider, cache, and module-level state\n * ```\n */\nexport function createCliGitService(options: CliGitServiceOptions): GitService {\n\tconst service = GitService.createSingleton(options.watchingProvider, {\n\t\twatchPolling: options.watchPolling,\n\t\tverifyBaseRemeasure: options.verifyBaseRemeasure,\n\t\t// The host's file system reads the shared `.git/config` the watch service compares reports of\n\t\treadFile: path => options.context.fs.readFile(fileUri(path)),\n\t\t// And lists the directory the admin entries of its worktrees are in\n\t\treadDirectory: path => options.context.fs.readDirectory(fileUri(path)),\n\t});\n\n\tconst locator = options.locator ?? (() => findGitPath(options.gitPath ?? null));\n\tconst provider = new CliGitProvider({\n\t\tcontext: options.context,\n\t\tlocator: locator,\n\t\tgitOptions: options.gitOptions,\n\t\tcache: options.cache,\n\t\tignoredWorktreeFolders: options.ignoredWorktreeFolders,\n\t});\n\n\tservice.register(provider, () => true);\n\n\t// Advance the provider cache's status generation clock from the watch session's global events — the standalone\n\t// analogue of what the VS Code host wires in `GitProviderService`. Without this, a consumer that passes a\n\t// `watchingProvider` and drives watch sessions gets FS events but a FROZEN clock, so `dedupeByStatusGeneration`\n\t// degrades to the pre-change/post-change stale-join this branch exists to fix. Standalone has no `Repository`\n\t// models, so there are no force-fired `closed`/`lastFetched` bypassing the session for the global repo event to\n\t// miss. Subscriptions are torn down with the service (its `watchService`'s emitters are disposed on dispose).\n\tservice.watchService?.onDidChangeWorkingTree(repoPath => provider.cache.onWorkingTreeChanged(repoPath));\n\t// Announced kinds were evicted when core announced its write; evicting them again would discard what the host\n\t// re-read since\n\tservice.watchService?.onDidChangeRepository(e => {\n\t\tconst changes = e.getUnannouncedChanges();\n\t\tif (changes.length) {\n\t\t\tprovider.cache.onRepositoryChanged(e.repoPath, changes);\n\t\t}\n\t});\n\t// FETCH_HEAD maps to no session change, and there's no `Repository` model here to force-fire `lastFetched` (as\n\t// the VS Code host does), so bridge it straight to the cache or the last-fetched time is never re-read.\n\tservice.watchService?.onDidChangeFetchHead(repoPath =>\n\t\tprovider.cache.onRepositoryChanged(repoPath, ['lastFetched']),\n\t);\n\treturn service;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,oBAAoB,SAA2C;CAC9E,MAAM,UAAU,WAAW,gBAAgB,QAAQ,kBAAkB;EACpE,cAAc,QAAQ;EACtB,qBAAqB,QAAQ;EAE7B,WAAU,SAAQ,QAAQ,QAAQ,GAAG,SAAS,QAAQ,IAAI,CAAC;EAE3D,gBAAe,SAAQ,QAAQ,QAAQ,GAAG,cAAc,QAAQ,IAAI,CAAC;CACtE,CAAC;CAED,MAAM,UAAU,QAAQ,kBAAkB,YAAY,QAAQ,WAAW,IAAI;CAC7E,MAAM,WAAW,IAAI,eAAe;EACnC,SAAS,QAAQ;EACR;EACT,YAAY,QAAQ;EACpB,OAAO,QAAQ;EACf,wBAAwB,QAAQ;CACjC,CAAC;CAED,QAAQ,SAAS,gBAAgB,IAAI;CAQrC,QAAQ,cAAc,wBAAuB,aAAY,SAAS,MAAM,qBAAqB,QAAQ,CAAC;CAGtG,QAAQ,cAAc,uBAAsB,MAAK;EAChD,MAAM,UAAU,EAAE,sBAAsB;EACxC,IAAI,QAAQ,QACX,SAAS,MAAM,oBAAoB,EAAE,UAAU,OAAO;CAExD,CAAC;CAGD,QAAQ,cAAc,sBAAqB,aAC1C,SAAS,MAAM,oBAAoB,UAAU,CAAC,aAAa,CAAC,CAC7D;CACA,OAAO;AACR"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gitkraken/core-gitlens",
3
3
  "description": "GitLens core — shared Git / AI / GitHub primitives for internal GitKraken consumption",
4
- "version": "0.16.5",
4
+ "version": "0.17.0",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "GitKraken",