@gitkraken/core-gitlens 0.16.0 → 0.16.1

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 (34) hide show
  1. package/CHANGELOG.md +15 -1
  2. package/dist/git/cache.js +2 -2
  3. package/dist/git/cache.js.map +1 -1
  4. package/dist/git/run.types.d.ts +3 -0
  5. package/dist/git/watching/classifyChange.js +3 -0
  6. package/dist/git/watching/classifyChange.js.map +1 -1
  7. package/dist/git/watching/watchSession.d.ts +2 -0
  8. package/dist/git/watching/watchSession.js +9 -1
  9. package/dist/git/watching/watchSession.js.map +1 -1
  10. package/dist/git-cli/cliGitProvider.d.ts +1 -1
  11. package/dist/git-cli/cliGitProvider.js +7 -4
  12. package/dist/git-cli/cliGitProvider.js.map +1 -1
  13. package/dist/git-cli/exec/commandClassifier.d.ts +18 -3
  14. package/dist/git-cli/exec/commandClassifier.js +562 -16
  15. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  16. package/dist/git-cli/exec/exec.d.ts +2 -2
  17. package/dist/git-cli/exec/git.d.ts +31 -4
  18. package/dist/git-cli/exec/git.js +105 -43
  19. package/dist/git-cli/exec/git.js.map +1 -1
  20. package/dist/git-cli/exec/headState.d.ts +32 -0
  21. package/dist/git-cli/exec/headState.js +78 -0
  22. package/dist/git-cli/exec/headState.js.map +1 -0
  23. package/dist/git-cli/providers/branches.js +14 -4
  24. package/dist/git-cli/providers/branches.js.map +1 -1
  25. package/dist/git-cli/providers/operations.d.ts +6 -7
  26. package/dist/git-cli/providers/operations.js +46 -64
  27. package/dist/git-cli/providers/operations.js.map +1 -1
  28. package/dist/git-cli/providers/refs.d.ts +4 -1
  29. package/dist/git-cli/providers/refs.js +13 -9
  30. package/dist/git-cli/providers/refs.js.map +1 -1
  31. package/dist/utils/env/node/exec.d.ts +38 -1
  32. package/dist/utils/env/node/exec.js +14 -6
  33. package/dist/utils/env/node/exec.js.map +1 -1
  34. package/package.json +1 -1
@@ -411,11 +411,11 @@ var RefsGitSubProvider = class {
411
411
  await this.provider.branches.forgetDeletedBranch(repoPath, branch);
412
412
  } finally {
413
413
  this.context.hooks?.cache?.onReset?.(repoPath, "branches", "config");
414
- this.context.hooks?.repository?.onChanged?.(repoPath, ["config", "heads"]);
414
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["config", "heads"], [ref], { configConfinedToRefs: true });
415
415
  }
416
416
  return;
417
417
  }
418
- this.fireReferenceChanged(repoPath, ref);
418
+ this.fireReferenceChanged(repoPath, ref, false);
419
419
  }
420
420
  async updateReference(repoPath, ref, sha, options, cancellation) {
421
421
  const scope = getScopedLogger();
@@ -444,25 +444,29 @@ var RefsGitSubProvider = class {
444
444
  }
445
445
  }, ex));
446
446
  }
447
- this.fireReferenceChanged(repoPath, ref);
447
+ this.fireReferenceChanged(repoPath, ref, true);
448
448
  }
449
- /** Announces a ref mutation to the cache and repository hooks. */
450
- fireReferenceChanged(repoPath, ref) {
449
+ /**
450
+ * Announces a ref mutation to the cache and repository hooks. `deref` says git followed a symbolic `ref` (no
451
+ * `--no-deref`), so a remote's `HEAD` moved the branch it points to and stays unnamed.
452
+ */
453
+ fireReferenceChanged(repoPath, ref, deref) {
454
+ const refs = deref && ref.endsWith("/HEAD") ? void 0 : [ref];
451
455
  if (ref === "HEAD") {
452
456
  this.context.hooks?.cache?.onReset?.(repoPath, "branches", "status");
453
457
  this.context.hooks?.repository?.onChanged?.(repoPath, ["head", "heads"]);
454
458
  } else if (ref.startsWith("refs/heads/")) {
455
459
  this.context.hooks?.cache?.onReset?.(repoPath, "branches");
456
- this.context.hooks?.repository?.onChanged?.(repoPath, ["heads"]);
460
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["heads"], refs);
457
461
  } else if (ref.startsWith("refs/tags/")) {
458
462
  this.context.hooks?.cache?.onReset?.(repoPath, "tags");
459
- this.context.hooks?.repository?.onChanged?.(repoPath, ["tags"]);
463
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["tags"], refs);
460
464
  } else if (ref.startsWith("refs/remotes/")) {
461
465
  this.context.hooks?.cache?.onReset?.(repoPath, "branches");
462
- this.context.hooks?.repository?.onChanged?.(repoPath, ["remotes"]);
466
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["remotes"], refs);
463
467
  } else if (ref === "refs/stash") {
464
468
  this.context.hooks?.cache?.onReset?.(repoPath, "stashes");
465
- this.context.hooks?.repository?.onChanged?.(repoPath, ["stash"]);
469
+ this.context.hooks?.repository?.onChanged?.(repoPath, ["stash"], refs);
466
470
  }
467
471
  }
468
472
  };
@@ -1 +1 @@
1
- {"version":3,"file":"refs.js","names":[],"sources":["../../../../git-cli/src/providers/refs.ts"],"sourcesContent":["import type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { ReferenceUpdateError } from '@gitlens/git/errors.js';\nimport type { GitBranch } from '@gitlens/git/models/branch.js';\nimport type { GitReference, GitRefTip, RefRecord } from '@gitlens/git/models/reference.js';\nimport type { GitReflogEntry } from '@gitlens/git/models/reflog.js';\nimport { deletedOrMissing } from '@gitlens/git/models/revision.js';\nimport type { GitTag } from '@gitlens/git/models/tag.js';\nimport type { GitRefsSubProvider } from '@gitlens/git/providers/refs.js';\nimport type { GitCommandPriority } from '@gitlens/git/run.types.js';\nimport { isRemoteHEAD } from '@gitlens/git/utils/branch.utils.js';\nimport { createReference } from '@gitlens/git/utils/reference.utils.js';\nimport { isSha, isShaWithOptionalRevisionSuffix, isUncommitted } from '@gitlens/git/utils/revision.utils.js';\nimport { compareRefTips } from '@gitlens/git/utils/sorting.js';\nimport { CancellationError, isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateAsyncByDelimiter } from '@gitlens/utils/string.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { toFsPath } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport type { GitResult } from '../exec/exec.types.js';\nimport type { Git, GitError } from '../exec/git.js';\nimport { getGitCommandError, gitConfigsBranch, gitConfigsLog } from '../exec/git.js';\nimport { getReflogEntryParser } from '../parsers/reflogParser.js';\nimport { getRefParser } from '../parsers/refParser.js';\n\nexport class RefsGitSubProvider implements GitRefsSubProvider {\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@debug()\n\tasync checkIfCouldBeValidBranchOrTagName(repoPath: string, ref: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'throw' }, 'check-ref-format', '--branch', ref);\n\t\t\treturn Boolean(result.stdout.trim());\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Raw `git for-each-ref` records covering branches, remotes, tags, and replace-refs in a single\n\t * pass. CLI-internal — siblings call via `this.provider.refs.getRefs(...)`. The `RefRecord` shape\n\t * is `for-each-ref`-specific and intentionally absent from the public `GitRefsSubProvider`\n\t * interface.\n\t */\n\t@debug()\n\tasync getRefs(repoPath: string, cancellation?: AbortSignal, options?: { force?: boolean }): Promise<RefRecord[]> {\n\t\tif (!repoPath) return [];\n\n\t\tconst scope = getScopedLogger();\n\n\t\t// Forces a fresh enumeration: evicts the shared cache entry (and the derived `refTips` projection,\n\t\t// which would otherwise still answer from the old records) so the factory below re-runs and\n\t\t// repopulates both from one spawn. Deliberately `delete`, not `invalidate` — soft-invalidation\n\t\t// leaves an in-flight entry rideable, so a caller needing a point-in-time read (the graph's tip\n\t\t// gate) could join an enumeration that started before the change it's trying to detect. Evicting\n\t\t// is safe mid-flight: the in-flight factory's settle is ownership-guarded and won't evict the\n\t\t// successor installed here.\n\t\tif (options?.force) {\n\t\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\t\tthis.cache.refs.delete(commonPath);\n\t\t\tthis.cache.refTips.delete(commonPath);\n\t\t}\n\n\t\treturn this.cache.getRefs(\n\t\t\trepoPath,\n\t\t\tasync (commonPath, cacheable, signal) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst supported = await this.git.supported('git:for-each-ref');\n\t\t\t\t\tconst parser = getRefParser(supported);\n\t\t\t\t\tconst result = await this.git.run(\n\t\t\t\t\t\t{ cwd: commonPath, cancellation: signal, configs: gitConfigsBranch, errors: 'ignore' },\n\t\t\t\t\t\t'for-each-ref',\n\t\t\t\t\t\t...parser.arguments,\n\t\t\t\t\t\t'refs/heads/',\n\t\t\t\t\t\t'refs/remotes/',\n\t\t\t\t\t\t'refs/tags/',\n\t\t\t\t\t\t// `git replace`/grafts — not surfaced to branch/tag/refTip consumers (filtered by\n\t\t\t\t\t\t// refname prefix), but needed by the graph's replace-ref change gate (graph.ts).\n\t\t\t\t\t\t'refs/replace/',\n\t\t\t\t\t);\n\t\t\t\t\t// Under `errors: 'ignore'` a cancelled run RESOLVES empty rather than throwing, so the\n\t\t\t\t\t// catch below never sees it and an empty enumeration would be cached as a real answer —\n\t\t\t\t\t// permanently, since this map has no TTL. Same guard `git.run`'s own caching path uses.\n\t\t\t\t\t// The graph's tip gate calls this with `force: true` and supersedes itself freely, so it\n\t\t\t\t\t// is routinely the sole registrant on the aggregate whose abort kills the spawn.\n\t\t\t\t\t// Covers a FAILED enumeration too, not just a cancelled one: a transient `for-each-ref`\n\t\t\t\t\t// failure (a lingering `index.lock`, EMFILE, the git dir briefly unavailable) also\n\t\t\t\t\t// resolves empty, and caching that `[]` would blank every branch, tag and ref-tip\n\t\t\t\t\t// consumer for the rest of the session. A genuinely ref-less repo still exits cleanly\n\t\t\t\t\t// with empty stdout, so it caches as before.\n\t\t\t\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0 || signal?.aborted) {\n\t\t\t\t\t\tcacheable?.invalidate();\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\tif (!result?.stdout) return [];\n\n\t\t\t\t\tusing sw = maybeStopWatch(scope, { log: { onlyExit: true, level: 'debug' } });\n\n\t\t\t\t\tconst records = [...parser.parse(result.stdout)];\n\n\t\t\t\t\tsw?.stop({ suffix: ` parsed ${records.length} ref records` });\n\n\t\t\t\t\treturn records;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tcacheable?.invalidate();\n\t\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\t@debug()\n\tasync getRefTips(\n\t\trepoPath: string,\n\t\toptions?: { include?: ReadonlyArray<'heads' | 'remotes' | 'tags'> },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRefTip[]> {\n\t\tif (!repoPath) return [];\n\n\t\tconst scope = getScopedLogger();\n\n\t\t// Cache always holds the full set; subset filtering is applied on read so callers asking\n\t\t// for different subsets share one cache entry.\n\t\tconst all = await this.cache.getRefTips(\n\t\t\trepoPath,\n\t\t\tasync (_commonPath, _cacheable, signal) => {\n\t\t\t\tconst records = await this.getRefs(repoPath, signal);\n\n\t\t\t\tusing sw = maybeStopWatch(scope, { log: { onlyExit: true, level: 'debug' } });\n\n\t\t\t\tconst tips: GitRefTip[] = [];\n\t\t\t\tfor (const record of records) {\n\t\t\t\t\tconst fullName = record.name;\n\t\t\t\t\tif (!fullName) continue;\n\t\t\t\t\t// Skip refs/remotes/<remote>/HEAD — symbolic, not a real tip.\n\t\t\t\t\tif (isRemoteHEAD(fullName)) continue;\n\n\t\t\t\t\tlet type: GitRefTip['type'];\n\t\t\t\t\tlet name: string;\n\t\t\t\t\tif (fullName.startsWith('refs/heads/')) {\n\t\t\t\t\t\ttype = 'branch';\n\t\t\t\t\t\tname = fullName.substring(11);\n\t\t\t\t\t} else if (fullName.startsWith('refs/remotes/')) {\n\t\t\t\t\t\ttype = 'remote';\n\t\t\t\t\t\tname = fullName.substring(13);\n\t\t\t\t\t} else if (fullName.startsWith('refs/tags/')) {\n\t\t\t\t\t\ttype = 'tag';\n\t\t\t\t\t\tname = fullName.substring(10);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Annotated tags: peeledObjectname is the commit SHA; objectname is the tag-object SHA.\n\t\t\t\t\t// Lightweight tags / branches: peeledObjectname is empty; objectname is already the commit SHA.\n\t\t\t\t\tconst sha = record.peeledObjectname || record.objectname;\n\n\t\t\t\t\ttips.push({ type: type, name: name, fullName: fullName, sha: sha });\n\t\t\t\t}\n\n\t\t\t\tsw?.stop({ suffix: ` projected ${tips.length} ref tips` });\n\n\t\t\t\treturn tips;\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\n\t\tconst include = options?.include;\n\t\tif (include == null || (include.includes('heads') && include.includes('remotes') && include.includes('tags'))) {\n\t\t\treturn all;\n\t\t}\n\n\t\treturn all.filter(r => {\n\t\t\tswitch (r.type) {\n\t\t\t\tcase 'branch':\n\t\t\t\t\treturn include.includes('heads');\n\t\t\t\tcase 'remote':\n\t\t\t\t\treturn include.includes('remotes');\n\t\t\t\tcase 'tag':\n\t\t\t\t\treturn include.includes('tags');\n\t\t\t}\n\t\t});\n\t}\n\n\t@debug()\n\tasync getRefsContainingShas(\n\t\trepoPath: string,\n\t\tshas: ReadonlySet<string> | readonly string[],\n\t\toldestSha: string,\n\t\toptions?: { include?: ReadonlyArray<'heads' | 'remotes' | 'tags'> },\n\t\tcancellation?: AbortSignal,\n\t): Promise<Map<string, GitRefTip[]>> {\n\t\tconst targetShas = shas instanceof Set ? shas : new Set(shas);\n\t\tif (!targetShas.size || !oldestSha) return new Map();\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst tips = await this.getRefTips(repoPath, options, cancellation);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\tif (!tips.length) return new Map();\n\n\t\t// Index tips by full ref name (for projection back to GitRefTip later) and seed the\n\t\t// propagation map by tip SHA. Multiple refs sharing a tip (e.g. local + remote-tracking)\n\t\t// collapse into one Set entry whose union covers all of them.\n\t\tconst tipsByName = new Map<string, GitRefTip>();\n\t\tconst propMap = new Map<string, Set<string>>();\n\t\tfor (const tip of tips) {\n\t\t\ttipsByName.set(tip.fullName, tip);\n\t\t\tlet names = propMap.get(tip.sha);\n\t\t\tif (names == null) {\n\t\t\t\tnames = new Set();\n\t\t\t\tpropMap.set(tip.sha, names);\n\t\t\t}\n\t\t\tnames.add(tip.fullName);\n\t\t}\n\n\t\tusing sw = maybeStopWatch(scope, { log: { onlyExit: true, level: 'debug' } });\n\n\t\t// `^<oldestSha>^@` excludes the parents of <oldestSha> and everything older. When <oldestSha>\n\t\t// is a root commit, `^@` resolves to nothing and the walk is naturally unbounded — git\n\t\t// doesn't error in that case (no negative refs to apply).\n\t\tconst stream = this.git.stream(\n\t\t\t{ cwd: repoPath, cancellation: cancellation, configs: gitConfigsBranch },\n\t\t\t'rev-list',\n\t\t\t'--topo-order',\n\t\t\t'--parents',\n\t\t\t'--all',\n\t\t\t`^${oldestSha}^@`,\n\t\t);\n\t\tusing _streamDisposer = createDisposable(() => void stream.return?.(undefined));\n\n\t\t// Topological propagation: `--topo-order` emits all children of X before X's own line, so\n\t\t// by the time we process \"X P1 P2...\" every child has already merged its set into X. We\n\t\t// then forward X's accumulated set to each parent — one-pass and correct.\n\t\ttry {\n\t\t\tfor await (const line of iterateAsyncByDelimiter(stream, '\\n')) {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\t\t\tif (!line) continue;\n\n\t\t\t\tconst firstSpace = line.indexOf(' ');\n\t\t\t\tconst sha = firstSpace === -1 ? line : line.substring(0, firstSpace);\n\t\t\t\tif (!sha) continue;\n\n\t\t\t\tconst refs = propMap.get(sha);\n\t\t\t\tif (!refs?.size) continue;\n\n\t\t\t\tif (firstSpace === -1) continue; // No parents — nothing to propagate to\n\n\t\t\t\t// Walk parent SHAs by index without splitting the whole line.\n\t\t\t\tlet start = firstSpace + 1;\n\t\t\t\twhile (start < line.length) {\n\t\t\t\t\tconst next = line.indexOf(' ', start);\n\t\t\t\t\tconst parentSha = next === -1 ? line.substring(start) : line.substring(start, next);\n\t\t\t\t\tif (parentSha) {\n\t\t\t\t\t\tlet parentRefs = propMap.get(parentSha);\n\t\t\t\t\t\tif (parentRefs == null) {\n\t\t\t\t\t\t\tparentRefs = new Set(refs);\n\t\t\t\t\t\t\tpropMap.set(parentSha, parentRefs);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor (const name of refs) {\n\t\t\t\t\t\t\t\tparentRefs.add(name);\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\tif (next === -1) break;\n\n\t\t\t\t\tstart = next + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn new Map();\n\t\t}\n\n\t\t// Project to targets, materialize GitRefTip[], sort.\n\t\tconst result = new Map<string, GitRefTip[]>();\n\t\tfor (const sha of targetShas) {\n\t\t\tconst names = propMap.get(sha);\n\t\t\tif (!names?.size) continue;\n\n\t\t\tconst refs: GitRefTip[] = [];\n\t\t\tfor (const name of names) {\n\t\t\t\tconst tip = tipsByName.get(name);\n\t\t\t\tif (tip != null) {\n\t\t\t\t\trefs.push(tip);\n\t\t\t\t}\n\t\t\t}\n\t\t\trefs.sort(compareRefTips);\n\t\t\tresult.set(sha, refs);\n\t\t}\n\n\t\tsw?.stop({ suffix: ` resolved ${result.size}/${targetShas.size} target shas` });\n\n\t\treturn result;\n\t}\n\n\t@debug()\n\tasync getMergeBase(\n\t\trepoPath: string,\n\t\tref1: string,\n\t\tref2: string,\n\t\toptions?: { forkPoint?: boolean; priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<string | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{\n\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\tcancellation: cancellation,\n\t\t\t\t\t// Why: ref1/ref2 are usually branch names; correctness relies on the gitResults cache being\n\t\t\t\t\t// cleared on 'heads'/'remotes' events when refs move. Web (no fs watcher) sees up to\n\t\t\t\t\t// `accessTTL` of staleness — acceptable trade-off for the perf win on graph/branch reads.\n\t\t\t\t\tcaching: { cache: this.cache.gitResults, options: { accessTTL: 5 * 60 * 1000 } },\n\t\t\t\t\t...(options?.priority != null ? { priority: options.priority } : undefined),\n\t\t\t\t},\n\t\t\t\t'merge-base',\n\t\t\t\toptions?.forkPoint ? '--fork-point' : undefined,\n\t\t\t\tref1,\n\t\t\t\tref2,\n\t\t\t);\n\t\t\tif (!result.stdout) return undefined;\n\n\t\t\treturn result.stdout.split('\\n')[0].trim() || undefined;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getReflogEntries(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { grep?: string; priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitReflogEntry[]> {\n\t\t// `show` so a branch named `delete`, `expire` or `exists` isn't read as that subcommand, and the trailing\n\t\t// `--` so one named like a tracked file isn't rejected as ambiguous\n\t\tconst parser = getReflogEntryParser();\n\t\tconst args = ['reflog', 'show', ...parser.arguments];\n\t\tif (options?.grep) {\n\t\t\targs.push(`--grep-reflog=${options.grep}`);\n\t\t}\n\t\targs.push(ref, '--');\n\n\t\tconst result = await this.git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\terrors: 'throw',\n\t\t\t\tcancellation: cancellation,\n\t\t\t\t// A user's `log.showSignature` would print gpg output into the records\n\t\t\t\tconfigs: gitConfigsLog,\n\t\t\t\t...(options?.priority != null ? { priority: options.priority } : undefined),\n\t\t\t},\n\t\t\t...args,\n\t\t);\n\n\t\tconst entries: GitReflogEntry[] = [];\n\t\tfor (const entry of parser.parse(result.stdout)) {\n\t\t\tentries.push({ sha: entry.sha, message: entry.subject });\n\t\t}\n\t\treturn entries;\n\t}\n\n\t@debug()\n\tasync getReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { force?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitReference | undefined> {\n\t\tif (!ref || ref === deletedOrMissing) return undefined;\n\n\t\tconst valid = options?.force\n\t\t\t? await this.validateReference(repoPath, ref, { force: true }, cancellation)\n\t\t\t: await this.isValidReference(repoPath, ref, undefined, cancellation);\n\t\tif (!valid) return undefined;\n\n\t\tif (ref !== 'HEAD' && !isShaWithOptionalRevisionSuffix(ref)) {\n\t\t\tconst branch = await this.provider.branches.getBranch(\n\t\t\t\trepoPath,\n\t\t\t\tref,\n\t\t\t\toptions?.force ? { force: true } : undefined,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (branch != null) {\n\t\t\t\treturn createReference(branch.ref, repoPath, {\n\t\t\t\t\tid: branch.id,\n\t\t\t\t\trefType: 'branch',\n\t\t\t\t\tname: branch.name,\n\t\t\t\t\tremote: branch.remote,\n\t\t\t\t\tupstream: branch.upstream,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Not forced: a tag that moves is rare enough that `force` is scoped to branches and validation.\n\t\t\tconst tag = await this.provider.tags.getTag(repoPath, ref, cancellation);\n\t\t\tif (tag != null) {\n\t\t\t\treturn createReference(tag.ref, repoPath, {\n\t\t\t\t\tid: tag.id,\n\t\t\t\t\trefType: 'tag',\n\t\t\t\t\tname: tag.name,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn createReference(ref, repoPath, { refType: 'revision' });\n\t}\n\n\t@debug()\n\tasync getSymbolicReferenceName(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<string | undefined> {\n\t\tconst result = await this.runSymbolicReferenceName(repoPath, ref, options, cancellation);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t/**\n\t * Like {@link getSymbolicReferenceName}, but tells a ref git confirmed doesn't exist (`rev-parse --verify` exit 1)\n\t * apart from a read that didn't complete (timeout, error, cancellation), which never reports `missing`.\n\t * Shares the same cached `rev-parse`, though only a found ref is cached: a missing one re-runs on every call.\n\t */\n\t@debug()\n\tasync getSymbolicReferenceNameOrMissing(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<{ name: string | undefined; missing: boolean }> {\n\t\tconst result = await this.runSymbolicReferenceName(repoPath, ref, options, cancellation);\n\t\treturn {\n\t\t\tname: result.stdout.trim() || undefined,\n\t\t\tmissing: result.completion.status === 'exited' && result.completion.code === 1,\n\t\t};\n\t}\n\n\tprivate async runSymbolicReferenceName(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitResult> {\n\t\tconst supportsEndOfOptions = await this.git.supports('git:rev-parse:end-of-options');\n\n\t\treturn this.git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\tcancellation: cancellation,\n\t\t\t\terrors: 'ignore',\n\t\t\t\t// Why: a fixed ref name's symbolic name is itself stable; only HEAD's, or whether the ref exists\n\t\t\t\t// at all, changes — and the gitResults cache is cleared on those ref events. 60s TTL is the failsafe\n\t\t\t\t// for watcher latency / web — matches the other \"resolve symbolic state\" calls in commits.ts.\n\t\t\t\tcaching: { cache: this.cache.gitResults, options: { accessTTL: 60 * 1000 } },\n\t\t\t\t...(options?.priority != null ? { priority: options.priority } : undefined),\n\t\t\t},\n\t\t\t'rev-parse',\n\t\t\t'--verify',\n\t\t\t'--quiet',\n\t\t\t'--symbolic-full-name',\n\t\t\t'--abbrev-ref',\n\t\t\tsupportsEndOfOptions ? '--end-of-options' : undefined,\n\t\t\tref,\n\t\t);\n\t}\n\n\t@debug({ args: repoPath => ({ repoPath: repoPath }) })\n\tasync hasBranchOrTag(\n\t\trepoPath: string | undefined,\n\t\toptions?: {\n\t\t\tfilter?: { branches?: (b: GitBranch) => boolean; tags?: (t: GitTag) => boolean };\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<boolean> {\n\t\tif (repoPath == null) return false;\n\n\t\tconst [{ values: branches }, { values: tags }] = await Promise.all([\n\t\t\tthis.provider.branches.getBranches(\n\t\t\t\trepoPath,\n\t\t\t\t{ filter: options?.filter?.branches, sort: false },\n\t\t\t\tcancellation,\n\t\t\t),\n\t\t\tthis.provider.tags.getTags(repoPath, { filter: options?.filter?.tags, sort: false }, cancellation),\n\t\t]);\n\n\t\treturn branches.length !== 0 || tags.length !== 0;\n\t}\n\n\t@debug()\n\tasync isValidReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\tpathOrUri?: string | Uri,\n\t\tcancellation?: AbortSignal,\n\t): Promise<boolean> {\n\t\tconst path = pathOrUri != null ? toFsPath(pathOrUri) : undefined;\n\t\tconst relativePath = path ? this.provider.getRelativePath(path, repoPath) : undefined;\n\t\treturn Boolean(\n\t\t\t(await this.validateReference(repoPath, ref, { relativePath: relativePath }, cancellation))?.length,\n\t\t);\n\t}\n\n\t@trace()\n\tasync validateReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { relativePath?: string; force?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<string | undefined> {\n\t\tif (!ref) return undefined;\n\t\tif (ref === deletedOrMissing || isUncommitted(ref)) return ref;\n\n\t\tconst relativePath = options?.relativePath;\n\t\tconst supportsEndOfOptions = await this.git.supports('git:rev-parse:end-of-options');\n\n\t\t// Why: a SHA-only validation (no path suffix) is effectively immutable — 5-min TTL is safe.\n\t\t// Otherwise the resolved SHA can shift on ref move (or working-tree change for path-scoped\n\t\t// validation); rely on gitResults being cleared on 'head'/'heads'/'remotes' events, with 60s\n\t\t// TTL as the failsafe for watcher latency / web.\n\t\tconst stable = relativePath == null && isSha(ref);\n\t\tconst result = await this.git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\tcancellation: cancellation,\n\t\t\t\terrors: 'ignore',\n\t\t\t\tcaching: {\n\t\t\t\t\tcache: this.cache.gitResults,\n\t\t\t\t\toptions: { accessTTL: stable ? 5 * 60 * 1000 : 60 * 1000 },\n\t\t\t\t\tforce: options?.force,\n\t\t\t\t},\n\t\t\t},\n\t\t\t'rev-parse',\n\t\t\t'--verify',\n\t\t\tsupportsEndOfOptions ? '--end-of-options' : undefined,\n\t\t\trelativePath ? `${ref}:./${relativePath}` : `${ref}^{commit}`,\n\t\t);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t@debug()\n\tasync deleteReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { expected?: string },\n\t\tcancellation?: AbortSignal,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\t// `--no-deref`: without it git deletes what a symbolic ref points to rather than the ref itself, so\n\t\t// deleting `refs/remotes/origin/HEAD` would take `origin/main` with it\n\t\tconst args = ['update-ref', '-d', '--no-deref', ref];\n\t\tif (options?.expected != null) {\n\t\t\targs.push(options.expected);\n\t\t}\n\n\t\t// `HEAD` is either the checked-out branch or, detached, the repository's own HEAD file — neither\n\t\t// may be deleted\n\t\tif (ref === 'HEAD') {\n\t\t\tthrow new ReferenceUpdateError({\n\t\t\t\treason: 'checkedOut',\n\t\t\t\taction: 'delete',\n\t\t\t\tref: ref,\n\t\t\t\tgitCommand: { repoPath: repoPath, args: args },\n\t\t\t});\n\t\t}\n\n\t\t// `update-ref -d` has none of `git branch -d`'s guards or cleanup, so a local branch gets them here:\n\t\t// deleting one checked out in a worktree would leave that worktree on a branch that no longer exists.\n\t\tconst branch = ref.startsWith('refs/heads/') ? ref.substring('refs/heads/'.length) : undefined;\n\t\tif (branch != null) {\n\t\t\t// Read git's worktree list directly rather than the cached one: that names a worktree's branch\n\t\t\t// only when the separately cached branch list has it, so a worktree or branch made outside core\n\t\t\t// since the last read would slip past this guard.\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, cancellation: cancellation, errors: 'throw' },\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\tif (result.stdout.split('\\n').some(l => l.trim() === `branch ${ref}`)) {\n\t\t\t\tthrow new ReferenceUpdateError({\n\t\t\t\t\treason: 'checkedOut',\n\t\t\t\t\taction: 'delete',\n\t\t\t\t\tref: ref,\n\t\t\t\t\tgitCommand: { repoPath: repoPath, args: args },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// `errors: 'throw'`: the default handler resolves a `GitWarnings` match (e.g. \"not a git\n\t\t\t// repository\"), which would report a write that never happened as done\n\t\t\tawait this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, ...args);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\tthrow getGitCommandError(\n\t\t\t\t'update-ref-delete',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew ReferenceUpdateError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treason: reason,\n\t\t\t\t\t\t\taction: 'delete',\n\t\t\t\t\t\t\tref: ref,\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\t\t}\n\n\t\tif (branch != null) {\n\t\t\ttry {\n\t\t\t\t// What `git branch -d` removes itself (the upstream and other per-branch settings), then GitLens's own\n\t\t\t\tawait this.git.run(\n\t\t\t\t\t{ cwd: repoPath, errors: 'ignore' },\n\t\t\t\t\t'config',\n\t\t\t\t\t'--remove-section',\n\t\t\t\t\t`branch.${branch}`,\n\t\t\t\t);\n\t\t\t\tawait this.provider.branches.forgetDeletedBranch(repoPath, branch);\n\t\t\t} finally {\n\t\t\t\t// The ref is already gone, so announce even if the cleanup above fails. A single combined\n\t\t\t\t// reset/announce rather than this plus `fireReferenceChanged`'s own `'branches'`/`['heads']` —\n\t\t\t\t// a branch delete only needs one of each.\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'config');\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'heads']);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.fireReferenceChanged(repoPath, ref);\n\t}\n\n\t@debug()\n\tasync updateReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\tsha: string,\n\t\toptions?: { expected?: string | 'absent' },\n\t\tcancellation?: AbortSignal,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst args = ['update-ref', ref, sha];\n\t\tif (options?.expected != null) {\n\t\t\t// Git's own compare-and-swap old-value argument, which makes the update atomic against a\n\t\t\t// concurrent writer. An EMPTY old value is how git spells \"the ref must not exist yet\"; the\n\t\t\t// empty string has to reach argv intact, which it does because the executor drops only\n\t\t\t// null/undefined.\n\t\t\targs.push(options.expected === 'absent' ? '' : options.expected);\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, ...args);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\tthrow getGitCommandError(\n\t\t\t\t'update-ref',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew ReferenceUpdateError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treason: reason,\n\t\t\t\t\t\t\taction: 'update',\n\t\t\t\t\t\t\tref: ref,\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\t\t}\n\n\t\tthis.fireReferenceChanged(repoPath, ref);\n\t}\n\n\t/** Announces a ref mutation to the cache and repository hooks. */\n\tprivate fireReferenceChanged(repoPath: string, ref: string): void {\n\t\tif (ref === 'HEAD') {\n\t\t\t// `update-ref HEAD` moves whatever HEAD resolves to — the checked-out branch, or a detached HEAD\n\t\t\t// — which also changes what the index and working tree are compared against.\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads']);\n\t\t} else if (ref.startsWith('refs/heads/')) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);\n\t\t} else if (ref.startsWith('refs/tags/')) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'tags');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['tags']);\n\t\t} else if (ref.startsWith('refs/remotes/')) {\n\t\t\t// Remote-tracking branches are read through the branch and ref-tip caches, which `'branches'`\n\t\t\t// clears; `'remotes'` covers only the configured remotes, which a ref write cannot change.\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);\n\t\t} else if (ref === 'refs/stash') {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'stashes');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['stash']);\n\t\t}\n\t\t// A ref outside those namespaces (a consumer's own bookkeeping under `refs/<tool>/`) is visible to\n\t\t// nothing core caches, and announcing it as an unknown change would make a host refresh everything\n\t\t// for a write it cannot observe.\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"refs.js","names":[],"sources":["../../../../git-cli/src/providers/refs.ts"],"sourcesContent":["import type { Cache } from '@gitlens/git/cache.js';\nimport type { GitServiceContext } from '@gitlens/git/context.js';\nimport { ReferenceUpdateError } from '@gitlens/git/errors.js';\nimport type { GitBranch } from '@gitlens/git/models/branch.js';\nimport type { GitReference, GitRefTip, RefRecord } from '@gitlens/git/models/reference.js';\nimport type { GitReflogEntry } from '@gitlens/git/models/reflog.js';\nimport { deletedOrMissing } from '@gitlens/git/models/revision.js';\nimport type { GitTag } from '@gitlens/git/models/tag.js';\nimport type { GitRefsSubProvider } from '@gitlens/git/providers/refs.js';\nimport type { GitCommandPriority } from '@gitlens/git/run.types.js';\nimport { isRemoteHEAD } from '@gitlens/git/utils/branch.utils.js';\nimport { createReference } from '@gitlens/git/utils/reference.utils.js';\nimport { isSha, isShaWithOptionalRevisionSuffix, isUncommitted } from '@gitlens/git/utils/revision.utils.js';\nimport { compareRefTips } from '@gitlens/git/utils/sorting.js';\nimport { CancellationError, isCancellationError } from '@gitlens/utils/cancellation.js';\nimport { debug, trace } from '@gitlens/utils/decorators/log.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport { getScopedLogger } from '@gitlens/utils/logger.scoped.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateAsyncByDelimiter } from '@gitlens/utils/string.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { toFsPath } from '@gitlens/utils/uri.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\nimport type { GitResult } from '../exec/exec.types.js';\nimport type { Git, GitError } from '../exec/git.js';\nimport { getGitCommandError, gitConfigsBranch, gitConfigsLog } from '../exec/git.js';\nimport { getReflogEntryParser } from '../parsers/reflogParser.js';\nimport { getRefParser } from '../parsers/refParser.js';\n\nexport class RefsGitSubProvider implements GitRefsSubProvider {\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@debug()\n\tasync checkIfCouldBeValidBranchOrTagName(repoPath: string, ref: string): Promise<boolean> {\n\t\ttry {\n\t\t\tconst result = await this.git.run({ cwd: repoPath, errors: 'throw' }, 'check-ref-format', '--branch', ref);\n\t\t\treturn Boolean(result.stdout.trim());\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Raw `git for-each-ref` records covering branches, remotes, tags, and replace-refs in a single\n\t * pass. CLI-internal — siblings call via `this.provider.refs.getRefs(...)`. The `RefRecord` shape\n\t * is `for-each-ref`-specific and intentionally absent from the public `GitRefsSubProvider`\n\t * interface.\n\t */\n\t@debug()\n\tasync getRefs(repoPath: string, cancellation?: AbortSignal, options?: { force?: boolean }): Promise<RefRecord[]> {\n\t\tif (!repoPath) return [];\n\n\t\tconst scope = getScopedLogger();\n\n\t\t// Forces a fresh enumeration: evicts the shared cache entry (and the derived `refTips` projection,\n\t\t// which would otherwise still answer from the old records) so the factory below re-runs and\n\t\t// repopulates both from one spawn. Deliberately `delete`, not `invalidate` — soft-invalidation\n\t\t// leaves an in-flight entry rideable, so a caller needing a point-in-time read (the graph's tip\n\t\t// gate) could join an enumeration that started before the change it's trying to detect. Evicting\n\t\t// is safe mid-flight: the in-flight factory's settle is ownership-guarded and won't evict the\n\t\t// successor installed here.\n\t\tif (options?.force) {\n\t\t\tconst commonPath = this.cache.getCommonPath(repoPath);\n\t\t\tthis.cache.refs.delete(commonPath);\n\t\t\tthis.cache.refTips.delete(commonPath);\n\t\t}\n\n\t\treturn this.cache.getRefs(\n\t\t\trepoPath,\n\t\t\tasync (commonPath, cacheable, signal) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst supported = await this.git.supported('git:for-each-ref');\n\t\t\t\t\tconst parser = getRefParser(supported);\n\t\t\t\t\tconst result = await this.git.run(\n\t\t\t\t\t\t{ cwd: commonPath, cancellation: signal, configs: gitConfigsBranch, errors: 'ignore' },\n\t\t\t\t\t\t'for-each-ref',\n\t\t\t\t\t\t...parser.arguments,\n\t\t\t\t\t\t'refs/heads/',\n\t\t\t\t\t\t'refs/remotes/',\n\t\t\t\t\t\t'refs/tags/',\n\t\t\t\t\t\t// `git replace`/grafts — not surfaced to branch/tag/refTip consumers (filtered by\n\t\t\t\t\t\t// refname prefix), but needed by the graph's replace-ref change gate (graph.ts).\n\t\t\t\t\t\t'refs/replace/',\n\t\t\t\t\t);\n\t\t\t\t\t// Under `errors: 'ignore'` a cancelled run RESOLVES empty rather than throwing, so the\n\t\t\t\t\t// catch below never sees it and an empty enumeration would be cached as a real answer —\n\t\t\t\t\t// permanently, since this map has no TTL. Same guard `git.run`'s own caching path uses.\n\t\t\t\t\t// The graph's tip gate calls this with `force: true` and supersedes itself freely, so it\n\t\t\t\t\t// is routinely the sole registrant on the aggregate whose abort kills the spawn.\n\t\t\t\t\t// Covers a FAILED enumeration too, not just a cancelled one: a transient `for-each-ref`\n\t\t\t\t\t// failure (a lingering `index.lock`, EMFILE, the git dir briefly unavailable) also\n\t\t\t\t\t// resolves empty, and caching that `[]` would blank every branch, tag and ref-tip\n\t\t\t\t\t// consumer for the rest of the session. A genuinely ref-less repo still exits cleanly\n\t\t\t\t\t// with empty stdout, so it caches as before.\n\t\t\t\t\tif (result.completion.status !== 'exited' || result.exitCode !== 0 || signal?.aborted) {\n\t\t\t\t\t\tcacheable?.invalidate();\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\tif (!result?.stdout) return [];\n\n\t\t\t\t\tusing sw = maybeStopWatch(scope, { log: { onlyExit: true, level: 'debug' } });\n\n\t\t\t\t\tconst records = [...parser.parse(result.stdout)];\n\n\t\t\t\t\tsw?.stop({ suffix: ` parsed ${records.length} ref records` });\n\n\t\t\t\t\treturn records;\n\t\t\t\t} catch (ex) {\n\t\t\t\t\tcacheable?.invalidate();\n\t\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\t\tscope?.error(ex);\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\t}\n\n\t@debug()\n\tasync getRefTips(\n\t\trepoPath: string,\n\t\toptions?: { include?: ReadonlyArray<'heads' | 'remotes' | 'tags'> },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRefTip[]> {\n\t\tif (!repoPath) return [];\n\n\t\tconst scope = getScopedLogger();\n\n\t\t// Cache always holds the full set; subset filtering is applied on read so callers asking\n\t\t// for different subsets share one cache entry.\n\t\tconst all = await this.cache.getRefTips(\n\t\t\trepoPath,\n\t\t\tasync (_commonPath, _cacheable, signal) => {\n\t\t\t\tconst records = await this.getRefs(repoPath, signal);\n\n\t\t\t\tusing sw = maybeStopWatch(scope, { log: { onlyExit: true, level: 'debug' } });\n\n\t\t\t\tconst tips: GitRefTip[] = [];\n\t\t\t\tfor (const record of records) {\n\t\t\t\t\tconst fullName = record.name;\n\t\t\t\t\tif (!fullName) continue;\n\t\t\t\t\t// Skip refs/remotes/<remote>/HEAD — symbolic, not a real tip.\n\t\t\t\t\tif (isRemoteHEAD(fullName)) continue;\n\n\t\t\t\t\tlet type: GitRefTip['type'];\n\t\t\t\t\tlet name: string;\n\t\t\t\t\tif (fullName.startsWith('refs/heads/')) {\n\t\t\t\t\t\ttype = 'branch';\n\t\t\t\t\t\tname = fullName.substring(11);\n\t\t\t\t\t} else if (fullName.startsWith('refs/remotes/')) {\n\t\t\t\t\t\ttype = 'remote';\n\t\t\t\t\t\tname = fullName.substring(13);\n\t\t\t\t\t} else if (fullName.startsWith('refs/tags/')) {\n\t\t\t\t\t\ttype = 'tag';\n\t\t\t\t\t\tname = fullName.substring(10);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Annotated tags: peeledObjectname is the commit SHA; objectname is the tag-object SHA.\n\t\t\t\t\t// Lightweight tags / branches: peeledObjectname is empty; objectname is already the commit SHA.\n\t\t\t\t\tconst sha = record.peeledObjectname || record.objectname;\n\n\t\t\t\t\ttips.push({ type: type, name: name, fullName: fullName, sha: sha });\n\t\t\t\t}\n\n\t\t\t\tsw?.stop({ suffix: ` projected ${tips.length} ref tips` });\n\n\t\t\t\treturn tips;\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\n\t\tconst include = options?.include;\n\t\tif (include == null || (include.includes('heads') && include.includes('remotes') && include.includes('tags'))) {\n\t\t\treturn all;\n\t\t}\n\n\t\treturn all.filter(r => {\n\t\t\tswitch (r.type) {\n\t\t\t\tcase 'branch':\n\t\t\t\t\treturn include.includes('heads');\n\t\t\t\tcase 'remote':\n\t\t\t\t\treturn include.includes('remotes');\n\t\t\t\tcase 'tag':\n\t\t\t\t\treturn include.includes('tags');\n\t\t\t}\n\t\t});\n\t}\n\n\t@debug()\n\tasync getRefsContainingShas(\n\t\trepoPath: string,\n\t\tshas: ReadonlySet<string> | readonly string[],\n\t\toldestSha: string,\n\t\toptions?: { include?: ReadonlyArray<'heads' | 'remotes' | 'tags'> },\n\t\tcancellation?: AbortSignal,\n\t): Promise<Map<string, GitRefTip[]>> {\n\t\tconst targetShas = shas instanceof Set ? shas : new Set(shas);\n\t\tif (!targetShas.size || !oldestSha) return new Map();\n\n\t\tconst scope = getScopedLogger();\n\n\t\tconst tips = await this.getRefTips(repoPath, options, cancellation);\n\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\tif (!tips.length) return new Map();\n\n\t\t// Index tips by full ref name (for projection back to GitRefTip later) and seed the\n\t\t// propagation map by tip SHA. Multiple refs sharing a tip (e.g. local + remote-tracking)\n\t\t// collapse into one Set entry whose union covers all of them.\n\t\tconst tipsByName = new Map<string, GitRefTip>();\n\t\tconst propMap = new Map<string, Set<string>>();\n\t\tfor (const tip of tips) {\n\t\t\ttipsByName.set(tip.fullName, tip);\n\t\t\tlet names = propMap.get(tip.sha);\n\t\t\tif (names == null) {\n\t\t\t\tnames = new Set();\n\t\t\t\tpropMap.set(tip.sha, names);\n\t\t\t}\n\t\t\tnames.add(tip.fullName);\n\t\t}\n\n\t\tusing sw = maybeStopWatch(scope, { log: { onlyExit: true, level: 'debug' } });\n\n\t\t// `^<oldestSha>^@` excludes the parents of <oldestSha> and everything older. When <oldestSha>\n\t\t// is a root commit, `^@` resolves to nothing and the walk is naturally unbounded — git\n\t\t// doesn't error in that case (no negative refs to apply).\n\t\tconst stream = this.git.stream(\n\t\t\t{ cwd: repoPath, cancellation: cancellation, configs: gitConfigsBranch },\n\t\t\t'rev-list',\n\t\t\t'--topo-order',\n\t\t\t'--parents',\n\t\t\t'--all',\n\t\t\t`^${oldestSha}^@`,\n\t\t);\n\t\tusing _streamDisposer = createDisposable(() => void stream.return?.(undefined));\n\n\t\t// Topological propagation: `--topo-order` emits all children of X before X's own line, so\n\t\t// by the time we process \"X P1 P2...\" every child has already merged its set into X. We\n\t\t// then forward X's accumulated set to each parent — one-pass and correct.\n\t\ttry {\n\t\t\tfor await (const line of iterateAsyncByDelimiter(stream, '\\n')) {\n\t\t\t\tif (cancellation?.aborted) throw new CancellationError();\n\t\t\t\tif (!line) continue;\n\n\t\t\t\tconst firstSpace = line.indexOf(' ');\n\t\t\t\tconst sha = firstSpace === -1 ? line : line.substring(0, firstSpace);\n\t\t\t\tif (!sha) continue;\n\n\t\t\t\tconst refs = propMap.get(sha);\n\t\t\t\tif (!refs?.size) continue;\n\n\t\t\t\tif (firstSpace === -1) continue; // No parents — nothing to propagate to\n\n\t\t\t\t// Walk parent SHAs by index without splitting the whole line.\n\t\t\t\tlet start = firstSpace + 1;\n\t\t\t\twhile (start < line.length) {\n\t\t\t\t\tconst next = line.indexOf(' ', start);\n\t\t\t\t\tconst parentSha = next === -1 ? line.substring(start) : line.substring(start, next);\n\t\t\t\t\tif (parentSha) {\n\t\t\t\t\t\tlet parentRefs = propMap.get(parentSha);\n\t\t\t\t\t\tif (parentRefs == null) {\n\t\t\t\t\t\t\tparentRefs = new Set(refs);\n\t\t\t\t\t\t\tpropMap.set(parentSha, parentRefs);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfor (const name of refs) {\n\t\t\t\t\t\t\t\tparentRefs.add(name);\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\tif (next === -1) break;\n\n\t\t\t\t\tstart = next + 1;\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (ex) {\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\tscope?.error(ex);\n\t\t\treturn new Map();\n\t\t}\n\n\t\t// Project to targets, materialize GitRefTip[], sort.\n\t\tconst result = new Map<string, GitRefTip[]>();\n\t\tfor (const sha of targetShas) {\n\t\t\tconst names = propMap.get(sha);\n\t\t\tif (!names?.size) continue;\n\n\t\t\tconst refs: GitRefTip[] = [];\n\t\t\tfor (const name of names) {\n\t\t\t\tconst tip = tipsByName.get(name);\n\t\t\t\tif (tip != null) {\n\t\t\t\t\trefs.push(tip);\n\t\t\t\t}\n\t\t\t}\n\t\t\trefs.sort(compareRefTips);\n\t\t\tresult.set(sha, refs);\n\t\t}\n\n\t\tsw?.stop({ suffix: ` resolved ${result.size}/${targetShas.size} target shas` });\n\n\t\treturn result;\n\t}\n\n\t@debug()\n\tasync getMergeBase(\n\t\trepoPath: string,\n\t\tref1: string,\n\t\tref2: string,\n\t\toptions?: { forkPoint?: boolean; priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<string | undefined> {\n\t\tconst scope = getScopedLogger();\n\n\t\ttry {\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{\n\t\t\t\t\tcwd: repoPath,\n\t\t\t\t\tcancellation: cancellation,\n\t\t\t\t\t// Why: ref1/ref2 are usually branch names; correctness relies on the gitResults cache being\n\t\t\t\t\t// cleared on 'heads'/'remotes' events when refs move. Web (no fs watcher) sees up to\n\t\t\t\t\t// `accessTTL` of staleness — acceptable trade-off for the perf win on graph/branch reads.\n\t\t\t\t\tcaching: { cache: this.cache.gitResults, options: { accessTTL: 5 * 60 * 1000 } },\n\t\t\t\t\t...(options?.priority != null ? { priority: options.priority } : undefined),\n\t\t\t\t},\n\t\t\t\t'merge-base',\n\t\t\t\toptions?.forkPoint ? '--fork-point' : undefined,\n\t\t\t\tref1,\n\t\t\t\tref2,\n\t\t\t);\n\t\t\tif (!result.stdout) return undefined;\n\n\t\t\treturn result.stdout.split('\\n')[0].trim() || undefined;\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\t@debug()\n\tasync getReflogEntries(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { grep?: string; priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitReflogEntry[]> {\n\t\t// `show` so a branch named `delete`, `expire` or `exists` isn't read as that subcommand, and the trailing\n\t\t// `--` so one named like a tracked file isn't rejected as ambiguous\n\t\tconst parser = getReflogEntryParser();\n\t\tconst args = ['reflog', 'show', ...parser.arguments];\n\t\tif (options?.grep) {\n\t\t\targs.push(`--grep-reflog=${options.grep}`);\n\t\t}\n\t\targs.push(ref, '--');\n\n\t\tconst result = await this.git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\terrors: 'throw',\n\t\t\t\tcancellation: cancellation,\n\t\t\t\t// A user's `log.showSignature` would print gpg output into the records\n\t\t\t\tconfigs: gitConfigsLog,\n\t\t\t\t...(options?.priority != null ? { priority: options.priority } : undefined),\n\t\t\t},\n\t\t\t...args,\n\t\t);\n\n\t\tconst entries: GitReflogEntry[] = [];\n\t\tfor (const entry of parser.parse(result.stdout)) {\n\t\t\tentries.push({ sha: entry.sha, message: entry.subject });\n\t\t}\n\t\treturn entries;\n\t}\n\n\t@debug()\n\tasync getReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { force?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitReference | undefined> {\n\t\tif (!ref || ref === deletedOrMissing) return undefined;\n\n\t\tconst valid = options?.force\n\t\t\t? await this.validateReference(repoPath, ref, { force: true }, cancellation)\n\t\t\t: await this.isValidReference(repoPath, ref, undefined, cancellation);\n\t\tif (!valid) return undefined;\n\n\t\tif (ref !== 'HEAD' && !isShaWithOptionalRevisionSuffix(ref)) {\n\t\t\tconst branch = await this.provider.branches.getBranch(\n\t\t\t\trepoPath,\n\t\t\t\tref,\n\t\t\t\toptions?.force ? { force: true } : undefined,\n\t\t\t\tcancellation,\n\t\t\t);\n\t\t\tif (branch != null) {\n\t\t\t\treturn createReference(branch.ref, repoPath, {\n\t\t\t\t\tid: branch.id,\n\t\t\t\t\trefType: 'branch',\n\t\t\t\t\tname: branch.name,\n\t\t\t\t\tremote: branch.remote,\n\t\t\t\t\tupstream: branch.upstream,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Not forced: a tag that moves is rare enough that `force` is scoped to branches and validation.\n\t\t\tconst tag = await this.provider.tags.getTag(repoPath, ref, cancellation);\n\t\t\tif (tag != null) {\n\t\t\t\treturn createReference(tag.ref, repoPath, {\n\t\t\t\t\tid: tag.id,\n\t\t\t\t\trefType: 'tag',\n\t\t\t\t\tname: tag.name,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\treturn createReference(ref, repoPath, { refType: 'revision' });\n\t}\n\n\t@debug()\n\tasync getSymbolicReferenceName(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<string | undefined> {\n\t\tconst result = await this.runSymbolicReferenceName(repoPath, ref, options, cancellation);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t/**\n\t * Like {@link getSymbolicReferenceName}, but tells a ref git confirmed doesn't exist (`rev-parse --verify` exit 1)\n\t * apart from a read that didn't complete (timeout, error, cancellation), which never reports `missing`.\n\t * Shares the same cached `rev-parse`, though only a found ref is cached: a missing one re-runs on every call.\n\t */\n\t@debug()\n\tasync getSymbolicReferenceNameOrMissing(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<{ name: string | undefined; missing: boolean }> {\n\t\tconst result = await this.runSymbolicReferenceName(repoPath, ref, options, cancellation);\n\t\treturn {\n\t\t\tname: result.stdout.trim() || undefined,\n\t\t\tmissing: result.completion.status === 'exited' && result.completion.code === 1,\n\t\t};\n\t}\n\n\tprivate async runSymbolicReferenceName(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitResult> {\n\t\tconst supportsEndOfOptions = await this.git.supports('git:rev-parse:end-of-options');\n\n\t\treturn this.git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\tcancellation: cancellation,\n\t\t\t\terrors: 'ignore',\n\t\t\t\t// Why: a fixed ref name's symbolic name is itself stable; only HEAD's, or whether the ref exists\n\t\t\t\t// at all, changes — and the gitResults cache is cleared on those ref events. 60s TTL is the failsafe\n\t\t\t\t// for watcher latency / web — matches the other \"resolve symbolic state\" calls in commits.ts.\n\t\t\t\tcaching: { cache: this.cache.gitResults, options: { accessTTL: 60 * 1000 } },\n\t\t\t\t...(options?.priority != null ? { priority: options.priority } : undefined),\n\t\t\t},\n\t\t\t'rev-parse',\n\t\t\t'--verify',\n\t\t\t'--quiet',\n\t\t\t'--symbolic-full-name',\n\t\t\t'--abbrev-ref',\n\t\t\tsupportsEndOfOptions ? '--end-of-options' : undefined,\n\t\t\tref,\n\t\t);\n\t}\n\n\t@debug({ args: repoPath => ({ repoPath: repoPath }) })\n\tasync hasBranchOrTag(\n\t\trepoPath: string | undefined,\n\t\toptions?: {\n\t\t\tfilter?: { branches?: (b: GitBranch) => boolean; tags?: (t: GitTag) => boolean };\n\t\t},\n\t\tcancellation?: AbortSignal,\n\t): Promise<boolean> {\n\t\tif (repoPath == null) return false;\n\n\t\tconst [{ values: branches }, { values: tags }] = await Promise.all([\n\t\t\tthis.provider.branches.getBranches(\n\t\t\t\trepoPath,\n\t\t\t\t{ filter: options?.filter?.branches, sort: false },\n\t\t\t\tcancellation,\n\t\t\t),\n\t\t\tthis.provider.tags.getTags(repoPath, { filter: options?.filter?.tags, sort: false }, cancellation),\n\t\t]);\n\n\t\treturn branches.length !== 0 || tags.length !== 0;\n\t}\n\n\t@debug()\n\tasync isValidReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\tpathOrUri?: string | Uri,\n\t\tcancellation?: AbortSignal,\n\t): Promise<boolean> {\n\t\tconst path = pathOrUri != null ? toFsPath(pathOrUri) : undefined;\n\t\tconst relativePath = path ? this.provider.getRelativePath(path, repoPath) : undefined;\n\t\treturn Boolean(\n\t\t\t(await this.validateReference(repoPath, ref, { relativePath: relativePath }, cancellation))?.length,\n\t\t);\n\t}\n\n\t@trace()\n\tasync validateReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { relativePath?: string; force?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<string | undefined> {\n\t\tif (!ref) return undefined;\n\t\tif (ref === deletedOrMissing || isUncommitted(ref)) return ref;\n\n\t\tconst relativePath = options?.relativePath;\n\t\tconst supportsEndOfOptions = await this.git.supports('git:rev-parse:end-of-options');\n\n\t\t// Why: a SHA-only validation (no path suffix) is effectively immutable — 5-min TTL is safe.\n\t\t// Otherwise the resolved SHA can shift on ref move (or working-tree change for path-scoped\n\t\t// validation); rely on gitResults being cleared on 'head'/'heads'/'remotes' events, with 60s\n\t\t// TTL as the failsafe for watcher latency / web.\n\t\tconst stable = relativePath == null && isSha(ref);\n\t\tconst result = await this.git.run(\n\t\t\t{\n\t\t\t\tcwd: repoPath,\n\t\t\t\tcancellation: cancellation,\n\t\t\t\terrors: 'ignore',\n\t\t\t\tcaching: {\n\t\t\t\t\tcache: this.cache.gitResults,\n\t\t\t\t\toptions: { accessTTL: stable ? 5 * 60 * 1000 : 60 * 1000 },\n\t\t\t\t\tforce: options?.force,\n\t\t\t\t},\n\t\t\t},\n\t\t\t'rev-parse',\n\t\t\t'--verify',\n\t\t\tsupportsEndOfOptions ? '--end-of-options' : undefined,\n\t\t\trelativePath ? `${ref}:./${relativePath}` : `${ref}^{commit}`,\n\t\t);\n\t\treturn result.stdout.trim() || undefined;\n\t}\n\n\t@debug()\n\tasync deleteReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\toptions?: { expected?: string },\n\t\tcancellation?: AbortSignal,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\t// `--no-deref`: without it git deletes what a symbolic ref points to rather than the ref itself, so\n\t\t// deleting `refs/remotes/origin/HEAD` would take `origin/main` with it\n\t\tconst args = ['update-ref', '-d', '--no-deref', ref];\n\t\tif (options?.expected != null) {\n\t\t\targs.push(options.expected);\n\t\t}\n\n\t\t// `HEAD` is either the checked-out branch or, detached, the repository's own HEAD file — neither\n\t\t// may be deleted\n\t\tif (ref === 'HEAD') {\n\t\t\tthrow new ReferenceUpdateError({\n\t\t\t\treason: 'checkedOut',\n\t\t\t\taction: 'delete',\n\t\t\t\tref: ref,\n\t\t\t\tgitCommand: { repoPath: repoPath, args: args },\n\t\t\t});\n\t\t}\n\n\t\t// `update-ref -d` has none of `git branch -d`'s guards or cleanup, so a local branch gets them here:\n\t\t// deleting one checked out in a worktree would leave that worktree on a branch that no longer exists.\n\t\tconst branch = ref.startsWith('refs/heads/') ? ref.substring('refs/heads/'.length) : undefined;\n\t\tif (branch != null) {\n\t\t\t// Read git's worktree list directly rather than the cached one: that names a worktree's branch\n\t\t\t// only when the separately cached branch list has it, so a worktree or branch made outside core\n\t\t\t// since the last read would slip past this guard.\n\t\t\tconst result = await this.git.run(\n\t\t\t\t{ cwd: repoPath, cancellation: cancellation, errors: 'throw' },\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\tif (result.stdout.split('\\n').some(l => l.trim() === `branch ${ref}`)) {\n\t\t\t\tthrow new ReferenceUpdateError({\n\t\t\t\t\treason: 'checkedOut',\n\t\t\t\t\taction: 'delete',\n\t\t\t\t\tref: ref,\n\t\t\t\t\tgitCommand: { repoPath: repoPath, args: args },\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\t// `errors: 'throw'`: the default handler resolves a `GitWarnings` match (e.g. \"not a git\n\t\t\t// repository\"), which would report a write that never happened as done\n\t\t\tawait this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, ...args);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\tthrow getGitCommandError(\n\t\t\t\t'update-ref-delete',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew ReferenceUpdateError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treason: reason,\n\t\t\t\t\t\t\taction: 'delete',\n\t\t\t\t\t\t\tref: ref,\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\t\t}\n\n\t\tif (branch != null) {\n\t\t\ttry {\n\t\t\t\t// What `git branch -d` removes itself (the upstream and other per-branch settings), then GitLens's own\n\t\t\t\tawait this.git.run(\n\t\t\t\t\t{ cwd: repoPath, errors: 'ignore' },\n\t\t\t\t\t'config',\n\t\t\t\t\t'--remove-section',\n\t\t\t\t\t`branch.${branch}`,\n\t\t\t\t);\n\t\t\t\tawait this.provider.branches.forgetDeletedBranch(repoPath, branch);\n\t\t\t} finally {\n\t\t\t\t// The ref is already gone, so announce even if the cleanup above fails. A single combined\n\t\t\t\t// reset/announce rather than this plus `fireReferenceChanged`'s own `'branches'`/`['heads']` —\n\t\t\t\t// a branch delete only needs one of each. The config change is the branch's own section.\n\t\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'config');\n\t\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['config', 'heads'], [ref], {\n\t\t\t\t\tconfigConfinedToRefs: true,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tthis.fireReferenceChanged(repoPath, ref, false);\n\t}\n\n\t@debug()\n\tasync updateReference(\n\t\trepoPath: string,\n\t\tref: string,\n\t\tsha: string,\n\t\toptions?: { expected?: string | 'absent' },\n\t\tcancellation?: AbortSignal,\n\t): Promise<void> {\n\t\tconst scope = getScopedLogger();\n\n\t\tconst args = ['update-ref', ref, sha];\n\t\tif (options?.expected != null) {\n\t\t\t// Git's own compare-and-swap old-value argument, which makes the update atomic against a\n\t\t\t// concurrent writer. An EMPTY old value is how git spells \"the ref must not exist yet\"; the\n\t\t\t// empty string has to reach argv intact, which it does because the executor drops only\n\t\t\t// null/undefined.\n\t\t\targs.push(options.expected === 'absent' ? '' : options.expected);\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, ...args);\n\t\t} catch (ex) {\n\t\t\tscope?.error(ex);\n\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\tthrow getGitCommandError(\n\t\t\t\t'update-ref',\n\t\t\t\tex as GitError,\n\t\t\t\treason =>\n\t\t\t\t\tnew ReferenceUpdateError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\treason: reason,\n\t\t\t\t\t\t\taction: 'update',\n\t\t\t\t\t\t\tref: ref,\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\t\t}\n\n\t\tthis.fireReferenceChanged(repoPath, ref, true);\n\t}\n\n\t/**\n\t * Announces a ref mutation to the cache and repository hooks. `deref` says git followed a symbolic `ref` (no\n\t * `--no-deref`), so a remote's `HEAD` moved the branch it points to and stays unnamed.\n\t */\n\tprivate fireReferenceChanged(repoPath: string, ref: string, deref: boolean): void {\n\t\tconst refs = deref && ref.endsWith('/HEAD') ? undefined : [ref];\n\t\tif (ref === 'HEAD') {\n\t\t\t// `update-ref HEAD` moves whatever HEAD resolves to — the checked-out branch, or a detached HEAD\n\t\t\t// — which also changes what the index and working tree are compared against.\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches', 'status');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['head', 'heads']);\n\t\t} else if (ref.startsWith('refs/heads/')) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['heads'], refs);\n\t\t} else if (ref.startsWith('refs/tags/')) {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'tags');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['tags'], refs);\n\t\t} else if (ref.startsWith('refs/remotes/')) {\n\t\t\t// Remote-tracking branches are read through the branch and ref-tip caches, which `'branches'`\n\t\t\t// clears; `'remotes'` covers only the configured remotes, which a ref write cannot change.\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'branches');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['remotes'], refs);\n\t\t} else if (ref === 'refs/stash') {\n\t\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'stashes');\n\t\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['stash'], refs);\n\t\t}\n\t\t// A ref outside those namespaces (a consumer's own bookkeeping under `refs/<tool>/`) is in no cached ref\n\t\t// list, and announcing it as an unknown change would make a host refresh everything for it. Only a\n\t\t// `git log --all` walk (graph, search, contributors) sees one, when it points at a commit, so a row it\n\t\t// adds or drops can stay stale until the next heads change.\n\t}\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { CancellationReason } from "../../cancellation.js";
2
- import { Writable } from "stream";
2
+ import { Readable, Writable } from "stream";
3
3
  //#region ../utils/src/env/node/exec.d.ts
4
4
  interface RunErrorContext {
5
5
  message: string;
@@ -35,6 +35,41 @@ export declare function findExecutable(exe: string, args: string[]): Promise<{
35
35
  args: string[];
36
36
  }>;
37
37
  export declare function getWindowsShortPath(path: string): Promise<string>;
38
+ /** What core uses of a spawned process; Node's `ChildProcess` satisfies it */
39
+ export interface SpawnedProcess {
40
+ readonly stdin: Writable | null;
41
+ readonly stdout: Readable | null;
42
+ readonly stderr: Readable | null;
43
+ readonly killed: boolean;
44
+ readonly exitCode: number | null;
45
+ readonly spawnargs: string[];
46
+ once(event: 'error', listener: (error: Error) => void): this;
47
+ once(event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;
48
+ removeAllListeners(): this;
49
+ }
50
+ export interface ProcessSpawnOptions {
51
+ readonly cwd?: string;
52
+ /** The child's whole environment, not additions to the spawner's own; an `undefined` value leaves the key out */
53
+ readonly env?: Record<string, string | undefined>;
54
+ /** Milliseconds; absent means none */
55
+ readonly timeout?: number;
56
+ readonly signal?: AbortSignal;
57
+ /** A `null` entry means `'pipe'` */
58
+ readonly stdio?: ['pipe' | 'ignore', null, null];
59
+ }
60
+ /**
61
+ * Launches a process in place of Node's `spawn`, and must behave as it does:
62
+ * - pipe stdout and stderr, and stdin unless `stdio[0]` is `'ignore'`, which leaves `stdin` null
63
+ * - honour `signal`, including one already aborted: kill with SIGTERM, then emit `'error'` with `name` `AbortError`,
64
+ * unless the process has already exited
65
+ * - honour `timeout`: kill with SIGTERM and set `killed`, without emitting `'error'`
66
+ * - report a killed process's `'close'` with a null code and its signal, not an exit code such as 143
67
+ * - emit no event before returning, `'error'` at most once, and `'close'` exactly once and last: also after `'error'`
68
+ * (a failed launch, an abort), and only after `stdout` and `stderr` have ended or been destroyed, with `exitCode`
69
+ * and `killed` already set
70
+ * - close the child's stdout when `stdout` is destroyed, which is how a reader that stops early ends the process
71
+ */
72
+ export type ProcessSpawner = (command: string, args: readonly string[], options: ProcessSpawnOptions) => SpawnedProcess;
38
73
  export interface RunOptions<TEncoding = BufferEncoding | 'buffer'> {
39
74
  cwd?: string;
40
75
  readonly env?: Record<string, string | undefined>;
@@ -73,6 +108,8 @@ export interface RunOptions<TEncoding = BufferEncoding | 'buffer'> {
73
108
  * Use for commands whose failure is expected (e.g. paired with `errors: 'ignore'`).
74
109
  */
75
110
  readonly quiet?: boolean;
111
+ /** Launches the process; defaults to Node's `spawn`. Only used by `runSpawn` */
112
+ readonly spawn?: ProcessSpawner;
76
113
  }
77
114
  type ExitCodeOnlyRunOptions<TEncoding = BufferEncoding | 'buffer'> = RunOptions<TEncoding> & {
78
115
  exitCodeOnly: true;
@@ -201,17 +201,24 @@ function endStdin(stream, stdin, stdinEncoding) {
201
201
  }
202
202
  function runSpawn(command, args, encoding, options) {
203
203
  const scope = getScopedLogger() ?? maybeStartScopedLogger("Exec.runSpawn");
204
- const { stdin, stdinEncoding, cancellation, decode, ...opts } = options;
204
+ const { stdin, stdinEncoding, cancellation, decode, spawn: spawner, ...opts } = options;
205
205
  return new Promise((resolve, reject) => {
206
- const proc = spawn(command, args, {
207
- ...opts,
208
- signal: cancellation
206
+ const proc = (spawner ?? spawn)(command, args, {
207
+ cwd: opts.cwd,
208
+ env: opts.env,
209
+ timeout: opts.timeout === 0 ? void 0 : opts.timeout,
210
+ signal: cancellation,
211
+ stdio: [
212
+ "pipe",
213
+ null,
214
+ null
215
+ ]
209
216
  });
210
217
  const errorEncoding = encoding === "buffer" ? "utf8" : encoding;
211
218
  const stdoutBuffers = [];
212
- proc.stdout.on("data", (data) => stdoutBuffers.push(data));
219
+ proc.stdout?.on("data", (data) => stdoutBuffers.push(data));
213
220
  const stderrBuffers = [];
214
- proc.stderr.on("data", (data) => stderrBuffers.push(data));
221
+ proc.stderr?.on("data", (data) => stderrBuffers.push(data));
215
222
  function getStdio(encoding) {
216
223
  const stdout = Buffer.concat(stdoutBuffers);
217
224
  const stderr = Buffer.concat(stderrBuffers);
@@ -272,6 +279,7 @@ function runSpawn(command, args, encoding, options) {
272
279
  stderr
273
280
  });
274
281
  });
282
+ if (proc.stdout == null || proc.stderr == null) reject(/* @__PURE__ */ new Error(`'${command}' was spawned without stdout and stderr pipes`));
275
283
  if (stdin != null) endStdin(proc.stdin, stdin, stdinEncoding);
276
284
  }).finally(() => scope?.[Symbol.dispose]());
277
285
  }
@@ -1 +1 @@
1
- {"version":3,"file":"exec.js","names":["joinPaths"],"sources":["../../../../../utils/src/env/node/exec.ts"],"sourcesContent":["import type { ExecFileException, ExecFileOptions } from 'child_process';\nimport { exec, execFile, spawn } from 'child_process';\nimport type { Stats } from 'fs';\nimport { access, constants } from 'fs';\nimport { stat } from 'fs/promises';\nimport { join as joinPaths } from 'path';\nimport * as process from 'process';\nimport type { Writable } from 'stream';\nimport type { CancellationReason } from '../../cancellation.js';\nimport { getScopedLogger, maybeStartScopedLogger } from '../../logger.scoped.js';\nimport { normalizePath } from '../../path.js';\nimport { isWindows } from './platform.js';\n\n// -- Error types --\n\ninterface RunErrorContext {\n\tmessage: string;\n\tcmd?: string | undefined;\n\tkilled?: boolean | undefined;\n\tcode?: string | number | null | undefined;\n\tsignal?: NodeJS.Signals | undefined;\n}\n\nexport class RunError extends Error {\n\treadonly cmd?: string | undefined;\n\treadonly killed?: boolean | undefined;\n\treadonly code?: string | number | undefined;\n\treadonly signal?: NodeJS.Signals | undefined;\n\treadonly stdout: string;\n\treadonly stderr: string;\n\n\tconstructor(context: RunErrorContext, stdout: string, stderr: string) {\n\t\tsuper(context.message);\n\n\t\tthis.cmd = context.cmd;\n\t\tthis.killed = context.killed;\n\t\tthis.code = context.code ?? undefined;\n\t\tthis.signal = context.signal;\n\t\tthis.stdout = stdout?.trim() ?? '';\n\t\tthis.stderr = stderr?.trim() ?? '';\n\n\t\tthis.name = 'RunError';\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\nexport class CancelledRunError extends RunError {\n\treadonly reason: CancellationReason;\n\n\tconstructor(\n\t\tcmd: string,\n\t\tkilled: boolean,\n\t\tcode?: number | string | undefined,\n\t\tsignal: NodeJS.Signals = 'SIGTERM',\n\t\treason: CancellationReason = 'unknown',\n\t) {\n\t\tsuper(\n\t\t\t{ message: `Operation cancelled; command=${cmd}`, cmd: cmd, killed: killed, code: code, signal: signal },\n\t\t\t'',\n\t\t\t'',\n\t\t);\n\n\t\tthis.reason = reason;\n\n\t\tthis.name = 'CancelledRunError';\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n// -- Execution utilities --\n\nconst slashesRegex = /[\\\\/]/;\nconst ps1Regex = /\\.ps1$/i;\nconst batOrCmdRegex = /\\.(bat|cmd)$/i;\nconst jsRegex = /\\.(js)$/i;\n\n/**\n * Search PATH to see if a file exists in any of the path folders.\n *\n * @param {string} exe The file to search for\n * @return {string} A fully qualified path, or the original path if nothing\n * is found\n *\n * @private\n */\nasync function runDownPath(exe: string): Promise<string> {\n\t// NB: Windows won't search PATH looking for executables in spawn like\n\t// Posix does\n\n\t// Files with any directory path don't get this applied\n\tif (slashesRegex.test(exe)) return exe;\n\n\tconst target = joinPaths('.', exe);\n\ttry {\n\t\tconst stats = await stat(target);\n\t\tif (stats?.isFile() && isExecutable(stats)) return target;\n\t} catch {}\n\n\tconst path = process.env.PATH;\n\tif (path != null && path.length !== 0) {\n\t\tconst haystack = path.split(isWindows ? ';' : ':');\n\t\tfor (const p of haystack) {\n\t\t\tconst needle = joinPaths(p, exe);\n\t\t\ttry {\n\t\t\t\tconst stats = await stat(needle);\n\t\t\t\tif (stats?.isFile() && isExecutable(stats)) return needle;\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\treturn exe;\n}\n\nfunction isExecutable(stats: Stats) {\n\tif (isWindows) return true;\n\n\tconst isGroup = stats.gid ? stats.gid === process.getgid?.() : true;\n\tconst isUser = stats.uid ? stats.uid === process.getuid?.() : true;\n\n\treturn Boolean(stats.mode & 0o0001 || (stats.mode & 0o0010 && isGroup) || (stats.mode & 0o0100 && isUser));\n}\n\n/**\n * Finds the executable and parameters to run on Windows. This method\n * mimics the POSIX behavior of being able to run scripts as executables by\n * replacing the passed-in executable with the script runner, for PowerShell,\n * CMD, and node scripts.\n *\n * This method also does the work of running down PATH, which spawn on Windows\n * also doesn't do, unlike on POSIX.\n */\nexport async function findExecutable(exe: string, args: string[]): Promise<{ cmd: string; args: string[] }> {\n\t// POSIX can just execute scripts directly, no need for silly goosery\n\tif (!isWindows) return { cmd: await runDownPath(exe), args: args };\n\n\tif (!(await fsExists(exe))) {\n\t\t// NB: When you write something like `surf-client ... -- surf-build` on Windows,\n\t\t// a shell would normally convert that to surf-build.cmd, but since it's passed\n\t\t// in as an argument, it doesn't happen\n\t\tconst possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];\n\t\tfor (const ext of possibleExts) {\n\t\t\tconst possibleFullPath = await runDownPath(`${exe}${ext}`);\n\n\t\t\tif (await fsExists(possibleFullPath)) return findExecutable(possibleFullPath, args);\n\t\t}\n\t}\n\n\tif (ps1Regex.test(exe)) {\n\t\tconst cmd = joinPaths(\n\t\t\tprocess.env.SYSTEMROOT ?? 'C:\\\\WINDOWS',\n\t\t\t'System32',\n\t\t\t'WindowsPowerShell',\n\t\t\t'v1.0',\n\t\t\t'PowerShell.exe',\n\t\t);\n\t\tconst psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];\n\n\t\treturn { cmd: cmd, args: [...psargs, ...args] };\n\t}\n\n\tif (batOrCmdRegex.test(exe)) {\n\t\tconst cmd = joinPaths(process.env.SYSTEMROOT ?? 'C:\\\\WINDOWS', 'System32', 'cmd.exe');\n\t\tconst cmdArgs = ['/C', exe, ...args];\n\n\t\treturn { cmd: cmd, args: cmdArgs };\n\t}\n\n\tif (jsRegex.test(exe)) {\n\t\tconst cmd = process.execPath;\n\t\tconst nodeArgs = [exe];\n\n\t\treturn { cmd: cmd, args: [...nodeArgs, ...args] };\n\t}\n\n\treturn { cmd: exe, args: args };\n}\n\nexport async function getWindowsShortPath(path: string): Promise<string> {\n\treturn new Promise<string>((resolve, reject) => {\n\t\texec(`for %I in (\"${path}\") do @echo %~sI`, (error, stdout, _stderr) => {\n\t\t\tif (error != null) {\n\t\t\t\treject(error);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tresolve(normalizePath(stdout.trim()));\n\t\t});\n\t});\n}\n\nexport interface RunOptions<TEncoding = BufferEncoding | 'buffer'> {\n\tcwd?: string;\n\treadonly env?: Record<string, string | undefined>;\n\treadonly encoding?: TEncoding;\n\t/**\n\t * Decodes non-UTF-8 output. Called when `encoding` is not utf8/binary/buffer.\n\t * Provided by the host via the `GitOptions.decode` option.\n\t * If absent and a non-UTF-8 encoding is requested, throws at runtime.\n\t */\n\treadonly decode?: (data: Uint8Array, options?: { readonly encoding: string }) => string | Promise<string>;\n\t/**\n\t * The size the output buffer to allocate to the spawned process. Set this\n\t * if you are anticipating a large amount of output.\n\t *\n\t * If not specified, this will be 10MB (10485760 bytes) which should be\n\t * enough for most Git operations.\n\t */\n\treadonly maxBuffer?: number;\n\treadonly cancellation?: AbortSignal;\n\t/**\n\t * An optional string or buffer which will be written to\n\t * the child process stdin stream immediately immediately\n\t * after spawning the process.\n\t */\n\treadonly stdin?: string | Buffer;\n\t/**\n\t * The encoding to use when writing to stdin, if the stdin\n\t * parameter is a string.\n\t */\n\treadonly stdinEncoding?: string;\n\treadonly timeout?: number;\n\t/**\n\t * When true, suppresses the `[EXEC]` stderr warning emitted on debug-enabled scopes.\n\t * Use for commands whose failure is expected (e.g. paired with `errors: 'ignore'`).\n\t */\n\treadonly quiet?: boolean;\n}\n\nconst bufferExceededRegex = /stdout maxBuffer( length)? exceeded/;\n\ntype ExitCodeOnlyRunOptions<TEncoding = BufferEncoding | 'buffer'> = RunOptions<TEncoding> & { exitCodeOnly: true };\n\nexport function run(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | string,\n\toptions: ExitCodeOnlyRunOptions<BufferEncoding>,\n): Promise<number>;\nexport function run(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | string,\n\toptions?: RunOptions<BufferEncoding>,\n): Promise<string>;\nexport function run<T extends number | string>(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | string,\n\toptions?: RunOptions<BufferEncoding> & { exitCodeOnly?: boolean },\n): Promise<T> {\n\tconst scope = getScopedLogger() ?? maybeStartScopedLogger('Exec.run');\n\n\tconst { stdin, stdinEncoding, cancellation, decode, ...opts }: RunOptions<BufferEncoding> & ExecFileOptions = {\n\t\tmaxBuffer: 1000 * 1024 * 1024,\n\t\t...options,\n\t};\n\n\tconst promise = new Promise<T>((resolve, reject) => {\n\t\tconst proc = execFile(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\t{ ...opts, signal: cancellation },\n\t\t\tasync (error: ExecFileException | null, stdout, stderr) => {\n\t\t\t\tif (options?.exitCodeOnly) {\n\t\t\t\t\tresolve((error?.code ?? proc.exitCode) as T);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (error != null) {\n\t\t\t\t\tif (error.signal === 'SIGTERM') {\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew CancelledRunError(\n\t\t\t\t\t\t\t\t`${command} ${args.join(' ')}`,\n\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t\terror.code ?? undefined,\n\t\t\t\t\t\t\t\terror.signal,\n\t\t\t\t\t\t\t\tcancellation?.aborted ? 'aborted' : error.killed ? 'timeout' : 'unknown',\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (bufferExceededRegex.test(error.message)) {\n\t\t\t\t\t\terror.message = `Command output exceeded the allocated stdout buffer. Set 'options.maxBuffer' to a larger value than ${opts.maxBuffer} bytes`;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet stdoutDecoded: string;\n\t\t\t\t\tlet stderrDecoded: string;\n\t\t\t\t\tif (encoding === 'utf8' || encoding === 'binary' || encoding === 'buffer') {\n\t\t\t\t\t\t// stdout & stderr can be `Buffer` or `string\n\t\t\t\t\t\t// oxlint-disable-next-line typescript/no-unnecessary-type-conversion\n\t\t\t\t\t\tstdoutDecoded = stdout.toString();\n\t\t\t\t\t\t// oxlint-disable-next-line typescript/no-unnecessary-type-conversion\n\t\t\t\t\t\tstderrDecoded = stderr.toString();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (decode == null) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Non-UTF-8 encoding '${encoding}' requested but no decode function configured`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstdoutDecoded = await decode(\n\t\t\t\t\t\t\tBuffer.from(stdout, 'binary'),\n\t\t\t\t\t\t\tencoding ? { encoding: encoding } : undefined,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tstderrDecoded = await decode(\n\t\t\t\t\t\t\tBuffer.from(stderr, 'binary'),\n\t\t\t\t\t\t\tencoding ? { encoding: encoding } : undefined,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treject(new RunError(error, stdoutDecoded, stderrDecoded));\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (stderr && !options?.quiet && scope?.enabled('debug')) {\n\t\t\t\t\tscope?.warn(`[EXEC] '${command} ${args.join(' ')}' \\u2022 ${stderr}`);\n\t\t\t\t}\n\n\t\t\t\tif (encoding === 'utf8' || encoding === 'binary' || encoding === 'buffer') {\n\t\t\t\t\tresolve(stdout as T);\n\t\t\t\t} else {\n\t\t\t\t\tif (decode == null) {\n\t\t\t\t\t\tthrow new Error(`Non-UTF-8 encoding '${encoding}' requested but no decode function configured`);\n\t\t\t\t\t}\n\n\t\t\t\t\tresolve(\n\t\t\t\t\t\t(await decode(\n\t\t\t\t\t\t\tBuffer.from(stdout, 'binary'),\n\t\t\t\t\t\t\tencoding ? { encoding: encoding } : undefined,\n\t\t\t\t\t\t)) as T,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tif (stdin != null) {\n\t\t\tendStdin(proc.stdin, stdin, stdinEncoding);\n\t\t}\n\t});\n\n\treturn promise.finally(() => scope?.[Symbol.dispose]());\n}\n\n/**\n * Writes a child's whole input and closes it. A child can exit, or be killed on cancellation, before reading all of\n * it, and the pipe then errors (EPIPE, or EOF on Windows). Unlistened, that error is thrown as an uncaught exception\n * and takes down the whole host process, so it is dropped: the child's exit status and stderr already report the\n * failure, and a child that chose to stop reading answered from what it read.\n */\nexport function endStdin(stream: Writable | null, stdin: string | Buffer, stdinEncoding: string | undefined): void {\n\tif (stream == null) return;\n\n\tstream.on('error', () => {});\n\tif (typeof stdin === 'string') {\n\t\tstream.end(stdin, (stdinEncoding ?? 'utf8') as BufferEncoding);\n\t} else {\n\t\tstream.end(stdin);\n\t}\n}\n\nexport interface RunExitResult {\n\t/**\n\t * The process's exit code. ABSENT when the process never exited normally — killed by a signal, in which\n\t * case {@link signal} is set. Deliberately optional rather than defaulted to `0`: a signalled process\n\t * reported as `exitCode: 0` is indistinguishable from a clean success, so callers would read a partial\n\t * or empty stdout as a complete answer.\n\t */\n\treadonly exitCode?: number;\n\t/** Set when the process was terminated by a signal instead of exiting; {@link exitCode} is then absent. */\n\treadonly signal?: NodeJS.Signals;\n\t/** Whether Node itself killed the process (its spawn `timeout`), as opposed to an external signal. */\n\treadonly killed?: boolean;\n}\n\nexport interface RunResult<T extends string | Buffer> extends RunExitResult {\n\tstdout: T;\n\tstderr: T;\n}\n\nexport function runSpawn(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | 'buffer' | string,\n\toptions: ExitCodeOnlyRunOptions,\n): Promise<RunExitResult>;\nexport function runSpawn<T extends string | Buffer>(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | 'buffer' | string,\n\toptions: RunOptions,\n): Promise<RunResult<T>>;\nexport function runSpawn<T extends string | Buffer>(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | 'buffer' | string,\n\toptions: RunOptions & { exitCodeOnly?: boolean },\n): Promise<RunExitResult | RunResult<T>> {\n\tconst scope = getScopedLogger() ?? maybeStartScopedLogger('Exec.runSpawn');\n\n\tconst { stdin, stdinEncoding, cancellation, decode, ...opts }: RunOptions = options;\n\n\tconst promise = new Promise<RunExitResult | RunResult<T>>((resolve, reject) => {\n\t\tconst proc = spawn(command, args, { ...opts, signal: cancellation });\n\t\tconst errorEncoding = encoding === 'buffer' ? 'utf8' : encoding;\n\n\t\tconst stdoutBuffers: Buffer[] = [];\n\t\tproc.stdout.on('data', (data: Buffer) => stdoutBuffers.push(data));\n\n\t\tconst stderrBuffers: Buffer[] = [];\n\t\tproc.stderr.on('data', (data: Buffer) => stderrBuffers.push(data));\n\n\t\tfunction getStdio<T>(\n\t\t\tencoding: BufferEncoding | 'buffer' | string,\n\t\t): { stdout: T; stderr: T } | Promise<{ stdout: T; stderr: T }> {\n\t\t\tconst stdout = Buffer.concat(stdoutBuffers);\n\t\t\tconst stderr = Buffer.concat(stderrBuffers);\n\t\t\tif (encoding === 'utf8' || encoding === 'binary') {\n\t\t\t\treturn { stdout: stdout.toString(encoding) as T, stderr: stderr.toString(encoding) as T };\n\t\t\t}\n\t\t\tif (encoding === 'buffer') {\n\t\t\t\treturn { stdout: stdout as T, stderr: stderr as T };\n\t\t\t}\n\n\t\t\tif (decode == null) {\n\t\t\t\tthrow new Error(`Non-UTF-8 encoding '${encoding}' requested but no decode function configured`);\n\t\t\t}\n\n\t\t\treturn Promise.all([\n\t\t\t\tdecode(stdout, encoding ? { encoding: encoding } : undefined),\n\t\t\t\tdecode(stderr, encoding ? { encoding: encoding } : undefined),\n\t\t\t]).then(([decodedStdout, decodedStderr]) => ({\n\t\t\t\tstdout: decodedStdout as T,\n\t\t\t\tstderr: decodedStderr as T,\n\t\t\t}));\n\t\t}\n\n\t\tproc.once('error', async ex => {\n\t\t\tif (ex?.name === 'AbortError') {\n\t\t\t\treject(new CancelledRunError(`${command} ${args.join(' ')}`, true, undefined, undefined, 'aborted'));\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst stdio = getStdio<string>(errorEncoding);\n\t\t\tconst { stdout, stderr } = stdio instanceof Promise ? await stdio : stdio;\n\n\t\t\treject(new RunError(ex, stdout, stderr));\n\t\t});\n\n\t\tproc.once('close', async (code, signal) => {\n\t\t\tif (options?.exitCodeOnly) {\n\t\t\t\t// Resolves unconditionally, so this is the one path that can hand back a signalled run. `code`\n\t\t\t\t// is null exactly when the process was killed — report the signal rather than coercing to `0`,\n\t\t\t\t// which would claim a clean success for a command that never finished. Note this returns BEFORE\n\t\t\t\t// the SIGTERM-to-cancellation diversion below, so callers have to classify that themselves.\n\t\t\t\tresolve({ exitCode: code ?? undefined, signal: signal ?? undefined, killed: proc.killed });\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (code !== 0 || signal) {\n\t\t\t\tconst stdio = getStdio<string>(errorEncoding);\n\t\t\t\tconst { stdout, stderr } = stdio instanceof Promise ? await stdio : stdio;\n\t\t\t\tif (stderr.length && !options?.quiet && scope?.enabled('debug')) {\n\t\t\t\t\tscope?.warn(`[EXEC] '${command} ${args.join(' ')}' \\u2022 ${stderr}`);\n\t\t\t\t}\n\n\t\t\t\tif (signal === 'SIGTERM') {\n\t\t\t\t\t// Only Node's spawn `timeout` kills it (a caller abort rejected above, from `'error'`); an\n\t\t\t\t\t// external SIGTERM leaves `proc.killed` false\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew CancelledRunError(\n\t\t\t\t\t\t\t`${command} ${args.join(' ')}`,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\tcode ?? undefined,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\tproc.killed ? 'timeout' : 'unknown',\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\treject(\n\t\t\t\t\tnew RunError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmessage: `Command failed with exit code ${code}`,\n\t\t\t\t\t\t\tcode: code,\n\t\t\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstdout,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst stdio = getStdio<T>(encoding);\n\t\t\tconst { stdout, stderr } = stdio instanceof Promise ? await stdio : stdio;\n\t\t\tif (stderr.length && !options?.quiet && scope?.enabled('debug')) {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`[EXEC] '${command} ${args.join(' ')}' \\u2022 ${typeof stderr === 'string' ? stderr : stderr.toString()}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// The guard above already rejected every non-zero and every signalled close, so this is a clean\n\t\t\t// zero exit — `code` cannot be null here. (The optional typing exists for the `exitCodeOnly`\n\t\t\t// branch, which is where a codeless result can genuinely surface.)\n\t\t\tresolve({ exitCode: code ?? undefined, signal: signal ?? undefined, stdout: stdout, stderr: stderr });\n\t\t});\n\n\t\tif (stdin != null) {\n\t\t\tendStdin(proc.stdin, stdin, stdinEncoding);\n\t\t}\n\t});\n\n\treturn promise.finally(() => scope?.[Symbol.dispose]());\n}\n\nexport async function fsExists(path: string): Promise<boolean> {\n\treturn new Promise<boolean>(resolve => access(path, constants.F_OK, err => resolve(err == null)));\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"exec.js","names":["joinPaths"],"sources":["../../../../../utils/src/env/node/exec.ts"],"sourcesContent":["import type { ExecFileException, ExecFileOptions } from 'child_process';\nimport { exec, execFile, spawn } from 'child_process';\nimport type { Stats } from 'fs';\nimport { access, constants } from 'fs';\nimport { stat } from 'fs/promises';\nimport { join as joinPaths } from 'path';\nimport * as process from 'process';\nimport type { Readable, Writable } from 'stream';\nimport type { CancellationReason } from '../../cancellation.js';\nimport { getScopedLogger, maybeStartScopedLogger } from '../../logger.scoped.js';\nimport { normalizePath } from '../../path.js';\nimport { isWindows } from './platform.js';\n\n// -- Error types --\n\ninterface RunErrorContext {\n\tmessage: string;\n\tcmd?: string | undefined;\n\tkilled?: boolean | undefined;\n\tcode?: string | number | null | undefined;\n\tsignal?: NodeJS.Signals | undefined;\n}\n\nexport class RunError extends Error {\n\treadonly cmd?: string | undefined;\n\treadonly killed?: boolean | undefined;\n\treadonly code?: string | number | undefined;\n\treadonly signal?: NodeJS.Signals | undefined;\n\treadonly stdout: string;\n\treadonly stderr: string;\n\n\tconstructor(context: RunErrorContext, stdout: string, stderr: string) {\n\t\tsuper(context.message);\n\n\t\tthis.cmd = context.cmd;\n\t\tthis.killed = context.killed;\n\t\tthis.code = context.code ?? undefined;\n\t\tthis.signal = context.signal;\n\t\tthis.stdout = stdout?.trim() ?? '';\n\t\tthis.stderr = stderr?.trim() ?? '';\n\n\t\tthis.name = 'RunError';\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\nexport class CancelledRunError extends RunError {\n\treadonly reason: CancellationReason;\n\n\tconstructor(\n\t\tcmd: string,\n\t\tkilled: boolean,\n\t\tcode?: number | string | undefined,\n\t\tsignal: NodeJS.Signals = 'SIGTERM',\n\t\treason: CancellationReason = 'unknown',\n\t) {\n\t\tsuper(\n\t\t\t{ message: `Operation cancelled; command=${cmd}`, cmd: cmd, killed: killed, code: code, signal: signal },\n\t\t\t'',\n\t\t\t'',\n\t\t);\n\n\t\tthis.reason = reason;\n\n\t\tthis.name = 'CancelledRunError';\n\t\tError.captureStackTrace?.(this, new.target);\n\t}\n}\n\n// -- Execution utilities --\n\nconst slashesRegex = /[\\\\/]/;\nconst ps1Regex = /\\.ps1$/i;\nconst batOrCmdRegex = /\\.(bat|cmd)$/i;\nconst jsRegex = /\\.(js)$/i;\n\n/**\n * Search PATH to see if a file exists in any of the path folders.\n *\n * @param {string} exe The file to search for\n * @return {string} A fully qualified path, or the original path if nothing\n * is found\n *\n * @private\n */\nasync function runDownPath(exe: string): Promise<string> {\n\t// NB: Windows won't search PATH looking for executables in spawn like\n\t// Posix does\n\n\t// Files with any directory path don't get this applied\n\tif (slashesRegex.test(exe)) return exe;\n\n\tconst target = joinPaths('.', exe);\n\ttry {\n\t\tconst stats = await stat(target);\n\t\tif (stats?.isFile() && isExecutable(stats)) return target;\n\t} catch {}\n\n\tconst path = process.env.PATH;\n\tif (path != null && path.length !== 0) {\n\t\tconst haystack = path.split(isWindows ? ';' : ':');\n\t\tfor (const p of haystack) {\n\t\t\tconst needle = joinPaths(p, exe);\n\t\t\ttry {\n\t\t\t\tconst stats = await stat(needle);\n\t\t\t\tif (stats?.isFile() && isExecutable(stats)) return needle;\n\t\t\t} catch {}\n\t\t}\n\t}\n\n\treturn exe;\n}\n\nfunction isExecutable(stats: Stats) {\n\tif (isWindows) return true;\n\n\tconst isGroup = stats.gid ? stats.gid === process.getgid?.() : true;\n\tconst isUser = stats.uid ? stats.uid === process.getuid?.() : true;\n\n\treturn Boolean(stats.mode & 0o0001 || (stats.mode & 0o0010 && isGroup) || (stats.mode & 0o0100 && isUser));\n}\n\n/**\n * Finds the executable and parameters to run on Windows. This method\n * mimics the POSIX behavior of being able to run scripts as executables by\n * replacing the passed-in executable with the script runner, for PowerShell,\n * CMD, and node scripts.\n *\n * This method also does the work of running down PATH, which spawn on Windows\n * also doesn't do, unlike on POSIX.\n */\nexport async function findExecutable(exe: string, args: string[]): Promise<{ cmd: string; args: string[] }> {\n\t// POSIX can just execute scripts directly, no need for silly goosery\n\tif (!isWindows) return { cmd: await runDownPath(exe), args: args };\n\n\tif (!(await fsExists(exe))) {\n\t\t// NB: When you write something like `surf-client ... -- surf-build` on Windows,\n\t\t// a shell would normally convert that to surf-build.cmd, but since it's passed\n\t\t// in as an argument, it doesn't happen\n\t\tconst possibleExts = ['.exe', '.bat', '.cmd', '.ps1'];\n\t\tfor (const ext of possibleExts) {\n\t\t\tconst possibleFullPath = await runDownPath(`${exe}${ext}`);\n\n\t\t\tif (await fsExists(possibleFullPath)) return findExecutable(possibleFullPath, args);\n\t\t}\n\t}\n\n\tif (ps1Regex.test(exe)) {\n\t\tconst cmd = joinPaths(\n\t\t\tprocess.env.SYSTEMROOT ?? 'C:\\\\WINDOWS',\n\t\t\t'System32',\n\t\t\t'WindowsPowerShell',\n\t\t\t'v1.0',\n\t\t\t'PowerShell.exe',\n\t\t);\n\t\tconst psargs = ['-ExecutionPolicy', 'Unrestricted', '-NoLogo', '-NonInteractive', '-File', exe];\n\n\t\treturn { cmd: cmd, args: [...psargs, ...args] };\n\t}\n\n\tif (batOrCmdRegex.test(exe)) {\n\t\tconst cmd = joinPaths(process.env.SYSTEMROOT ?? 'C:\\\\WINDOWS', 'System32', 'cmd.exe');\n\t\tconst cmdArgs = ['/C', exe, ...args];\n\n\t\treturn { cmd: cmd, args: cmdArgs };\n\t}\n\n\tif (jsRegex.test(exe)) {\n\t\tconst cmd = process.execPath;\n\t\tconst nodeArgs = [exe];\n\n\t\treturn { cmd: cmd, args: [...nodeArgs, ...args] };\n\t}\n\n\treturn { cmd: exe, args: args };\n}\n\nexport async function getWindowsShortPath(path: string): Promise<string> {\n\treturn new Promise<string>((resolve, reject) => {\n\t\texec(`for %I in (\"${path}\") do @echo %~sI`, (error, stdout, _stderr) => {\n\t\t\tif (error != null) {\n\t\t\t\treject(error);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tresolve(normalizePath(stdout.trim()));\n\t\t});\n\t});\n}\n\n/** What core uses of a spawned process; Node's `ChildProcess` satisfies it */\nexport interface SpawnedProcess {\n\treadonly stdin: Writable | null;\n\treadonly stdout: Readable | null;\n\treadonly stderr: Readable | null;\n\treadonly killed: boolean;\n\treadonly exitCode: number | null;\n\treadonly spawnargs: string[];\n\tonce(event: 'error', listener: (error: Error) => void): this;\n\tonce(event: 'close', listener: (code: number | null, signal: NodeJS.Signals | null) => void): this;\n\tremoveAllListeners(): this;\n}\n\nexport interface ProcessSpawnOptions {\n\treadonly cwd?: string;\n\t/** The child's whole environment, not additions to the spawner's own; an `undefined` value leaves the key out */\n\treadonly env?: Record<string, string | undefined>;\n\t/** Milliseconds; absent means none */\n\treadonly timeout?: number;\n\treadonly signal?: AbortSignal;\n\t/** A `null` entry means `'pipe'` */\n\treadonly stdio?: ['pipe' | 'ignore', null, null];\n}\n\n/**\n * Launches a process in place of Node's `spawn`, and must behave as it does:\n * - pipe stdout and stderr, and stdin unless `stdio[0]` is `'ignore'`, which leaves `stdin` null\n * - honour `signal`, including one already aborted: kill with SIGTERM, then emit `'error'` with `name` `AbortError`,\n * unless the process has already exited\n * - honour `timeout`: kill with SIGTERM and set `killed`, without emitting `'error'`\n * - report a killed process's `'close'` with a null code and its signal, not an exit code such as 143\n * - emit no event before returning, `'error'` at most once, and `'close'` exactly once and last: also after `'error'`\n * (a failed launch, an abort), and only after `stdout` and `stderr` have ended or been destroyed, with `exitCode`\n * and `killed` already set\n * - close the child's stdout when `stdout` is destroyed, which is how a reader that stops early ends the process\n */\nexport type ProcessSpawner = (command: string, args: readonly string[], options: ProcessSpawnOptions) => SpawnedProcess;\n\nexport interface RunOptions<TEncoding = BufferEncoding | 'buffer'> {\n\tcwd?: string;\n\treadonly env?: Record<string, string | undefined>;\n\treadonly encoding?: TEncoding;\n\t/**\n\t * Decodes non-UTF-8 output. Called when `encoding` is not utf8/binary/buffer.\n\t * Provided by the host via the `GitOptions.decode` option.\n\t * If absent and a non-UTF-8 encoding is requested, throws at runtime.\n\t */\n\treadonly decode?: (data: Uint8Array, options?: { readonly encoding: string }) => string | Promise<string>;\n\t/**\n\t * The size the output buffer to allocate to the spawned process. Set this\n\t * if you are anticipating a large amount of output.\n\t *\n\t * If not specified, this will be 10MB (10485760 bytes) which should be\n\t * enough for most Git operations.\n\t */\n\treadonly maxBuffer?: number;\n\treadonly cancellation?: AbortSignal;\n\t/**\n\t * An optional string or buffer which will be written to\n\t * the child process stdin stream immediately immediately\n\t * after spawning the process.\n\t */\n\treadonly stdin?: string | Buffer;\n\t/**\n\t * The encoding to use when writing to stdin, if the stdin\n\t * parameter is a string.\n\t */\n\treadonly stdinEncoding?: string;\n\treadonly timeout?: number;\n\t/**\n\t * When true, suppresses the `[EXEC]` stderr warning emitted on debug-enabled scopes.\n\t * Use for commands whose failure is expected (e.g. paired with `errors: 'ignore'`).\n\t */\n\treadonly quiet?: boolean;\n\t/** Launches the process; defaults to Node's `spawn`. Only used by `runSpawn` */\n\treadonly spawn?: ProcessSpawner;\n}\n\nconst bufferExceededRegex = /stdout maxBuffer( length)? exceeded/;\n\ntype ExitCodeOnlyRunOptions<TEncoding = BufferEncoding | 'buffer'> = RunOptions<TEncoding> & { exitCodeOnly: true };\n\nexport function run(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | string,\n\toptions: ExitCodeOnlyRunOptions<BufferEncoding>,\n): Promise<number>;\nexport function run(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | string,\n\toptions?: RunOptions<BufferEncoding>,\n): Promise<string>;\nexport function run<T extends number | string>(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | string,\n\toptions?: RunOptions<BufferEncoding> & { exitCodeOnly?: boolean },\n): Promise<T> {\n\tconst scope = getScopedLogger() ?? maybeStartScopedLogger('Exec.run');\n\n\tconst { stdin, stdinEncoding, cancellation, decode, ...opts }: RunOptions<BufferEncoding> & ExecFileOptions = {\n\t\tmaxBuffer: 1000 * 1024 * 1024,\n\t\t...options,\n\t};\n\n\tconst promise = new Promise<T>((resolve, reject) => {\n\t\tconst proc = execFile(\n\t\t\tcommand,\n\t\t\targs,\n\t\t\t{ ...opts, signal: cancellation },\n\t\t\tasync (error: ExecFileException | null, stdout, stderr) => {\n\t\t\t\tif (options?.exitCodeOnly) {\n\t\t\t\t\tresolve((error?.code ?? proc.exitCode) as T);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (error != null) {\n\t\t\t\t\tif (error.signal === 'SIGTERM') {\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew CancelledRunError(\n\t\t\t\t\t\t\t\t`${command} ${args.join(' ')}`,\n\t\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\t\terror.code ?? undefined,\n\t\t\t\t\t\t\t\terror.signal,\n\t\t\t\t\t\t\t\tcancellation?.aborted ? 'aborted' : error.killed ? 'timeout' : 'unknown',\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (bufferExceededRegex.test(error.message)) {\n\t\t\t\t\t\terror.message = `Command output exceeded the allocated stdout buffer. Set 'options.maxBuffer' to a larger value than ${opts.maxBuffer} bytes`;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet stdoutDecoded: string;\n\t\t\t\t\tlet stderrDecoded: string;\n\t\t\t\t\tif (encoding === 'utf8' || encoding === 'binary' || encoding === 'buffer') {\n\t\t\t\t\t\t// stdout & stderr can be `Buffer` or `string\n\t\t\t\t\t\t// oxlint-disable-next-line typescript/no-unnecessary-type-conversion\n\t\t\t\t\t\tstdoutDecoded = stdout.toString();\n\t\t\t\t\t\t// oxlint-disable-next-line typescript/no-unnecessary-type-conversion\n\t\t\t\t\t\tstderrDecoded = stderr.toString();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (decode == null) {\n\t\t\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t\t\t`Non-UTF-8 encoding '${encoding}' requested but no decode function configured`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tstdoutDecoded = await decode(\n\t\t\t\t\t\t\tBuffer.from(stdout, 'binary'),\n\t\t\t\t\t\t\tencoding ? { encoding: encoding } : undefined,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tstderrDecoded = await decode(\n\t\t\t\t\t\t\tBuffer.from(stderr, 'binary'),\n\t\t\t\t\t\t\tencoding ? { encoding: encoding } : undefined,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treject(new RunError(error, stdoutDecoded, stderrDecoded));\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (stderr && !options?.quiet && scope?.enabled('debug')) {\n\t\t\t\t\tscope?.warn(`[EXEC] '${command} ${args.join(' ')}' \\u2022 ${stderr}`);\n\t\t\t\t}\n\n\t\t\t\tif (encoding === 'utf8' || encoding === 'binary' || encoding === 'buffer') {\n\t\t\t\t\tresolve(stdout as T);\n\t\t\t\t} else {\n\t\t\t\t\tif (decode == null) {\n\t\t\t\t\t\tthrow new Error(`Non-UTF-8 encoding '${encoding}' requested but no decode function configured`);\n\t\t\t\t\t}\n\n\t\t\t\t\tresolve(\n\t\t\t\t\t\t(await decode(\n\t\t\t\t\t\t\tBuffer.from(stdout, 'binary'),\n\t\t\t\t\t\t\tencoding ? { encoding: encoding } : undefined,\n\t\t\t\t\t\t)) as T,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t);\n\n\t\tif (stdin != null) {\n\t\t\tendStdin(proc.stdin, stdin, stdinEncoding);\n\t\t}\n\t});\n\n\treturn promise.finally(() => scope?.[Symbol.dispose]());\n}\n\n/**\n * Writes a child's whole input and closes it. A child can exit, or be killed on cancellation, before reading all of\n * it, and the pipe then errors (EPIPE, or EOF on Windows). Unlistened, that error is thrown as an uncaught exception\n * and takes down the whole host process, so it is dropped: the child's exit status and stderr already report the\n * failure, and a child that chose to stop reading answered from what it read.\n */\nexport function endStdin(stream: Writable | null, stdin: string | Buffer, stdinEncoding: string | undefined): void {\n\tif (stream == null) return;\n\n\tstream.on('error', () => {});\n\tif (typeof stdin === 'string') {\n\t\tstream.end(stdin, (stdinEncoding ?? 'utf8') as BufferEncoding);\n\t} else {\n\t\tstream.end(stdin);\n\t}\n}\n\nexport interface RunExitResult {\n\t/**\n\t * The process's exit code. ABSENT when the process never exited normally — killed by a signal, in which\n\t * case {@link signal} is set. Deliberately optional rather than defaulted to `0`: a signalled process\n\t * reported as `exitCode: 0` is indistinguishable from a clean success, so callers would read a partial\n\t * or empty stdout as a complete answer.\n\t */\n\treadonly exitCode?: number;\n\t/** Set when the process was terminated by a signal instead of exiting; {@link exitCode} is then absent. */\n\treadonly signal?: NodeJS.Signals;\n\t/** Whether Node itself killed the process (its spawn `timeout`), as opposed to an external signal. */\n\treadonly killed?: boolean;\n}\n\nexport interface RunResult<T extends string | Buffer> extends RunExitResult {\n\tstdout: T;\n\tstderr: T;\n}\n\nexport function runSpawn(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | 'buffer' | string,\n\toptions: ExitCodeOnlyRunOptions,\n): Promise<RunExitResult>;\nexport function runSpawn<T extends string | Buffer>(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | 'buffer' | string,\n\toptions: RunOptions,\n): Promise<RunResult<T>>;\nexport function runSpawn<T extends string | Buffer>(\n\tcommand: string,\n\targs: readonly string[],\n\tencoding: BufferEncoding | 'buffer' | string,\n\toptions: RunOptions & { exitCodeOnly?: boolean },\n): Promise<RunExitResult | RunResult<T>> {\n\tconst scope = getScopedLogger() ?? maybeStartScopedLogger('Exec.runSpawn');\n\n\tconst { stdin, stdinEncoding, cancellation, decode, spawn: spawner, ...opts }: RunOptions = options;\n\n\tconst promise = new Promise<RunExitResult | RunResult<T>>((resolve, reject) => {\n\t\tconst proc = (spawner ?? spawn)(command, args, {\n\t\t\tcwd: opts.cwd,\n\t\t\tenv: opts.env,\n\t\t\t// 0 disables it, which the spawner contract spells as absent\n\t\t\ttimeout: opts.timeout === 0 ? undefined : opts.timeout,\n\t\t\tsignal: cancellation,\n\t\t\tstdio: ['pipe', null, null],\n\t\t});\n\t\tconst errorEncoding = encoding === 'buffer' ? 'utf8' : encoding;\n\n\t\tconst stdoutBuffers: Buffer[] = [];\n\t\tproc.stdout?.on('data', (data: Buffer) => stdoutBuffers.push(data));\n\n\t\tconst stderrBuffers: Buffer[] = [];\n\t\tproc.stderr?.on('data', (data: Buffer) => stderrBuffers.push(data));\n\n\t\tfunction getStdio<T>(\n\t\t\tencoding: BufferEncoding | 'buffer' | string,\n\t\t): { stdout: T; stderr: T } | Promise<{ stdout: T; stderr: T }> {\n\t\t\tconst stdout = Buffer.concat(stdoutBuffers);\n\t\t\tconst stderr = Buffer.concat(stderrBuffers);\n\t\t\tif (encoding === 'utf8' || encoding === 'binary') {\n\t\t\t\treturn { stdout: stdout.toString(encoding) as T, stderr: stderr.toString(encoding) as T };\n\t\t\t}\n\t\t\tif (encoding === 'buffer') {\n\t\t\t\treturn { stdout: stdout as T, stderr: stderr as T };\n\t\t\t}\n\n\t\t\tif (decode == null) {\n\t\t\t\tthrow new Error(`Non-UTF-8 encoding '${encoding}' requested but no decode function configured`);\n\t\t\t}\n\n\t\t\treturn Promise.all([\n\t\t\t\tdecode(stdout, encoding ? { encoding: encoding } : undefined),\n\t\t\t\tdecode(stderr, encoding ? { encoding: encoding } : undefined),\n\t\t\t]).then(([decodedStdout, decodedStderr]) => ({\n\t\t\t\tstdout: decodedStdout as T,\n\t\t\t\tstderr: decodedStderr as T,\n\t\t\t}));\n\t\t}\n\n\t\tproc.once('error', async ex => {\n\t\t\tif (ex?.name === 'AbortError') {\n\t\t\t\treject(new CancelledRunError(`${command} ${args.join(' ')}`, true, undefined, undefined, 'aborted'));\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst stdio = getStdio<string>(errorEncoding);\n\t\t\tconst { stdout, stderr } = stdio instanceof Promise ? await stdio : stdio;\n\n\t\t\treject(new RunError(ex, stdout, stderr));\n\t\t});\n\n\t\tproc.once('close', async (code, signal) => {\n\t\t\tif (options?.exitCodeOnly) {\n\t\t\t\t// Resolves unconditionally, so this is the one path that can hand back a signalled run. `code`\n\t\t\t\t// is null exactly when the process was killed — report the signal rather than coercing to `0`,\n\t\t\t\t// which would claim a clean success for a command that never finished. Note this returns BEFORE\n\t\t\t\t// the SIGTERM-to-cancellation diversion below, so callers have to classify that themselves.\n\t\t\t\tresolve({ exitCode: code ?? undefined, signal: signal ?? undefined, killed: proc.killed });\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (code !== 0 || signal) {\n\t\t\t\tconst stdio = getStdio<string>(errorEncoding);\n\t\t\t\tconst { stdout, stderr } = stdio instanceof Promise ? await stdio : stdio;\n\t\t\t\tif (stderr.length && !options?.quiet && scope?.enabled('debug')) {\n\t\t\t\t\tscope?.warn(`[EXEC] '${command} ${args.join(' ')}' \\u2022 ${stderr}`);\n\t\t\t\t}\n\n\t\t\t\tif (signal === 'SIGTERM') {\n\t\t\t\t\t// Only Node's spawn `timeout` kills it (a caller abort rejected above, from `'error'`); an\n\t\t\t\t\t// external SIGTERM leaves `proc.killed` false\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew CancelledRunError(\n\t\t\t\t\t\t\t`${command} ${args.join(' ')}`,\n\t\t\t\t\t\t\ttrue,\n\t\t\t\t\t\t\tcode ?? undefined,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t\tproc.killed ? 'timeout' : 'unknown',\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\treject(\n\t\t\t\t\tnew RunError(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmessage: `Command failed with exit code ${code}`,\n\t\t\t\t\t\t\tcode: code,\n\t\t\t\t\t\t\tsignal: signal ?? undefined,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tstdout,\n\t\t\t\t\t\tstderr,\n\t\t\t\t\t),\n\t\t\t\t);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst stdio = getStdio<T>(encoding);\n\t\t\tconst { stdout, stderr } = stdio instanceof Promise ? await stdio : stdio;\n\t\t\tif (stderr.length && !options?.quiet && scope?.enabled('debug')) {\n\t\t\t\tscope?.warn(\n\t\t\t\t\t`[EXEC] '${command} ${args.join(' ')}' \\u2022 ${typeof stderr === 'string' ? stderr : stderr.toString()}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// The guard above already rejected every non-zero and every signalled close, so this is a clean\n\t\t\t// zero exit — `code` cannot be null here. (The optional typing exists for the `exitCodeOnly`\n\t\t\t// branch, which is where a codeless result can genuinely surface.)\n\t\t\tresolve({ exitCode: code ?? undefined, signal: signal ?? undefined, stdout: stdout, stderr: stderr });\n\t\t});\n\n\t\t// Only a host spawner can leave these out; reading no output as empty would pass for a clean answer\n\t\tif (proc.stdout == null || proc.stderr == null) {\n\t\t\treject(new Error(`'${command}' was spawned without stdout and stderr pipes`));\n\t\t}\n\n\t\tif (stdin != null) {\n\t\t\tendStdin(proc.stdin, stdin, stdinEncoding);\n\t\t}\n\t});\n\n\treturn promise.finally(() => scope?.[Symbol.dispose]());\n}\n\nexport async function fsExists(path: string): Promise<boolean> {\n\treturn new Promise<boolean>(resolve => access(path, constants.F_OK, err => resolve(err == null)));\n}\n"],"mappings":"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"}
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.0",
4
+ "version": "0.16.1",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": {
7
7
  "name": "GitKraken",