@gitkraken/core-gitlens 0.15.0 → 0.15.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 (49) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/git/models/branch.d.ts +7 -0
  3. package/dist/git/models/branch.js +1 -0
  4. package/dist/git/models/branch.js.map +1 -1
  5. package/dist/git/models/graph.d.ts +2 -0
  6. package/dist/git/models/graph.js.map +1 -1
  7. package/dist/git/models/reference.d.ts +4 -0
  8. package/dist/git/providers/provider.d.ts +3 -1
  9. package/dist/git/providers/revision.d.ts +2 -1
  10. package/dist/git/run.types.d.ts +3 -2
  11. package/dist/git/utils/branch.utils.d.ts +15 -3
  12. package/dist/git/utils/branch.utils.js +34 -8
  13. package/dist/git/utils/branch.utils.js.map +1 -1
  14. package/dist/git/utils/reference.utils.d.ts +2 -0
  15. package/dist/git/utils/reference.utils.js.map +1 -1
  16. package/dist/git/watching/worktreeSummary.d.ts +7 -4
  17. package/dist/git/watching/worktreeSummary.js +22 -3
  18. package/dist/git/watching/worktreeSummary.js.map +1 -1
  19. package/dist/git-cli/cliGitProvider.d.ts +6 -0
  20. package/dist/git-cli/cliGitProvider.js +48 -2
  21. package/dist/git-cli/cliGitProvider.js.map +1 -1
  22. package/dist/git-cli/exec/git.d.ts +1 -0
  23. package/dist/git-cli/exec/git.js +8 -10
  24. package/dist/git-cli/exec/git.js.map +1 -1
  25. package/dist/git-cli/exec/indexLock.d.ts +9 -1
  26. package/dist/git-cli/exec/indexLock.js +19 -5
  27. package/dist/git-cli/exec/indexLock.js.map +1 -1
  28. package/dist/git-cli/parsers/logParser.d.ts +4 -0
  29. package/dist/git-cli/parsers/logParser.js +12 -2
  30. package/dist/git-cli/parsers/logParser.js.map +1 -1
  31. package/dist/git-cli/parsers/refParser.js +4 -2
  32. package/dist/git-cli/parsers/refParser.js.map +1 -1
  33. package/dist/git-cli/providers/branchRemote.d.ts +13 -1
  34. package/dist/git-cli/providers/branchRemote.js +41 -12
  35. package/dist/git-cli/providers/branchRemote.js.map +1 -1
  36. package/dist/git-cli/providers/branches.js +55 -21
  37. package/dist/git-cli/providers/branches.js.map +1 -1
  38. package/dist/git-cli/providers/graph.js +53 -32
  39. package/dist/git-cli/providers/graph.js.map +1 -1
  40. package/dist/git-cli/providers/operations.js +22 -4
  41. package/dist/git-cli/providers/operations.js.map +1 -1
  42. package/dist/git-cli/providers/revision.d.ts +8 -1
  43. package/dist/git-cli/providers/revision.js +22 -8
  44. package/dist/git-cli/providers/revision.js.map +1 -1
  45. package/dist/git-cli/providers/stash.js +3 -3
  46. package/dist/git-cli/providers/stash.js.map +1 -1
  47. package/dist/git-cli/providers/worktrees.js +3 -3
  48. package/dist/git-cli/providers/worktrees.js.map +1 -1
  49. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"indexLock.js","names":["wait"],"sources":["../../../../git-cli/src/exec/indexLock.ts"],"sourcesContent":["import { setTimeout as wait } from 'timers/promises';\nimport { leadingCommand } from './commandClassifier.js';\nimport { CancelledRunError, RunError } from './exec.errors.js';\nimport { abortReason } from './gitQueue.js';\n\n/**\n * Verbs that take `<gitdir>/index.lock` before changing anything, so a run that loses the race fails at once\n * with nothing half-done and is safe to retry. `stash` is left out because it is multi-step and can fail part\n * way, and the long or interactive verbs (`merge`, `rebase`, `cherry-pick`, `revert`, `am`, `pull`) because\n * holding the lock-ordering chain for their whole run would stall every other write to the checkout.\n */\nconst indexWritingVerbs: ReadonlySet<string> = new Set([\n\t'add',\n\t'checkout',\n\t'commit',\n\t'mv',\n\t'read-tree',\n\t'reset',\n\t'restore',\n\t'rm',\n\t'switch',\n\t'update-index',\n]);\n\nconst applyIndexOptions: ReadonlySet<string> = new Set(['--index', '--cached', '--3way', '-3']);\n\n/**\n * Matches git's `fatal: Unable to create '<path>/index.lock': File exists.` — the wording varies a little by version.\n * Anchored to the start of stderr: git takes the lock before doing anything else, so its own failure is the first\n * thing it prints past any `warning:`/`hint:` lines from parsing its arguments (`checkout` warns of an ambiguous\n * refname first). The same line after other output came from something the run started, such as a hook running its\n * own git command, after the run had already done its work — retrying would run it all again.\n */\nconst indexLockHeldRegex =\n\t/^(?:\\s*(?:warning|hint): [^\\r\\n]*\\r?\\n)*\\s*(?:fatal|error): Unable to create '[^\\r\\n]*index\\.lock': File exists/i;\n\n/** How long to wait before each retry of a run that found `index.lock` held; its length is the retry count. */\nexport const indexLockRetryDelays: readonly number[] = [100, 200, 400];\n\n/**\n * Whether the argv is a command that writes the index (`apply` only does with `--index`, `--cached` or `--3way`,\n * which implies `--index`, and never with `--check`).\n */\nexport function isIndexWriteCommand(args: readonly string[]): boolean {\n\tconst { verb, rest } = leadingCommand(args);\n\tif (verb === 'apply') return !rest.includes('--check') && rest.some(a => applyIndexOptions.has(a));\n\n\treturn indexWritingVerbs.has(verb);\n}\n\n/** Whether a run failed because another process held `index.lock`. */\nexport function isIndexLockHeldError(ex: unknown): boolean {\n\treturn ex instanceof RunError && !(ex instanceof CancelledRunError) && indexLockHeldRegex.test(ex.stderr);\n}\n\n/** Resolves after `ms`, or rejects as soon as `signal` aborts. */\nasync function delay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n\ttry {\n\t\tawait wait(ms, undefined, { signal: signal });\n\t} catch (ex) {\n\t\tif (signal?.aborted) throw abortReason(signal);\n\n\t\tthrow ex;\n\t}\n}\n\n/**\n * Captures the rest of git's `error: could not lock config file <path>[: <reason>]` line. A held lock reads\n * `: File exists`, except `--remove-section`/`--rename-section` print no reason (for any failure); permanent\n * failures (`Permission denied`, `No such file or directory`, ...) must not be retried.\n */\nconst configLockFailureRegex = /could not lock config file ([^\\r\\n]*)/i;\n\n/**\n * Whether the argv is `git config`. Only that verb: others that write config (`branch -m`, `remote add`) can\n * fail after a partial change, so a blind retry isn't safe.\n */\nexport function isConfigCommand(args: readonly string[]): boolean {\n\treturn leadingCommand(args).verb === 'config';\n}\n\n/** Whether a run failed because another process may hold the config file's `.lock`: the reason is `File exists` or absent. */\nexport function isConfigLockHeldError(ex: unknown): boolean {\n\tif (!(ex instanceof RunError) || ex instanceof CancelledRunError) return false;\n\n\tconst rest = configLockFailureRegex.exec(ex.stderr)?.[1];\n\tif (rest == null) return false;\n\n\treturn /: File exists\\s*$/i.test(rest) || !rest.includes(': ');\n}\n\n/**\n * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails with an error\n * `isHeldError` accepts. Any other failure, or the last lock failure, is rethrown as is.\n */\nasync function retryWhileHeld<T>(\n\tattempt: () => Promise<T>,\n\tsignal: AbortSignal | undefined,\n\tisHeldError: (ex: unknown) => boolean,\n\tbeforeRetry?: (lockError: Error) => void | Promise<void>,\n): Promise<T> {\n\tfor (let retry = 0; ; retry++) {\n\t\tlet lockError: Error;\n\t\ttry {\n\t\t\treturn await attempt();\n\t\t} catch (ex) {\n\t\t\tif (retry >= indexLockRetryDelays.length || !isHeldError(ex)) throw ex;\n\n\t\t\tlockError = ex as Error;\n\t\t}\n\n\t\tawait delay(indexLockRetryDelays[retry], signal);\n\t\tawait beforeRetry?.(lockError);\n\t}\n}\n\n/**\n * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails on a held\n * `index.lock`. Any other failure, or the last lock failure, is rethrown as is. `beforeRetry` runs after each\n * wait, right before the next attempt; whatever it throws ends the retrying and is rethrown.\n */\nexport function retryWhileIndexLocked<T>(\n\tattempt: () => Promise<T>,\n\tsignal: AbortSignal | undefined,\n\tbeforeRetry?: (lockError: Error) => void | Promise<void>,\n): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isIndexLockHeldError, beforeRetry);\n}\n\n/**\n * Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another\n * process — git fails at once rather than waiting.\n */\nexport function retryWhileConfigLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isConfigLockHeldError);\n}\n\n/**\n * Runs the work for each key one at a time, in arrival order. A failing run doesn't hold up the next one, and a\n * run whose signal aborts while it waits rejects at once without ever starting.\n */\nexport class KeyedSequencer {\n\t/** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */\n\tprivate readonly _tails = new Map<string, Promise<void>>();\n\n\tasync run<T>(key: string, signal: AbortSignal | undefined, fn: () => Promise<T>): Promise<T> {\n\t\tconst previous = this._tails.get(key);\n\n\t\tlet release!: () => void;\n\t\tconst tail = new Promise<void>(resolve => (release = resolve));\n\t\tthis._tails.set(key, tail);\n\n\t\tconst finish = () => {\n\t\t\trelease();\n\t\t\tif (this._tails.get(key) === tail) {\n\t\t\t\tthis._tails.delete(key);\n\t\t\t}\n\t\t};\n\n\t\tlet started = false;\n\t\ttry {\n\t\t\tif (previous != null) {\n\t\t\t\tawait waitFor(previous, signal);\n\t\t\t}\n\n\t\t\tstarted = true;\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tif (started || previous == null) {\n\t\t\t\tfinish();\n\t\t\t} else {\n\t\t\t\t// Dropped while waiting: whoever is queued behind still has to wait for the run ahead of us,\n\t\t\t\t// so the chain can only be released once that run is done.\n\t\t\t\tvoid previous.then(finish);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction waitFor(promise: Promise<void>, signal: AbortSignal | undefined): Promise<void> {\n\tif (signal == null) return promise;\n\tif (signal.aborted) return Promise.reject(abortReason(signal));\n\n\treturn new Promise<void>((resolve, reject) => {\n\t\tconst onAbort = () => reject(abortReason(signal));\n\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\t\tvoid promise.then(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\tresolve();\n\t\t});\n\t});\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"indexLock.js","names":["wait"],"sources":["../../../../git-cli/src/exec/indexLock.ts"],"sourcesContent":["import { setTimeout as wait } from 'timers/promises';\nimport { leadingCommand } from './commandClassifier.js';\nimport { CancelledRunError, RunError } from './exec.errors.js';\nimport { abortReason } from './gitQueue.js';\n\n/**\n * Verbs that take `<gitdir>/index.lock` before changing anything, so a run that loses the race fails at once\n * with nothing half-done and is safe to retry. `stash` is left out because it is multi-step and can fail part\n * way, and the long or interactive verbs (`merge`, `rebase`, `cherry-pick`, `revert`, `am`, `pull`) because\n * holding the lock-ordering chain for their whole run would stall every other write to the checkout.\n */\nconst indexWritingVerbs: ReadonlySet<string> = new Set([\n\t'add',\n\t'checkout',\n\t'commit',\n\t'mv',\n\t'read-tree',\n\t'reset',\n\t'restore',\n\t'rm',\n\t'switch',\n\t'update-index',\n]);\n\nconst applyIndexOptions: ReadonlySet<string> = new Set(['--index', '--cached', '--3way', '-3']);\n\n/**\n * Matches git's `fatal: Unable to create '<path>/index.lock': File exists.` — the wording varies a little by version,\n * but git always `die()`s (`fatal:`) on it.\n * Anchored to the start of stderr: git takes the lock before doing anything else, so its own failure is the first\n * thing it prints past any `warning:`/`hint:` lines from parsing its arguments (`checkout` warns of an ambiguous\n * refname first). The same line after other output came from something the run started, such as a hook running its\n * own git command, after the run had already done its work — retrying would run it all again.\n */\nconst indexLockHeldRegex =\n\t/^(?:\\s*(?:warning|hint): [^\\r\\n]*\\r?\\n)*\\s*fatal: Unable to create '[^\\r\\n]*index\\.lock': File exists/i;\n\n/**\n * The exit code in a run error's `code`, or `undefined` when the process never exited normally: a numeric string\n * is a real exit code, but an errno (`'ENOENT'`) means the spawn itself failed, and `undefined` means it was killed.\n */\nexport function toExitCode(code: number | string | undefined): number | undefined {\n\tconst exitCode = typeof code === 'number' ? code : code != null ? parseInt(code, 10) : NaN;\n\treturn Number.isInteger(exitCode) ? exitCode : undefined;\n}\n\n/** Git's exit code when it `die()`s, as it does on a held `index.lock`. */\nconst gitFatalExitCode = 128;\n\n/** How long to wait before each retry of a run that found `index.lock` held; its length is the retry count. */\nexport const indexLockRetryDelays: readonly number[] = [100, 200, 400];\n\n/**\n * Whether the argv is a command that writes the index (`apply` only does with `--index`, `--cached` or `--3way`,\n * which implies `--index`, and never with `--check`).\n */\nexport function isIndexWriteCommand(args: readonly string[]): boolean {\n\tconst { verb, rest } = leadingCommand(args);\n\tif (verb === 'apply') return !rest.includes('--check') && rest.some(a => applyIndexOptions.has(a));\n\n\treturn indexWritingVerbs.has(verb);\n}\n\n/**\n * Whether a run failed because another process held `index.lock`. The exit code must be 128 too: a hook that\n * prints the same line from its own git command and fails makes the run exit 1, after the hook's work is done.\n */\nexport function isIndexLockHeldError(ex: unknown): boolean {\n\treturn (\n\t\tex instanceof RunError &&\n\t\t!(ex instanceof CancelledRunError) &&\n\t\ttoExitCode(ex.code) === gitFatalExitCode &&\n\t\tindexLockHeldRegex.test(ex.stderr)\n\t);\n}\n\n/** Resolves after `ms`, or rejects as soon as `signal` aborts. */\nasync function delay(ms: number, signal: AbortSignal | undefined): Promise<void> {\n\ttry {\n\t\tawait wait(ms, undefined, { signal: signal });\n\t} catch (ex) {\n\t\tif (signal?.aborted) throw abortReason(signal);\n\n\t\tthrow ex;\n\t}\n}\n\n/**\n * Captures the rest of git's `error: could not lock config file <path>[: <reason>]` line. A held lock reads\n * `: File exists`, except `--remove-section`/`--rename-section` print no reason (for any failure); permanent\n * failures (`Permission denied`, `No such file or directory`, ...) must not be retried.\n */\nconst configLockFailureRegex = /could not lock config file ([^\\r\\n]*)/i;\n\n/**\n * Whether the argv is `git config`. Only that verb: others that write config (`branch -m`, `remote add`) can\n * fail after a partial change, so a blind retry isn't safe.\n */\nexport function isConfigCommand(args: readonly string[]): boolean {\n\treturn leadingCommand(args).verb === 'config';\n}\n\n/** Whether a run failed because another process may hold the config file's `.lock`: the reason is `File exists` or absent. */\nexport function isConfigLockHeldError(ex: unknown): boolean {\n\tif (!(ex instanceof RunError) || ex instanceof CancelledRunError) return false;\n\n\tconst rest = configLockFailureRegex.exec(ex.stderr)?.[1];\n\tif (rest == null) return false;\n\n\treturn /: File exists\\s*$/i.test(rest) || !rest.includes(': ');\n}\n\n/**\n * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails with an error\n * `isHeldError` accepts. Any other failure, or the last lock failure, is rethrown as is.\n */\nasync function retryWhileHeld<T>(\n\tattempt: () => Promise<T>,\n\tsignal: AbortSignal | undefined,\n\tisHeldError: (ex: unknown) => boolean,\n\tbeforeRetry?: (lockError: Error) => void | Promise<void>,\n): Promise<T> {\n\tfor (let retry = 0; ; retry++) {\n\t\tlet lockError: Error;\n\t\ttry {\n\t\t\treturn await attempt();\n\t\t} catch (ex) {\n\t\t\tif (retry >= indexLockRetryDelays.length || !isHeldError(ex)) throw ex;\n\n\t\t\tlockError = ex as Error;\n\t\t}\n\n\t\tawait delay(indexLockRetryDelays[retry], signal);\n\t\tawait beforeRetry?.(lockError);\n\t}\n}\n\n/**\n * Runs `attempt`, and runs it again after each of {@link indexLockRetryDelays} while it fails on a held\n * `index.lock`. Any other failure, or the last lock failure, is rethrown as is. `beforeRetry` runs after each\n * wait, right before the next attempt; whatever it throws ends the retrying and is rethrown.\n */\nexport function retryWhileIndexLocked<T>(\n\tattempt: () => Promise<T>,\n\tsignal: AbortSignal | undefined,\n\tbeforeRetry?: (lockError: Error) => void | Promise<void>,\n): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isIndexLockHeldError, beforeRetry);\n}\n\n/**\n * Like {@link retryWhileIndexLocked}, for a config write that loses the race for the file's `.lock` to another\n * process — git fails at once rather than waiting.\n */\nexport function retryWhileConfigLocked<T>(attempt: () => Promise<T>, signal: AbortSignal | undefined): Promise<T> {\n\treturn retryWhileHeld(attempt, signal, isConfigLockHeldError);\n}\n\n/**\n * Runs the work for each key one at a time, in arrival order. A failing run doesn't hold up the next one, and a\n * run whose signal aborts while it waits rejects at once without ever starting.\n */\nexport class KeyedSequencer {\n\t/** The tail of each key's chain — settles once everything queued so far has finished or been dropped. */\n\tprivate readonly _tails = new Map<string, Promise<void>>();\n\n\tasync run<T>(key: string, signal: AbortSignal | undefined, fn: () => Promise<T>): Promise<T> {\n\t\tconst previous = this._tails.get(key);\n\n\t\tlet release!: () => void;\n\t\tconst tail = new Promise<void>(resolve => (release = resolve));\n\t\tthis._tails.set(key, tail);\n\n\t\tconst finish = () => {\n\t\t\trelease();\n\t\t\tif (this._tails.get(key) === tail) {\n\t\t\t\tthis._tails.delete(key);\n\t\t\t}\n\t\t};\n\n\t\tlet started = false;\n\t\ttry {\n\t\t\tif (previous != null) {\n\t\t\t\tawait waitFor(previous, signal);\n\t\t\t}\n\n\t\t\tstarted = true;\n\t\t\treturn await fn();\n\t\t} finally {\n\t\t\tif (started || previous == null) {\n\t\t\t\tfinish();\n\t\t\t} else {\n\t\t\t\t// Dropped while waiting: whoever is queued behind still has to wait for the run ahead of us,\n\t\t\t\t// so the chain can only be released once that run is done.\n\t\t\t\tvoid previous.then(finish);\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunction waitFor(promise: Promise<void>, signal: AbortSignal | undefined): Promise<void> {\n\tif (signal == null) return promise;\n\tif (signal.aborted) return Promise.reject(abortReason(signal));\n\n\treturn new Promise<void>((resolve, reject) => {\n\t\tconst onAbort = () => reject(abortReason(signal));\n\t\tsignal.addEventListener('abort', onAbort, { once: true });\n\t\tvoid promise.then(() => {\n\t\t\tsignal.removeEventListener('abort', onAbort);\n\t\t\tresolve();\n\t\t});\n\t});\n}\n"],"mappings":"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"}
@@ -39,6 +39,10 @@ declare const graphMapping: {
39
39
  };
40
40
  export type GraphLogParser = LogParser<typeof graphMapping>;
41
41
  type GraphWithStatsLogParser = LogParserWithStats<typeof graphMapping>;
42
+ /**
43
+ * Prints `tips` as full refnames (`HEAD -> refs/heads/main`, `refs/remotes/origin/main`, `tag: refs/tags/v1`), whatever
44
+ * `log.decorate` says, since short names can't tell a local branch from a remote-tracking ref of the same name
45
+ */
42
46
  export declare function getGraphParser(stats?: boolean): GraphLogParser | GraphWithStatsLogParser;
43
47
  type ShaLogParser = Parser<string>;
44
48
  export declare function getShaLogParser(): ShaLogParser;
@@ -60,12 +60,22 @@ const graphMapping = {
60
60
  };
61
61
  let _graphParser;
62
62
  let _graphWithStatsParser;
63
+ /**
64
+ * Prints `tips` as full refnames (`HEAD -> refs/heads/main`, `refs/remotes/origin/main`, `tag: refs/tags/v1`), whatever
65
+ * `log.decorate` says, since short names can't tell a local branch from a remote-tracking ref of the same name
66
+ */
63
67
  function getGraphParser(stats) {
64
68
  if (stats) {
65
- _graphWithStatsParser ??= createLogParserWithStats(graphMapping);
69
+ if (_graphWithStatsParser == null) {
70
+ _graphWithStatsParser = createLogParserWithStats(graphMapping);
71
+ _graphWithStatsParser.arguments.push("--decorate=full");
72
+ }
66
73
  return _graphWithStatsParser;
67
74
  }
68
- _graphParser ??= createLogParser(graphMapping);
75
+ if (_graphParser == null) {
76
+ _graphParser = createLogParser(graphMapping);
77
+ _graphParser.arguments.push("--decorate=full");
78
+ }
69
79
  return _graphParser;
70
80
  }
71
81
  let _shaParser;
@@ -1 +1 @@
1
- {"version":3,"file":"logParser.js","names":[],"sources":["../../../../git-cli/src/parsers/logParser.ts"],"sourcesContent":["import type { GitFileIndexStatus } from '@gitlens/git/models/fileStatus.js';\nimport { diffHunkRegex, diffRegex } from '@gitlens/git/parsers/diffParser.js';\nimport { joinPaths, normalizePath } from '@gitlens/utils/path.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { flattenString, iterateAsyncByDelimiter, iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { ExtractAll } from '@gitlens/utils/types.js';\n\nconst commitsMapping = {\n\tsha: '%H',\n\tauthor: '%aN',\n\tauthorEmail: '%aE',\n\tauthorDate: '%at',\n\tcommitter: '%cN',\n\tcommitterEmail: '%cE',\n\tcommitterDate: '%ct',\n\tparents: '%P',\n\ttips: '%D',\n\tmessage: '%B',\n};\n\nexport type CommitsLogParser = LogParser<typeof commitsMapping>;\nlet _commitsParser: CommitsLogParser | undefined;\nexport type CommitsWithFilesLogParser = LogParserWithFilesAndStats<typeof commitsMapping>;\nlet _commitsWithFilesParser: CommitsWithFilesLogParser | undefined;\nexport type CommitsInFileRangeLogParser = LogParserWithFiles<typeof commitsMapping>;\nlet _commitsInFileRangeParser: CommitsInFileRangeLogParser | undefined;\n\nexport type ParsedCommit =\n\t| LogParsedEntry<typeof commitsMapping>\n\t| LogParsedEntryWithFiles<typeof commitsMapping>\n\t| LogParsedEntryWithFilesAndStats<typeof commitsMapping>;\n\nexport function getCommitsLogParser(\n\tincludeFiles: boolean,\n\tinFileRange?: boolean,\n): CommitsLogParser | CommitsWithFilesLogParser | CommitsInFileRangeLogParser {\n\tif (inFileRange) {\n\t\t_commitsInFileRangeParser ??= createLogParserWithPatch(commitsMapping);\n\t\treturn _commitsInFileRangeParser;\n\t}\n\n\tif (includeFiles) {\n\t\t_commitsWithFilesParser ??= createLogParserWithFilesAndStats(commitsMapping);\n\t\treturn _commitsWithFilesParser;\n\t}\n\n\t_commitsParser ??= createLogParser(commitsMapping);\n\treturn _commitsParser;\n}\n\nconst contributorsMapping = { sha: '%H', author: '%aN', email: '%aE', date: '%at', message: '%B' };\n\ntype ContributorsLogParser = LogParser<typeof contributorsMapping>;\nlet _contributorsParser: ContributorsLogParser | undefined;\ntype ContributorsWithStatsLogParser = LogParserWithStats<typeof contributorsMapping>;\nlet _contributorsWithStatsParser: ContributorsWithStatsLogParser | undefined;\n\nexport function getContributorsLogParser(stats?: boolean): ContributorsLogParser | ContributorsWithStatsLogParser {\n\tif (stats) {\n\t\t_contributorsWithStatsParser ??= createLogParserWithStats(contributorsMapping);\n\t\treturn _contributorsWithStatsParser;\n\t}\n\n\t_contributorsParser ??= createLogParser(contributorsMapping);\n\treturn _contributorsParser;\n}\n\nconst graphMapping = {\n\tsha: '%H',\n\tauthor: '%aN',\n\tauthorEmail: '%aE',\n\tauthorDate: '%at',\n\tcommitterDate: '%ct',\n\tparents: '%P',\n\ttips: '%D',\n\tmessage: '%B',\n};\n\nexport type GraphLogParser = LogParser<typeof graphMapping>;\nlet _graphParser: GraphLogParser | undefined;\ntype GraphWithStatsLogParser = LogParserWithStats<typeof graphMapping>;\nlet _graphWithStatsParser: GraphWithStatsLogParser | undefined;\n\nexport function getGraphParser(stats?: boolean): GraphLogParser | GraphWithStatsLogParser {\n\tif (stats) {\n\t\t_graphWithStatsParser ??= createLogParserWithStats(graphMapping);\n\t\treturn _graphWithStatsParser;\n\t}\n\n\t_graphParser ??= createLogParser(graphMapping);\n\treturn _graphParser;\n}\n\ntype ShaLogParser = Parser<string>;\nlet _shaParser: ShaLogParser | undefined;\n\nexport function getShaLogParser(): ShaLogParser {\n\t_shaParser ??= createLogParserSingle('%H');\n\treturn _shaParser;\n}\n\nconst shaAndDateMapping = { sha: '%H', authorDate: '%at', committerDate: '%ct' };\nconst shaAndDateAndTipsMapping = { sha: '%H', authorDate: '%at', committerDate: '%ct', tips: '%D' };\n\ntype ShaAndDatesLogParser = LogParser<typeof shaAndDateMapping & { tips?: string }>;\nlet _shaAndDatesParser: ShaAndDatesLogParser | undefined;\ntype ShaAndDatesAndTipsLogParser = LogParser<typeof shaAndDateAndTipsMapping>;\nlet _shaAndDatesAndTipsParser: ShaAndDatesAndTipsLogParser | undefined;\n\nexport function getShaAndDatesLogParser(includeTips?: boolean): ShaAndDatesLogParser | ShaAndDatesAndTipsLogParser {\n\tif (includeTips) {\n\t\t_shaAndDatesAndTipsParser ??= createLogParser(shaAndDateAndTipsMapping);\n\t\treturn _shaAndDatesAndTipsParser;\n\t}\n\n\t_shaAndDatesParser ??= createLogParser(shaAndDateMapping);\n\treturn _shaAndDatesParser;\n}\n\ntype ShaAndDatesWithFilesLogParser = LogParserWithFiles<typeof shaAndDateMapping & { tips?: string }>;\nlet _shaAndDatesWithFilesParser: ShaAndDatesWithFilesLogParser | undefined;\n\ntype ShaAndDatesAndTipsWithFilesLogParser = LogParserWithFiles<typeof shaAndDateAndTipsMapping>;\nlet _shaAndDatesAndTipsWithFilesParser: ShaAndDatesAndTipsWithFilesLogParser | undefined;\n\nexport function getShaAndDatesWithFilesLogParser(\n\tincludeTips?: boolean,\n): ShaAndDatesWithFilesLogParser | ShaAndDatesAndTipsWithFilesLogParser {\n\tif (includeTips) {\n\t\t_shaAndDatesAndTipsWithFilesParser ??= createLogParserWithFiles(shaAndDateAndTipsMapping);\n\t\treturn _shaAndDatesAndTipsWithFilesParser;\n\t}\n\n\t_shaAndDatesWithFilesParser ??= createLogParserWithFiles(shaAndDateMapping);\n\treturn _shaAndDatesWithFilesParser;\n}\n\nconst shaMapping = { sha: '%H' };\n\ntype ShaAndFilesAndStatsLogParser = LogParserWithFilesAndStats<typeof shaMapping>;\nlet _shaAndFilesAndStatsParser: ShaAndFilesAndStatsLogParser | undefined;\n\nexport function getShaAndFilesAndStatsLogParser(): ShaAndFilesAndStatsLogParser {\n\t_shaAndFilesAndStatsParser ??= createLogParserWithFilesAndStats(shaMapping);\n\treturn _shaAndFilesAndStatsParser;\n}\n\ntype ShaAndFileRangeLogParser = LogParserWithFiles<typeof shaMapping>;\nlet _shaAndFileRangeParser: ShaAndFileRangeLogParser | undefined;\n\nexport function getShaAndFileRangeLogParser(): ShaAndFileRangeLogParser {\n\t_shaAndFileRangeParser ??= createLogParserWithPatch(shaMapping);\n\treturn _shaAndFileRangeParser;\n}\n\ntype ShaAndFileSummaryLogParser = LogParserWithFileSummary<typeof shaMapping>;\nlet _shaAndFileSummaryParser: ShaAndFileSummaryLogParser | undefined;\n\nexport function getShaAndFileSummaryLogParser(): ShaAndFileSummaryLogParser {\n\t_shaAndFileSummaryParser ??= createLogParserWithFileSummary(shaMapping);\n\treturn _shaAndFileSummaryParser;\n}\n\ntype ShaAndStatsLogParser = LogParserWithStats<typeof shaMapping>;\nlet _shaAndStatsParser: ShaAndStatsLogParser | undefined;\n\nexport function getShaAndStatsLogParser(): ShaAndStatsLogParser {\n\t_shaAndStatsParser ??= createLogParserWithStats(shaMapping);\n\treturn _shaAndStatsParser;\n}\n\nconst stashMapping = {\n\tsha: '%H',\n\tauthorDate: '%at',\n\tcommittedDate: '%ct',\n\tparents: '%P',\n\tstashName: '%gd',\n\tsummary: '%gs',\n\t// %s (commit subject) is git's authoritative `On <branch>: …` / `WIP on <branch>: …` record,\n\t// present even when the reflog message (%gs) was customized (autostash, `git stash store -m`).\n\t// Used downstream to extract `stashOnRef` reliably.\n\tcommitSubject: '%s',\n};\n\ntype StashLogParser = LogParser<typeof stashMapping>;\nlet _stashParser: StashLogParser | undefined;\ntype StashLogParserWithFiles = LogParserWithFilesAndStats<typeof stashMapping>;\nlet _stashParserWithFiles: StashLogParserWithFiles | undefined;\n\nexport type ParsedStash = LogParsedEntry<typeof stashMapping>;\nexport type ParsedStashWithFiles = LogParsedEntryWithFilesAndStats<typeof stashMapping>;\n\nexport function getStashLogParser(includeFiles?: false): StashLogParser;\nexport function getStashLogParser(includeFiles: true): StashLogParserWithFiles;\nexport function getStashLogParser(includeFiles?: boolean): StashLogParser | StashLogParserWithFiles;\nexport function getStashLogParser(includeFiles?: boolean): StashLogParser | StashLogParserWithFiles {\n\tif (includeFiles) {\n\t\t_stashParserWithFiles ??= createLogParserWithFilesAndStats(stashMapping);\n\t\treturn _stashParserWithFiles;\n\t}\n\n\t_stashParser ??= createLogParser(stashMapping);\n\treturn _stashParser;\n}\n\ntype StashFilesOnlyLogParser = LogParserWithFilesAndStats<void>;\nlet _stashFilesOnlyParser: StashFilesOnlyLogParser | undefined;\n\nexport function getStashFilesOnlyLogParser(): StashFilesOnlyLogParser {\n\t_stashFilesOnlyParser ??= createLogParserWithFilesAndStats();\n\treturn _stashFilesOnlyParser;\n}\n\n// Parser types\nexport type Parser<T> = {\n\targuments: string[];\n\tseparators: { record: string; field: string };\n\tparse: (data: string | Iterable<string> | undefined) => Generator<T> | Iterable<T>;\n\tparseAsync?: never;\n};\nexport type AsyncParser<T> = {\n\targuments: string[];\n\tseparators: { record: string; field: string };\n\tparse: (data: string | Iterable<string> | undefined) => Generator<T> | Iterable<T>;\n\tparseAsync: (stream: AsyncGenerator<string>) => AsyncGenerator<T>;\n\t/**\n\t * Like `parseAsync` but yields one ARRAY of entries per stream chunk, scanning each chunk\n\t * synchronously with plain `indexOf` (no per-field iterator machinery). Bulk consumers (the\n\t * graph walk) avoid two promise hops per record — thousands of awaited yields collapse to one\n\t * per chunk. Only the plain parser implements it; per-record `parseAsync` remains the\n\t * general-purpose path.\n\t */\n\tparseAsyncBatched?: (stream: AsyncGenerator<string>) => AsyncGenerator<T[]>;\n};\n\nexport type LogParser<T> = AsyncParser<LogParsedEntry<T>>;\ntype LogParserWithFiles<T> = AsyncParser<LogParsedEntryWithFiles<T>>;\ntype LogParserWithFilesAndStats<T> = AsyncParser<LogParsedEntryWithFilesAndStats<T>>;\ntype LogParserWithFileSummary<T> = Parser<LogParsedEntryWithFiles<T>>;\ntype LogParserWithStats<T> = AsyncParser<LogParsedEntryWithStats<T>>;\n\n// Parsed entry types\ntype LogParsedEntry<T> = { [K in keyof T]: string } & { files?: never; stats?: never };\ntype LogParsedEntryWithFiles<T> = { [K in keyof T]: string } & { files: LogParsedFile[]; stats?: never };\ntype LogParsedEntryWithFilesAndStats<T> = { [K in keyof T]: string } & {\n\tfiles: LogParsedFileWithStats[];\n\tstats: LogParsedStatsWithFilesStats;\n};\ntype LogParsedEntryWithStats<T> = { [K in keyof T]: string } & { stats: LogParsedStats };\n\n// Parsed types\nexport interface LogParsedFile {\n\tstatus?: string;\n\tpath: string;\n\toriginalPath?: string;\n\tadditions?: never;\n\tdeletions?: never;\n\trange?: LogParsedRange;\n\toriginalRange?: LogParsedRange;\n\t/** Git file mode (e.g., 100644=regular, 100755=executable, 120000=symlink, 160000=submodule) */\n\tmode?: string;\n\t/** For submodule entries (mode 160000), the commit SHA the submodule points to */\n\toid?: string;\n\t/** For submodule entries (mode 160000), the previous commit SHA */\n\tpreviousOid?: string;\n}\ntype LogParsedFileWithStats = Omit<LogParsedFile, 'additions' | 'deletions'> & {\n\tadditions: number;\n\tdeletions: number;\n};\ninterface LogParsedRange {\n\tstartLine: number;\n\tendLine: number;\n}\ninterface LogParsedStats {\n\tfiles: number;\n\tadditions: number;\n\tdeletions: number;\n}\ninterface LogParsedStatsWithFilesStats {\n\tfiles: { added: number; changed: number; deleted: number };\n\tadditions: number;\n\tdeletions: number;\n}\n\nconst recordSep = '\\x1E'; // ASCII Record Separator character\nconst recordFormatSep = '%x1E';\nconst fieldSep = '\\x1D'; // ASCII Group Separator character\nconst fieldFormatSep = '%x1D';\n\n// A complete record terminates every mapped field with a field separator — a cancelled/early-closed\n// stream resolves cleanly with a truncated tail missing at least the final terminator.\nfunction isCompleteRecord(record: string, requiredFields: number): boolean {\n\tlet count = 0;\n\tlet index = -1;\n\twhile (count < requiredFields) {\n\t\tindex = record.indexOf(fieldSep, index + 1);\n\t\tif (index === -1) return false;\n\n\t\tcount++;\n\t}\n\treturn true;\n}\n\nexport function createLogParser<T extends Record<string, string>>(mapping: ExtractAll<T, string>): LogParser<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntry<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParser.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntry<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tentry = {} as unknown as LogParsedEntry<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue;\n\n\t\t\t\tcount++;\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntry<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParser.parseAsync', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntry<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\t\t\t// Drop a truncated tail (cancelled/early-closed stream) instead of yielding undefined/partial fields.\n\t\t\tif (!isCompleteRecord(record, keys.length)) continue;\n\n\t\t\tentry = {} as unknown as LogParsedEntry<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue;\n\n\t\t\t\tcount++;\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\t// Builds one entry from a complete record with a plain `indexOf` walk. Matches `parseAsync`'s\n\t// semantics exactly: fields beyond the mapping are ignored, and every mapped field is\n\t// terminated by a field separator (the format string appends one after each field).\n\tfunction parseRecord(record: string): LogParsedEntry<T> {\n\t\t// Fields sliced out of the record become long-lived (graph rows); flatten the record so they\n\t\t// pin a record-sized copy rather than the full walk output the record was sliced from.\n\t\trecord = flattenString(record);\n\t\tconst entry = {} as unknown as LogParsedEntry<T>;\n\t\tlet start = 0;\n\t\tfor (const key of keys) {\n\t\t\tconst end = record.indexOf(fieldSep, start);\n\t\t\tif (end === -1) break;\n\n\t\t\tentry[key] = record.substring(start, end) as T[keyof T];\n\t\t\tstart = end + 1;\n\t\t}\n\t\treturn entry;\n\t}\n\n\tasync function* parseAsyncBatched(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntry<T>[]> {\n\t\tusing sw = maybeStopWatch('Git.LogParser.parseAsyncBatched', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tlet count = 0;\n\t\tlet buffer = '';\n\t\tfor await (const chunk of stream) {\n\t\t\tbuffer += chunk;\n\n\t\t\tlet batch: LogParsedEntry<T>[] | undefined;\n\t\t\tlet start = 0;\n\t\t\twhile (true) {\n\t\t\t\tconst end = buffer.indexOf(recordSep, start);\n\t\t\t\tif (end === -1) break;\n\n\t\t\t\tif (end > start) {\n\t\t\t\t\t(batch ??= []).push(parseRecord(buffer.substring(start, end)));\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tstart = end + 1;\n\t\t\t}\n\t\t\tif (start > 0) {\n\t\t\t\tbuffer = buffer.substring(start);\n\t\t\t}\n\t\t\tif (batch != null) {\n\t\t\t\tyield batch;\n\t\t\t}\n\t\t}\n\t\t// Trailing record (the stream doesn't end with a record separator); a truncated tail from a\n\t\t// cancelled/early-closed stream is missing a field terminator and must be dropped, not parsed.\n\t\tif (buffer.length > 0 && isCompleteRecord(buffer, keys.length)) {\n\t\t\tcount++;\n\t\t\tyield [parseRecord(buffer)];\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t\tparseAsyncBatched: parseAsyncBatched,\n\t};\n}\n\nfunction createLogParserWithFilesAndStats(): LogParserWithFilesAndStats<void>;\nfunction createLogParserWithFilesAndStats<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFilesAndStats<T>;\nfunction createLogParserWithFilesAndStats<T extends Record<string, string> | void>(\n\tmapping?: ExtractAll<T, string>,\n): LogParserWithFilesAndStats<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tif (mapping != null) {\n\t\tfor (const key in mapping) {\n\t\t\tkeys.push(key);\n\t\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t\t}\n\t}\n\t// Use --raw instead of --summary to get accurate mode info for ALL files (not just creates/deletes)\n\t// This allows us to reliably detect submodules (mode 160000) even for modifications\n\tconst args = [`--format=${format}`, '--numstat', '--raw'];\n\n\tfunction parseFilesAndStats(content: string): LogParsedFileWithStats[] {\n\t\tconst files: LogParsedFileWithStats[] = [];\n\t\tif (!content?.length) return files;\n\n\t\t// Map to store info from --raw lines: path -> { mode, status, originalPath, oid?, previousOid? }\n\t\t// Raw format: :<old_mode> <new_mode> <old_sha> <new_sha> <status>[score]\\t<path>[\\t<new_path>]\n\t\tconst infoMap = new Map<\n\t\t\tstring,\n\t\t\t{ mode: string; status: string; originalPath?: string; oid?: string; previousOid?: string }\n\t\t>();\n\n\t\tlet startIndex: number;\n\t\tlet endIndex: number;\n\n\t\t// Single pass: --raw lines come first, then --numstat lines\n\t\tfor (const line of iterateByDelimiter(content, '\\n')) {\n\t\t\tif (!line) continue;\n\n\t\t\tif (line.charCodeAt(0) === 58 /* ':' */) {\n\t\t\t\t// --raw format: :<old_mode> <new_mode> <old_sha> <new_sha> <status>[score]\\t<path>[\\t<new_path>]\n\t\t\t\tconst tabIndex = line.indexOf('\\t');\n\t\t\t\tif (tabIndex === -1) continue;\n\n\t\t\t\t// Parse metadata using indexOf instead of split() to avoid array allocation\n\t\t\t\t// Format after ':': <old_mode> <new_mode> <old_sha> <new_sha> <status>[score]\n\n\t\t\t\t// Skip old_mode, find new_mode\n\t\t\t\tstartIndex = line.indexOf(' ') + 1;\n\t\t\t\tif (startIndex === 0) continue;\n\n\t\t\t\tendIndex = line.indexOf(' ', startIndex);\n\t\t\t\tif (endIndex === -1) continue;\n\n\t\t\t\tconst newMode = line.substring(startIndex, endIndex);\n\n\t\t\t\t// Parse old_sha and new_sha positions\n\t\t\t\tconst oldShaStart = endIndex + 1;\n\t\t\t\tconst oldShaEnd = line.indexOf(' ', oldShaStart);\n\t\t\t\tif (oldShaEnd === -1) continue;\n\n\t\t\t\tconst newShaStart = oldShaEnd + 1;\n\t\t\t\tconst newShaEnd = line.indexOf(' ', newShaStart);\n\t\t\t\tif (newShaEnd === -1) continue;\n\n\t\t\t\t// For submodules (mode 160000), capture the commit SHAs\n\t\t\t\tlet oid: string | undefined;\n\t\t\t\tlet previousOid: string | undefined;\n\t\t\t\tif (newMode === '160000') {\n\t\t\t\t\tpreviousOid = line.substring(oldShaStart, oldShaEnd);\n\t\t\t\t\toid = line.substring(newShaStart, newShaEnd);\n\t\t\t\t}\n\n\t\t\t\t// Status is first char after the last space\n\t\t\t\tstartIndex = newShaEnd;\n\t\t\t\tconst status = line.charAt(startIndex + 1);\n\n\t\t\t\t// Parse path(s) using indexOf instead of split\n\t\t\t\tconst pathPart = line.substring(tabIndex + 1);\n\t\t\t\tlet path: string;\n\t\t\t\tlet originalPath: string | undefined;\n\n\t\t\t\tconst pathTabIndex = pathPart.indexOf('\\t');\n\t\t\t\tif ((status === 'R' || status === 'C') && pathTabIndex !== -1) {\n\t\t\t\t\t// For renames/copies: old_path\\tnew_path\n\t\t\t\t\toriginalPath = pathPart.substring(0, pathTabIndex);\n\t\t\t\t\tpath = pathPart.substring(pathTabIndex + 1);\n\t\t\t\t} else {\n\t\t\t\t\tpath = pathPart;\n\t\t\t\t}\n\n\t\t\t\t// Trim to match parseCopyOrRename which trims renamed paths\n\t\t\t\tinfoMap.set(path.trim(), {\n\t\t\t\t\tmode: newMode,\n\t\t\t\t\tstatus: status,\n\t\t\t\t\toriginalPath: originalPath?.trim(),\n\t\t\t\t\toid: oid,\n\t\t\t\t\tpreviousOid: previousOid,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// --numstat format: <additions>\\t<deletions>\\t<path>\n\t\t\t\t// With renames: <additions>\\t<deletions>\\t{prefix/old => new/suffix}\n\t\t\t\tendIndex = line.indexOf('\\t');\n\t\t\t\tif (endIndex === -1) {\n\t\t\t\t\tdebugger;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst additions = line.substring(0, endIndex);\n\n\t\t\t\tstartIndex = endIndex + 1;\n\t\t\t\tendIndex = line.indexOf('\\t', startIndex);\n\t\t\t\tif (endIndex === -1) {\n\t\t\t\t\tdebugger;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst deletions = line.substring(startIndex, endIndex);\n\n\t\t\t\tlet path = line.substring(endIndex + 1);\n\t\t\t\tlet originalPath: string | undefined;\n\n\t\t\t\t// Parse rename format from numstat (e.g., \"{old => new}/path\")\n\t\t\t\t({ path, originalPath } = parseCopyOrRename(path, false));\n\n\t\t\t\t// Look up --raw info for this path (keyed by trimmed new/destination path)\n\t\t\t\tconst info = infoMap.get(path.trim());\n\n\t\t\t\t// Use status from --raw if available; provides accurate status (A/D/M/R/C/T)\n\t\t\t\tconst status = info?.status ?? (originalPath ? 'R' : 'M');\n\n\t\t\t\t// Use originalPath from --raw if available (cleaner than numstat format)\n\t\t\t\tif (info?.originalPath) {\n\t\t\t\t\toriginalPath = info.originalPath;\n\t\t\t\t}\n\n\t\t\t\tconst file: LogParsedFileWithStats = {\n\t\t\t\t\tstatus: status,\n\t\t\t\t\tpath: path.trim(),\n\t\t\t\t\toriginalPath: originalPath?.trim(),\n\t\t\t\t\tadditions: additions === '-' ? 0 : parseInt(additions, 10) || 0,\n\t\t\t\t\tdeletions: deletions === '-' ? 0 : parseInt(deletions, 10) || 0,\n\t\t\t\t\t// Store mode for consumers to derive file type (160000=submodule, 100755=executable, etc.)\n\t\t\t\t\tmode: info?.mode,\n\t\t\t\t\toid: info?.oid,\n\t\t\t\t\tpreviousOid: info?.previousOid,\n\t\t\t\t};\n\n\t\t\t\tfiles.push(file);\n\t\t\t}\n\t\t}\n\n\t\treturn files;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFilesAndStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithFilesAndStats.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFilesAndStats<T>;\n\t\tlet files: LogParsedFileWithStats[];\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFilesAndStats<T>;\n\t\t\tfiles = [];\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFilesAndStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commits and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tfiles.push(...parseFilesAndStats(summary));\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentry.files = files;\n\t\t\tif (files.length) {\n\t\t\t\tentry.stats = { additions: 0, deletions: 0, files: { added: 0, deleted: 0, changed: 0 } };\n\n\t\t\t\tfor (const f of files) {\n\t\t\t\t\tif (f.additions || f.deletions) {\n\t\t\t\t\t\tentry.stats.additions += f.additions ?? 0;\n\t\t\t\t\t\tentry.stats.deletions += f.deletions ?? 0;\n\t\t\t\t\t\tif (f.status === 'A' || f.status === '?') {\n\t\t\t\t\t\t\tentry.stats.files.added++;\n\t\t\t\t\t\t} else if (f.status === 'D') {\n\t\t\t\t\t\t\tentry.stats.files.deleted++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tentry.stats.files.changed++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFilesAndStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithFilesAndStats.parseAsync', {\n\t\t\tlog: { onlyExit: true, level: 'debug' },\n\t\t});\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFilesAndStats<T>;\n\t\tlet files: LogParsedFileWithStats[];\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\t\t\t// Drop a truncated tail (cancelled/early-closed stream) instead of yielding undefined/partial fields.\n\t\t\tif (!isCompleteRecord(record, keys.length)) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFilesAndStats<T>;\n\t\t\tfiles = [];\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFilesAndStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commits and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tfiles.push(...parseFilesAndStats(summary));\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentry.files = files;\n\t\t\tif (files.length) {\n\t\t\t\tentry.stats = { additions: 0, deletions: 0, files: { added: 0, deleted: 0, changed: 0 } };\n\n\t\t\t\tfor (const f of files) {\n\t\t\t\t\tif (f.additions || f.deletions) {\n\t\t\t\t\t\tentry.stats.additions += f.additions ?? 0;\n\t\t\t\t\t\tentry.stats.deletions += f.deletions ?? 0;\n\t\t\t\t\t\tif (f.status === 'A' || f.status === '?') {\n\t\t\t\t\t\t\tentry.stats.files.added++;\n\t\t\t\t\t\t} else if (f.status === 'D') {\n\t\t\t\t\t\t\tentry.stats.files.deleted++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tentry.stats.files.changed++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n\nfunction createLogParserWithFileSummary(): LogParserWithFileSummary<void>;\nfunction createLogParserWithFileSummary<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFileSummary<T>;\nfunction createLogParserWithFileSummary<T extends Record<string, string> | void>(\n\tmapping?: ExtractAll<T, string>,\n): LogParserWithFileSummary<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tif (mapping != null) {\n\t\tfor (const key in mapping) {\n\t\t\tkeys.push(key);\n\t\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t\t}\n\t}\n\tconst args = [`--format=${format}`, '--summary'];\n\n\tfunction parseFileSummary(content: string): LogParsedFile[] {\n\t\tconst files: LogParsedFile[] = [];\n\t\tif (!content?.length) return files;\n\n\t\tlet startIndex;\n\n\t\tlet path;\n\t\tlet originalPath;\n\t\tlet status;\n\n\t\tfor (const line of iterateByDelimiter(content, '\\n')) {\n\t\t\tif (!line) continue;\n\n\t\t\tif (line.startsWith(' rename ')) {\n\t\t\t\t({ path, originalPath } = parseCopyOrRename(line.substring(8 /* move past ' rename ' */), true));\n\t\t\t\tfiles.push({ path: path, originalPath: originalPath, status: 'R' });\n\t\t\t} else if (line.startsWith(' copy ')) {\n\t\t\t\t({ path, originalPath } = parseCopyOrRename(line.substring(6 /* move past ' copy ' */), true));\n\t\t\t\tfiles.push({ path: path, originalPath: originalPath, status: 'C' });\n\t\t\t} else {\n\t\t\t\tif (line.startsWith(' create mode ')) {\n\t\t\t\t\tstatus = 'A';\n\t\t\t\t} else if (line.startsWith(' delete mode ')) {\n\t\t\t\t\tstatus = 'D';\n\t\t\t\t} else {\n\t\t\t\t\t// Ignore \" mode change \" lines\n\t\t\t\t\tif (!line.startsWith(' mode change ')) {\n\t\t\t\t\t\tdebugger;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tstartIndex = line.indexOf(' ', 13 /* move past 'create mode <num>' or 'delete mode <num>' */);\n\t\t\t\tif (startIndex > -1) {\n\t\t\t\t\tpath = line.substring(startIndex + 1);\n\t\t\t\t\tfiles.push({ path: path, status: status });\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn files;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithFileSummary.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet files: LogParsedFile[];\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfiles = [];\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commits and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tfiles.push(...parseFileSummary(summary));\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentry.files = files;\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t};\n}\n\nfunction parseCopyOrRename(path: string, stripPercentage: boolean): { path: string; originalPath?: string } {\n\tconst renameIndex = path.indexOf(' => ');\n\tif (renameIndex === -1) return { path: path };\n\n\tlet hasBraces = true;\n\n\tlet openIndex = path.indexOf('{');\n\tif (openIndex === -1) {\n\t\thasBraces = false;\n\t\topenIndex = 0;\n\t}\n\n\tlet closeIndex = path.indexOf('}', openIndex);\n\tif (closeIndex === -1) {\n\t\thasBraces = false;\n\t\tcloseIndex = path.length;\n\t}\n\n\tconst prefix = path.substring(0, openIndex);\n\tconst fromPart = path.substring(hasBraces ? openIndex + 1 : 0, renameIndex);\n\tlet toPart = path.substring(renameIndex + 4, closeIndex);\n\tlet suffix = path.substring(closeIndex + 1);\n\n\t// Check for percentage marker which always appears at the end as (xx%)\n\tif (stripPercentage) {\n\t\tif (hasBraces) {\n\t\t\tif (suffix.endsWith('%)')) {\n\t\t\t\t// Find the last open paren that starts the percentage\n\t\t\t\tconst percentPos = suffix.lastIndexOf(' (');\n\t\t\t\tif (percentPos > -1) {\n\t\t\t\t\tsuffix = suffix.substring(0, percentPos);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (toPart.endsWith('%)')) {\n\t\t\t// Find the last open paren that starts the percentage\n\t\t\tconst percentPos = toPart.lastIndexOf(' (');\n\t\t\tif (percentPos > -1) {\n\t\t\t\ttoPart = toPart.substring(0, percentPos);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tpath: normalizePath(joinPaths(prefix, toPart, suffix).trim()),\n\t\toriginalPath: normalizePath(joinPaths(prefix, fromPart, suffix).trim()),\n\t};\n}\n\nfunction createLogParserSingle(field: string): Parser<string> {\n\tconst args = ['-z', `--format=${field}`];\n\n\tfunction parse(data: string | Iterable<string> | undefined): Iterable<string> {\n\t\tusing _sw = maybeStopWatch('Git.LogParserSingle.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\treturn data ? iterateByDelimiter(data, '\\0') : [];\n\t}\n\n\treturn { arguments: args, separators: { record: '\\0', field: '\\0' }, parse: parse };\n}\n\nfunction createLogParserWithPatch<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFiles<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction parsePatch(content: string): LogParsedFile | undefined {\n\t\tconst lines = iterateByDelimiter(content, '\\n');\n\t\tlet line = lines.next();\n\t\tif (line.done) return undefined;\n\n\t\tconst fileMatch = diffRegex.exec(line.value);\n\t\tif (fileMatch == null) return undefined;\n\n\t\twhile (!line.done && !line.value.startsWith('@@ ')) {\n\t\t\tline = lines.next();\n\t\t}\n\n\t\tconst rangeMatch = line.value.match(diffHunkRegex);\n\n\t\tlet range: LogParsedRange | undefined;\n\t\tlet originalRange: LogParsedRange | undefined;\n\t\tif (rangeMatch != null) {\n\t\t\tlet start = parseInt(rangeMatch[1], 10);\n\t\t\tlet count = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : 1;\n\n\t\t\toriginalRange = start && count ? { startLine: start, endLine: start + count - 1 } : undefined;\n\n\t\t\tstart = parseInt(rangeMatch[3], 10);\n\t\t\tcount = rangeMatch[4] ? parseInt(rangeMatch[4], 10) : 1;\n\n\t\t\trange = start && count ? { startLine: start, endLine: start + count - 1 } : undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: (fileMatch[1] === 'rename' ? 'R' : 'M') satisfies GitFileIndexStatus,\n\t\t\tpath: fileMatch[2],\n\t\t\toriginalPath: fileMatch[1] === 'rename' ? fileMatch[3] : undefined,\n\t\t\trange: range,\n\t\t\toriginalRange: originalRange,\n\t\t};\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithPatch.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and patch, if any\n\t\t\t\t\tconst patch = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tconst file = parsePatch(patch);\n\t\t\t\t\tentry.files = file != null ? [file] : [];\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithPatch.parseAsync', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\t\t\t// Drop a truncated tail (cancelled/early-closed stream) instead of yielding undefined/partial fields.\n\t\t\tif (!isCompleteRecord(record, keys.length)) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and patch, if any\n\t\t\t\t\tconst patch = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tconst file = parsePatch(patch);\n\t\t\t\t\tentry.files = file != null ? [file] : [];\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n\nfunction createLogParserWithStats<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithStats<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`, '--shortstat'];\n\n\tfunction parseStats(content: string): LogParsedStats {\n\t\tconst stats: LogParsedStats = { files: 0, additions: 0, deletions: 0 };\n\t\tcontent = content?.trim();\n\t\tif (!content?.length) return { files: 0, additions: 0, deletions: 0 };\n\n\t\tlet filesEnd;\n\t\tlet filesIndex = content.indexOf(' files changed');\n\t\tif (filesIndex === -1) {\n\t\t\tfilesIndex = content.indexOf(' file changed');\n\t\t\tif (filesIndex === -1) return stats;\n\n\t\t\tfilesEnd = filesIndex + ' file changed'.length;\n\t\t} else {\n\t\t\tfilesEnd = filesIndex + ' files changed'.length;\n\t\t}\n\n\t\t// Extract number before \" files changed\"\n\t\tconst filesPart = content.substring(0, filesIndex).trim();\n\t\tstats.files = parseInt(filesPart, 10) || 0;\n\n\t\t// Extract additions if present\n\t\tconst additionsIndex = content.indexOf(' insertion', filesEnd);\n\t\tif (additionsIndex !== -1) {\n\t\t\t// Look for the number before \"insertion(+)\" or \"insertions(+)\"\n\t\t\tconst spaceIndex = content.lastIndexOf(' ', additionsIndex - 1);\n\t\t\tif (spaceIndex !== -1) {\n\t\t\t\tconst additionsPart = content.substring(spaceIndex, additionsIndex).trim();\n\t\t\t\tstats.additions = parseInt(additionsPart, 10) || 0;\n\t\t\t}\n\t\t}\n\n\t\t// Extract deletions if present\n\t\tconst deletionsIndex = content.indexOf(' deletion', filesEnd);\n\t\tif (deletionsIndex !== -1) {\n\t\t\t// Look for the number before \"\"deletion(-)\" or \"deletions(-)\"\n\t\t\tconst spaceIndex = content.lastIndexOf(' ', deletionsIndex - 1);\n\t\t\tif (spaceIndex !== -1) {\n\t\t\t\tconst deletionsPart = content.substring(spaceIndex, deletionsIndex).trim();\n\t\t\t\tstats.deletions = parseInt(deletionsPart, 10) || 0;\n\t\t\t}\n\t\t}\n\n\t\treturn stats;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithStats.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithStats<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithStats<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.stats = parseStats(summary);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithStats.parseAsync', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithStats<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithStats<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.stats = parseStats(summary);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n\nfunction createLogParserWithFiles<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFiles<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`, '--name-only'];\n\n\tfunction parseFileNames(content: string): LogParsedFile[] {\n\t\tconst files: LogParsedFile[] = [];\n\t\tif (!content?.length) return files;\n\n\t\tfor (const line of iterateByDelimiter(content, '\\n')) {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (!trimmed) continue;\n\n\t\t\tfiles.push({ path: trimmed });\n\t\t}\n\n\t\treturn files;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.createLogParserWithFiles.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files, if any\n\t\t\t\t\tconst files = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.files = parseFileNames(files);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.createLogParserWithFiles.parseAsync', {\n\t\t\tlog: { onlyExit: true, level: 'debug' },\n\t\t});\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files, if any\n\t\t\t\t\tconst files = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.files = parseFileNames(files);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"logParser.js","names":[],"sources":["../../../../git-cli/src/parsers/logParser.ts"],"sourcesContent":["import type { GitFileIndexStatus } from '@gitlens/git/models/fileStatus.js';\nimport { diffHunkRegex, diffRegex } from '@gitlens/git/parsers/diffParser.js';\nimport { joinPaths, normalizePath } from '@gitlens/utils/path.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { flattenString, iterateAsyncByDelimiter, iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { ExtractAll } from '@gitlens/utils/types.js';\n\nconst commitsMapping = {\n\tsha: '%H',\n\tauthor: '%aN',\n\tauthorEmail: '%aE',\n\tauthorDate: '%at',\n\tcommitter: '%cN',\n\tcommitterEmail: '%cE',\n\tcommitterDate: '%ct',\n\tparents: '%P',\n\ttips: '%D',\n\tmessage: '%B',\n};\n\nexport type CommitsLogParser = LogParser<typeof commitsMapping>;\nlet _commitsParser: CommitsLogParser | undefined;\nexport type CommitsWithFilesLogParser = LogParserWithFilesAndStats<typeof commitsMapping>;\nlet _commitsWithFilesParser: CommitsWithFilesLogParser | undefined;\nexport type CommitsInFileRangeLogParser = LogParserWithFiles<typeof commitsMapping>;\nlet _commitsInFileRangeParser: CommitsInFileRangeLogParser | undefined;\n\nexport type ParsedCommit =\n\t| LogParsedEntry<typeof commitsMapping>\n\t| LogParsedEntryWithFiles<typeof commitsMapping>\n\t| LogParsedEntryWithFilesAndStats<typeof commitsMapping>;\n\nexport function getCommitsLogParser(\n\tincludeFiles: boolean,\n\tinFileRange?: boolean,\n): CommitsLogParser | CommitsWithFilesLogParser | CommitsInFileRangeLogParser {\n\tif (inFileRange) {\n\t\t_commitsInFileRangeParser ??= createLogParserWithPatch(commitsMapping);\n\t\treturn _commitsInFileRangeParser;\n\t}\n\n\tif (includeFiles) {\n\t\t_commitsWithFilesParser ??= createLogParserWithFilesAndStats(commitsMapping);\n\t\treturn _commitsWithFilesParser;\n\t}\n\n\t_commitsParser ??= createLogParser(commitsMapping);\n\treturn _commitsParser;\n}\n\nconst contributorsMapping = { sha: '%H', author: '%aN', email: '%aE', date: '%at', message: '%B' };\n\ntype ContributorsLogParser = LogParser<typeof contributorsMapping>;\nlet _contributorsParser: ContributorsLogParser | undefined;\ntype ContributorsWithStatsLogParser = LogParserWithStats<typeof contributorsMapping>;\nlet _contributorsWithStatsParser: ContributorsWithStatsLogParser | undefined;\n\nexport function getContributorsLogParser(stats?: boolean): ContributorsLogParser | ContributorsWithStatsLogParser {\n\tif (stats) {\n\t\t_contributorsWithStatsParser ??= createLogParserWithStats(contributorsMapping);\n\t\treturn _contributorsWithStatsParser;\n\t}\n\n\t_contributorsParser ??= createLogParser(contributorsMapping);\n\treturn _contributorsParser;\n}\n\nconst graphMapping = {\n\tsha: '%H',\n\tauthor: '%aN',\n\tauthorEmail: '%aE',\n\tauthorDate: '%at',\n\tcommitterDate: '%ct',\n\tparents: '%P',\n\ttips: '%D',\n\tmessage: '%B',\n};\n\nexport type GraphLogParser = LogParser<typeof graphMapping>;\nlet _graphParser: GraphLogParser | undefined;\ntype GraphWithStatsLogParser = LogParserWithStats<typeof graphMapping>;\nlet _graphWithStatsParser: GraphWithStatsLogParser | undefined;\n\n/**\n * Prints `tips` as full refnames (`HEAD -> refs/heads/main`, `refs/remotes/origin/main`, `tag: refs/tags/v1`), whatever\n * `log.decorate` says, since short names can't tell a local branch from a remote-tracking ref of the same name\n */\nexport function getGraphParser(stats?: boolean): GraphLogParser | GraphWithStatsLogParser {\n\tif (stats) {\n\t\tif (_graphWithStatsParser == null) {\n\t\t\t_graphWithStatsParser = createLogParserWithStats(graphMapping);\n\t\t\t_graphWithStatsParser.arguments.push('--decorate=full');\n\t\t}\n\t\treturn _graphWithStatsParser;\n\t}\n\n\tif (_graphParser == null) {\n\t\t_graphParser = createLogParser(graphMapping);\n\t\t_graphParser.arguments.push('--decorate=full');\n\t}\n\treturn _graphParser;\n}\n\ntype ShaLogParser = Parser<string>;\nlet _shaParser: ShaLogParser | undefined;\n\nexport function getShaLogParser(): ShaLogParser {\n\t_shaParser ??= createLogParserSingle('%H');\n\treturn _shaParser;\n}\n\nconst shaAndDateMapping = { sha: '%H', authorDate: '%at', committerDate: '%ct' };\nconst shaAndDateAndTipsMapping = { sha: '%H', authorDate: '%at', committerDate: '%ct', tips: '%D' };\n\ntype ShaAndDatesLogParser = LogParser<typeof shaAndDateMapping & { tips?: string }>;\nlet _shaAndDatesParser: ShaAndDatesLogParser | undefined;\ntype ShaAndDatesAndTipsLogParser = LogParser<typeof shaAndDateAndTipsMapping>;\nlet _shaAndDatesAndTipsParser: ShaAndDatesAndTipsLogParser | undefined;\n\nexport function getShaAndDatesLogParser(includeTips?: boolean): ShaAndDatesLogParser | ShaAndDatesAndTipsLogParser {\n\tif (includeTips) {\n\t\t_shaAndDatesAndTipsParser ??= createLogParser(shaAndDateAndTipsMapping);\n\t\treturn _shaAndDatesAndTipsParser;\n\t}\n\n\t_shaAndDatesParser ??= createLogParser(shaAndDateMapping);\n\treturn _shaAndDatesParser;\n}\n\ntype ShaAndDatesWithFilesLogParser = LogParserWithFiles<typeof shaAndDateMapping & { tips?: string }>;\nlet _shaAndDatesWithFilesParser: ShaAndDatesWithFilesLogParser | undefined;\n\ntype ShaAndDatesAndTipsWithFilesLogParser = LogParserWithFiles<typeof shaAndDateAndTipsMapping>;\nlet _shaAndDatesAndTipsWithFilesParser: ShaAndDatesAndTipsWithFilesLogParser | undefined;\n\nexport function getShaAndDatesWithFilesLogParser(\n\tincludeTips?: boolean,\n): ShaAndDatesWithFilesLogParser | ShaAndDatesAndTipsWithFilesLogParser {\n\tif (includeTips) {\n\t\t_shaAndDatesAndTipsWithFilesParser ??= createLogParserWithFiles(shaAndDateAndTipsMapping);\n\t\treturn _shaAndDatesAndTipsWithFilesParser;\n\t}\n\n\t_shaAndDatesWithFilesParser ??= createLogParserWithFiles(shaAndDateMapping);\n\treturn _shaAndDatesWithFilesParser;\n}\n\nconst shaMapping = { sha: '%H' };\n\ntype ShaAndFilesAndStatsLogParser = LogParserWithFilesAndStats<typeof shaMapping>;\nlet _shaAndFilesAndStatsParser: ShaAndFilesAndStatsLogParser | undefined;\n\nexport function getShaAndFilesAndStatsLogParser(): ShaAndFilesAndStatsLogParser {\n\t_shaAndFilesAndStatsParser ??= createLogParserWithFilesAndStats(shaMapping);\n\treturn _shaAndFilesAndStatsParser;\n}\n\ntype ShaAndFileRangeLogParser = LogParserWithFiles<typeof shaMapping>;\nlet _shaAndFileRangeParser: ShaAndFileRangeLogParser | undefined;\n\nexport function getShaAndFileRangeLogParser(): ShaAndFileRangeLogParser {\n\t_shaAndFileRangeParser ??= createLogParserWithPatch(shaMapping);\n\treturn _shaAndFileRangeParser;\n}\n\ntype ShaAndFileSummaryLogParser = LogParserWithFileSummary<typeof shaMapping>;\nlet _shaAndFileSummaryParser: ShaAndFileSummaryLogParser | undefined;\n\nexport function getShaAndFileSummaryLogParser(): ShaAndFileSummaryLogParser {\n\t_shaAndFileSummaryParser ??= createLogParserWithFileSummary(shaMapping);\n\treturn _shaAndFileSummaryParser;\n}\n\ntype ShaAndStatsLogParser = LogParserWithStats<typeof shaMapping>;\nlet _shaAndStatsParser: ShaAndStatsLogParser | undefined;\n\nexport function getShaAndStatsLogParser(): ShaAndStatsLogParser {\n\t_shaAndStatsParser ??= createLogParserWithStats(shaMapping);\n\treturn _shaAndStatsParser;\n}\n\nconst stashMapping = {\n\tsha: '%H',\n\tauthorDate: '%at',\n\tcommittedDate: '%ct',\n\tparents: '%P',\n\tstashName: '%gd',\n\tsummary: '%gs',\n\t// %s (commit subject) is git's authoritative `On <branch>: …` / `WIP on <branch>: …` record,\n\t// present even when the reflog message (%gs) was customized (autostash, `git stash store -m`).\n\t// Used downstream to extract `stashOnRef` reliably.\n\tcommitSubject: '%s',\n};\n\ntype StashLogParser = LogParser<typeof stashMapping>;\nlet _stashParser: StashLogParser | undefined;\ntype StashLogParserWithFiles = LogParserWithFilesAndStats<typeof stashMapping>;\nlet _stashParserWithFiles: StashLogParserWithFiles | undefined;\n\nexport type ParsedStash = LogParsedEntry<typeof stashMapping>;\nexport type ParsedStashWithFiles = LogParsedEntryWithFilesAndStats<typeof stashMapping>;\n\nexport function getStashLogParser(includeFiles?: false): StashLogParser;\nexport function getStashLogParser(includeFiles: true): StashLogParserWithFiles;\nexport function getStashLogParser(includeFiles?: boolean): StashLogParser | StashLogParserWithFiles;\nexport function getStashLogParser(includeFiles?: boolean): StashLogParser | StashLogParserWithFiles {\n\tif (includeFiles) {\n\t\t_stashParserWithFiles ??= createLogParserWithFilesAndStats(stashMapping);\n\t\treturn _stashParserWithFiles;\n\t}\n\n\t_stashParser ??= createLogParser(stashMapping);\n\treturn _stashParser;\n}\n\ntype StashFilesOnlyLogParser = LogParserWithFilesAndStats<void>;\nlet _stashFilesOnlyParser: StashFilesOnlyLogParser | undefined;\n\nexport function getStashFilesOnlyLogParser(): StashFilesOnlyLogParser {\n\t_stashFilesOnlyParser ??= createLogParserWithFilesAndStats();\n\treturn _stashFilesOnlyParser;\n}\n\n// Parser types\nexport type Parser<T> = {\n\targuments: string[];\n\tseparators: { record: string; field: string };\n\tparse: (data: string | Iterable<string> | undefined) => Generator<T> | Iterable<T>;\n\tparseAsync?: never;\n};\nexport type AsyncParser<T> = {\n\targuments: string[];\n\tseparators: { record: string; field: string };\n\tparse: (data: string | Iterable<string> | undefined) => Generator<T> | Iterable<T>;\n\tparseAsync: (stream: AsyncGenerator<string>) => AsyncGenerator<T>;\n\t/**\n\t * Like `parseAsync` but yields one ARRAY of entries per stream chunk, scanning each chunk\n\t * synchronously with plain `indexOf` (no per-field iterator machinery). Bulk consumers (the\n\t * graph walk) avoid two promise hops per record — thousands of awaited yields collapse to one\n\t * per chunk. Only the plain parser implements it; per-record `parseAsync` remains the\n\t * general-purpose path.\n\t */\n\tparseAsyncBatched?: (stream: AsyncGenerator<string>) => AsyncGenerator<T[]>;\n};\n\nexport type LogParser<T> = AsyncParser<LogParsedEntry<T>>;\ntype LogParserWithFiles<T> = AsyncParser<LogParsedEntryWithFiles<T>>;\ntype LogParserWithFilesAndStats<T> = AsyncParser<LogParsedEntryWithFilesAndStats<T>>;\ntype LogParserWithFileSummary<T> = Parser<LogParsedEntryWithFiles<T>>;\ntype LogParserWithStats<T> = AsyncParser<LogParsedEntryWithStats<T>>;\n\n// Parsed entry types\ntype LogParsedEntry<T> = { [K in keyof T]: string } & { files?: never; stats?: never };\ntype LogParsedEntryWithFiles<T> = { [K in keyof T]: string } & { files: LogParsedFile[]; stats?: never };\ntype LogParsedEntryWithFilesAndStats<T> = { [K in keyof T]: string } & {\n\tfiles: LogParsedFileWithStats[];\n\tstats: LogParsedStatsWithFilesStats;\n};\ntype LogParsedEntryWithStats<T> = { [K in keyof T]: string } & { stats: LogParsedStats };\n\n// Parsed types\nexport interface LogParsedFile {\n\tstatus?: string;\n\tpath: string;\n\toriginalPath?: string;\n\tadditions?: never;\n\tdeletions?: never;\n\trange?: LogParsedRange;\n\toriginalRange?: LogParsedRange;\n\t/** Git file mode (e.g., 100644=regular, 100755=executable, 120000=symlink, 160000=submodule) */\n\tmode?: string;\n\t/** For submodule entries (mode 160000), the commit SHA the submodule points to */\n\toid?: string;\n\t/** For submodule entries (mode 160000), the previous commit SHA */\n\tpreviousOid?: string;\n}\ntype LogParsedFileWithStats = Omit<LogParsedFile, 'additions' | 'deletions'> & {\n\tadditions: number;\n\tdeletions: number;\n};\ninterface LogParsedRange {\n\tstartLine: number;\n\tendLine: number;\n}\ninterface LogParsedStats {\n\tfiles: number;\n\tadditions: number;\n\tdeletions: number;\n}\ninterface LogParsedStatsWithFilesStats {\n\tfiles: { added: number; changed: number; deleted: number };\n\tadditions: number;\n\tdeletions: number;\n}\n\nconst recordSep = '\\x1E'; // ASCII Record Separator character\nconst recordFormatSep = '%x1E';\nconst fieldSep = '\\x1D'; // ASCII Group Separator character\nconst fieldFormatSep = '%x1D';\n\n// A complete record terminates every mapped field with a field separator — a cancelled/early-closed\n// stream resolves cleanly with a truncated tail missing at least the final terminator.\nfunction isCompleteRecord(record: string, requiredFields: number): boolean {\n\tlet count = 0;\n\tlet index = -1;\n\twhile (count < requiredFields) {\n\t\tindex = record.indexOf(fieldSep, index + 1);\n\t\tif (index === -1) return false;\n\n\t\tcount++;\n\t}\n\treturn true;\n}\n\nexport function createLogParser<T extends Record<string, string>>(mapping: ExtractAll<T, string>): LogParser<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntry<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParser.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntry<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tentry = {} as unknown as LogParsedEntry<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue;\n\n\t\t\t\tcount++;\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntry<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParser.parseAsync', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntry<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\t\t\t// Drop a truncated tail (cancelled/early-closed stream) instead of yielding undefined/partial fields.\n\t\t\tif (!isCompleteRecord(record, keys.length)) continue;\n\n\t\t\tentry = {} as unknown as LogParsedEntry<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue;\n\n\t\t\t\tcount++;\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\t// Builds one entry from a complete record with a plain `indexOf` walk. Matches `parseAsync`'s\n\t// semantics exactly: fields beyond the mapping are ignored, and every mapped field is\n\t// terminated by a field separator (the format string appends one after each field).\n\tfunction parseRecord(record: string): LogParsedEntry<T> {\n\t\t// Fields sliced out of the record become long-lived (graph rows); flatten the record so they\n\t\t// pin a record-sized copy rather than the full walk output the record was sliced from.\n\t\trecord = flattenString(record);\n\t\tconst entry = {} as unknown as LogParsedEntry<T>;\n\t\tlet start = 0;\n\t\tfor (const key of keys) {\n\t\t\tconst end = record.indexOf(fieldSep, start);\n\t\t\tif (end === -1) break;\n\n\t\t\tentry[key] = record.substring(start, end) as T[keyof T];\n\t\t\tstart = end + 1;\n\t\t}\n\t\treturn entry;\n\t}\n\n\tasync function* parseAsyncBatched(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntry<T>[]> {\n\t\tusing sw = maybeStopWatch('Git.LogParser.parseAsyncBatched', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tlet count = 0;\n\t\tlet buffer = '';\n\t\tfor await (const chunk of stream) {\n\t\t\tbuffer += chunk;\n\n\t\t\tlet batch: LogParsedEntry<T>[] | undefined;\n\t\t\tlet start = 0;\n\t\t\twhile (true) {\n\t\t\t\tconst end = buffer.indexOf(recordSep, start);\n\t\t\t\tif (end === -1) break;\n\n\t\t\t\tif (end > start) {\n\t\t\t\t\t(batch ??= []).push(parseRecord(buffer.substring(start, end)));\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t\tstart = end + 1;\n\t\t\t}\n\t\t\tif (start > 0) {\n\t\t\t\tbuffer = buffer.substring(start);\n\t\t\t}\n\t\t\tif (batch != null) {\n\t\t\t\tyield batch;\n\t\t\t}\n\t\t}\n\t\t// Trailing record (the stream doesn't end with a record separator); a truncated tail from a\n\t\t// cancelled/early-closed stream is missing a field terminator and must be dropped, not parsed.\n\t\tif (buffer.length > 0 && isCompleteRecord(buffer, keys.length)) {\n\t\t\tcount++;\n\t\t\tyield [parseRecord(buffer)];\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t\tparseAsyncBatched: parseAsyncBatched,\n\t};\n}\n\nfunction createLogParserWithFilesAndStats(): LogParserWithFilesAndStats<void>;\nfunction createLogParserWithFilesAndStats<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFilesAndStats<T>;\nfunction createLogParserWithFilesAndStats<T extends Record<string, string> | void>(\n\tmapping?: ExtractAll<T, string>,\n): LogParserWithFilesAndStats<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tif (mapping != null) {\n\t\tfor (const key in mapping) {\n\t\t\tkeys.push(key);\n\t\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t\t}\n\t}\n\t// Use --raw instead of --summary to get accurate mode info for ALL files (not just creates/deletes)\n\t// This allows us to reliably detect submodules (mode 160000) even for modifications\n\tconst args = [`--format=${format}`, '--numstat', '--raw'];\n\n\tfunction parseFilesAndStats(content: string): LogParsedFileWithStats[] {\n\t\tconst files: LogParsedFileWithStats[] = [];\n\t\tif (!content?.length) return files;\n\n\t\t// Map to store info from --raw lines: path -> { mode, status, originalPath, oid?, previousOid? }\n\t\t// Raw format: :<old_mode> <new_mode> <old_sha> <new_sha> <status>[score]\\t<path>[\\t<new_path>]\n\t\tconst infoMap = new Map<\n\t\t\tstring,\n\t\t\t{ mode: string; status: string; originalPath?: string; oid?: string; previousOid?: string }\n\t\t>();\n\n\t\tlet startIndex: number;\n\t\tlet endIndex: number;\n\n\t\t// Single pass: --raw lines come first, then --numstat lines\n\t\tfor (const line of iterateByDelimiter(content, '\\n')) {\n\t\t\tif (!line) continue;\n\n\t\t\tif (line.charCodeAt(0) === 58 /* ':' */) {\n\t\t\t\t// --raw format: :<old_mode> <new_mode> <old_sha> <new_sha> <status>[score]\\t<path>[\\t<new_path>]\n\t\t\t\tconst tabIndex = line.indexOf('\\t');\n\t\t\t\tif (tabIndex === -1) continue;\n\n\t\t\t\t// Parse metadata using indexOf instead of split() to avoid array allocation\n\t\t\t\t// Format after ':': <old_mode> <new_mode> <old_sha> <new_sha> <status>[score]\n\n\t\t\t\t// Skip old_mode, find new_mode\n\t\t\t\tstartIndex = line.indexOf(' ') + 1;\n\t\t\t\tif (startIndex === 0) continue;\n\n\t\t\t\tendIndex = line.indexOf(' ', startIndex);\n\t\t\t\tif (endIndex === -1) continue;\n\n\t\t\t\tconst newMode = line.substring(startIndex, endIndex);\n\n\t\t\t\t// Parse old_sha and new_sha positions\n\t\t\t\tconst oldShaStart = endIndex + 1;\n\t\t\t\tconst oldShaEnd = line.indexOf(' ', oldShaStart);\n\t\t\t\tif (oldShaEnd === -1) continue;\n\n\t\t\t\tconst newShaStart = oldShaEnd + 1;\n\t\t\t\tconst newShaEnd = line.indexOf(' ', newShaStart);\n\t\t\t\tif (newShaEnd === -1) continue;\n\n\t\t\t\t// For submodules (mode 160000), capture the commit SHAs\n\t\t\t\tlet oid: string | undefined;\n\t\t\t\tlet previousOid: string | undefined;\n\t\t\t\tif (newMode === '160000') {\n\t\t\t\t\tpreviousOid = line.substring(oldShaStart, oldShaEnd);\n\t\t\t\t\toid = line.substring(newShaStart, newShaEnd);\n\t\t\t\t}\n\n\t\t\t\t// Status is first char after the last space\n\t\t\t\tstartIndex = newShaEnd;\n\t\t\t\tconst status = line.charAt(startIndex + 1);\n\n\t\t\t\t// Parse path(s) using indexOf instead of split\n\t\t\t\tconst pathPart = line.substring(tabIndex + 1);\n\t\t\t\tlet path: string;\n\t\t\t\tlet originalPath: string | undefined;\n\n\t\t\t\tconst pathTabIndex = pathPart.indexOf('\\t');\n\t\t\t\tif ((status === 'R' || status === 'C') && pathTabIndex !== -1) {\n\t\t\t\t\t// For renames/copies: old_path\\tnew_path\n\t\t\t\t\toriginalPath = pathPart.substring(0, pathTabIndex);\n\t\t\t\t\tpath = pathPart.substring(pathTabIndex + 1);\n\t\t\t\t} else {\n\t\t\t\t\tpath = pathPart;\n\t\t\t\t}\n\n\t\t\t\t// Trim to match parseCopyOrRename which trims renamed paths\n\t\t\t\tinfoMap.set(path.trim(), {\n\t\t\t\t\tmode: newMode,\n\t\t\t\t\tstatus: status,\n\t\t\t\t\toriginalPath: originalPath?.trim(),\n\t\t\t\t\toid: oid,\n\t\t\t\t\tpreviousOid: previousOid,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// --numstat format: <additions>\\t<deletions>\\t<path>\n\t\t\t\t// With renames: <additions>\\t<deletions>\\t{prefix/old => new/suffix}\n\t\t\t\tendIndex = line.indexOf('\\t');\n\t\t\t\tif (endIndex === -1) {\n\t\t\t\t\tdebugger;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst additions = line.substring(0, endIndex);\n\n\t\t\t\tstartIndex = endIndex + 1;\n\t\t\t\tendIndex = line.indexOf('\\t', startIndex);\n\t\t\t\tif (endIndex === -1) {\n\t\t\t\t\tdebugger;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst deletions = line.substring(startIndex, endIndex);\n\n\t\t\t\tlet path = line.substring(endIndex + 1);\n\t\t\t\tlet originalPath: string | undefined;\n\n\t\t\t\t// Parse rename format from numstat (e.g., \"{old => new}/path\")\n\t\t\t\t({ path, originalPath } = parseCopyOrRename(path, false));\n\n\t\t\t\t// Look up --raw info for this path (keyed by trimmed new/destination path)\n\t\t\t\tconst info = infoMap.get(path.trim());\n\n\t\t\t\t// Use status from --raw if available; provides accurate status (A/D/M/R/C/T)\n\t\t\t\tconst status = info?.status ?? (originalPath ? 'R' : 'M');\n\n\t\t\t\t// Use originalPath from --raw if available (cleaner than numstat format)\n\t\t\t\tif (info?.originalPath) {\n\t\t\t\t\toriginalPath = info.originalPath;\n\t\t\t\t}\n\n\t\t\t\tconst file: LogParsedFileWithStats = {\n\t\t\t\t\tstatus: status,\n\t\t\t\t\tpath: path.trim(),\n\t\t\t\t\toriginalPath: originalPath?.trim(),\n\t\t\t\t\tadditions: additions === '-' ? 0 : parseInt(additions, 10) || 0,\n\t\t\t\t\tdeletions: deletions === '-' ? 0 : parseInt(deletions, 10) || 0,\n\t\t\t\t\t// Store mode for consumers to derive file type (160000=submodule, 100755=executable, etc.)\n\t\t\t\t\tmode: info?.mode,\n\t\t\t\t\toid: info?.oid,\n\t\t\t\t\tpreviousOid: info?.previousOid,\n\t\t\t\t};\n\n\t\t\t\tfiles.push(file);\n\t\t\t}\n\t\t}\n\n\t\treturn files;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFilesAndStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithFilesAndStats.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFilesAndStats<T>;\n\t\tlet files: LogParsedFileWithStats[];\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFilesAndStats<T>;\n\t\t\tfiles = [];\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFilesAndStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commits and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tfiles.push(...parseFilesAndStats(summary));\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentry.files = files;\n\t\t\tif (files.length) {\n\t\t\t\tentry.stats = { additions: 0, deletions: 0, files: { added: 0, deleted: 0, changed: 0 } };\n\n\t\t\t\tfor (const f of files) {\n\t\t\t\t\tif (f.additions || f.deletions) {\n\t\t\t\t\t\tentry.stats.additions += f.additions ?? 0;\n\t\t\t\t\t\tentry.stats.deletions += f.deletions ?? 0;\n\t\t\t\t\t\tif (f.status === 'A' || f.status === '?') {\n\t\t\t\t\t\t\tentry.stats.files.added++;\n\t\t\t\t\t\t} else if (f.status === 'D') {\n\t\t\t\t\t\t\tentry.stats.files.deleted++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tentry.stats.files.changed++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFilesAndStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithFilesAndStats.parseAsync', {\n\t\t\tlog: { onlyExit: true, level: 'debug' },\n\t\t});\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFilesAndStats<T>;\n\t\tlet files: LogParsedFileWithStats[];\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\t\t\t// Drop a truncated tail (cancelled/early-closed stream) instead of yielding undefined/partial fields.\n\t\t\tif (!isCompleteRecord(record, keys.length)) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFilesAndStats<T>;\n\t\t\tfiles = [];\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFilesAndStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commits and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tfiles.push(...parseFilesAndStats(summary));\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentry.files = files;\n\t\t\tif (files.length) {\n\t\t\t\tentry.stats = { additions: 0, deletions: 0, files: { added: 0, deleted: 0, changed: 0 } };\n\n\t\t\t\tfor (const f of files) {\n\t\t\t\t\tif (f.additions || f.deletions) {\n\t\t\t\t\t\tentry.stats.additions += f.additions ?? 0;\n\t\t\t\t\t\tentry.stats.deletions += f.deletions ?? 0;\n\t\t\t\t\t\tif (f.status === 'A' || f.status === '?') {\n\t\t\t\t\t\t\tentry.stats.files.added++;\n\t\t\t\t\t\t} else if (f.status === 'D') {\n\t\t\t\t\t\t\tentry.stats.files.deleted++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tentry.stats.files.changed++;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n\nfunction createLogParserWithFileSummary(): LogParserWithFileSummary<void>;\nfunction createLogParserWithFileSummary<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFileSummary<T>;\nfunction createLogParserWithFileSummary<T extends Record<string, string> | void>(\n\tmapping?: ExtractAll<T, string>,\n): LogParserWithFileSummary<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tif (mapping != null) {\n\t\tfor (const key in mapping) {\n\t\t\tkeys.push(key);\n\t\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t\t}\n\t}\n\tconst args = [`--format=${format}`, '--summary'];\n\n\tfunction parseFileSummary(content: string): LogParsedFile[] {\n\t\tconst files: LogParsedFile[] = [];\n\t\tif (!content?.length) return files;\n\n\t\tlet startIndex;\n\n\t\tlet path;\n\t\tlet originalPath;\n\t\tlet status;\n\n\t\tfor (const line of iterateByDelimiter(content, '\\n')) {\n\t\t\tif (!line) continue;\n\n\t\t\tif (line.startsWith(' rename ')) {\n\t\t\t\t({ path, originalPath } = parseCopyOrRename(line.substring(8 /* move past ' rename ' */), true));\n\t\t\t\tfiles.push({ path: path, originalPath: originalPath, status: 'R' });\n\t\t\t} else if (line.startsWith(' copy ')) {\n\t\t\t\t({ path, originalPath } = parseCopyOrRename(line.substring(6 /* move past ' copy ' */), true));\n\t\t\t\tfiles.push({ path: path, originalPath: originalPath, status: 'C' });\n\t\t\t} else {\n\t\t\t\tif (line.startsWith(' create mode ')) {\n\t\t\t\t\tstatus = 'A';\n\t\t\t\t} else if (line.startsWith(' delete mode ')) {\n\t\t\t\t\tstatus = 'D';\n\t\t\t\t} else {\n\t\t\t\t\t// Ignore \" mode change \" lines\n\t\t\t\t\tif (!line.startsWith(' mode change ')) {\n\t\t\t\t\t\tdebugger;\n\t\t\t\t\t}\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tstartIndex = line.indexOf(' ', 13 /* move past 'create mode <num>' or 'delete mode <num>' */);\n\t\t\t\tif (startIndex > -1) {\n\t\t\t\t\tpath = line.substring(startIndex + 1);\n\t\t\t\t\tfiles.push({ path: path, status: status });\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn files;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithFileSummary.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet files: LogParsedFile[];\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfiles = [];\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commits and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tfiles.push(...parseFileSummary(summary));\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tentry.files = files;\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t};\n}\n\nfunction parseCopyOrRename(path: string, stripPercentage: boolean): { path: string; originalPath?: string } {\n\tconst renameIndex = path.indexOf(' => ');\n\tif (renameIndex === -1) return { path: path };\n\n\tlet hasBraces = true;\n\n\tlet openIndex = path.indexOf('{');\n\tif (openIndex === -1) {\n\t\thasBraces = false;\n\t\topenIndex = 0;\n\t}\n\n\tlet closeIndex = path.indexOf('}', openIndex);\n\tif (closeIndex === -1) {\n\t\thasBraces = false;\n\t\tcloseIndex = path.length;\n\t}\n\n\tconst prefix = path.substring(0, openIndex);\n\tconst fromPart = path.substring(hasBraces ? openIndex + 1 : 0, renameIndex);\n\tlet toPart = path.substring(renameIndex + 4, closeIndex);\n\tlet suffix = path.substring(closeIndex + 1);\n\n\t// Check for percentage marker which always appears at the end as (xx%)\n\tif (stripPercentage) {\n\t\tif (hasBraces) {\n\t\t\tif (suffix.endsWith('%)')) {\n\t\t\t\t// Find the last open paren that starts the percentage\n\t\t\t\tconst percentPos = suffix.lastIndexOf(' (');\n\t\t\t\tif (percentPos > -1) {\n\t\t\t\t\tsuffix = suffix.substring(0, percentPos);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (toPart.endsWith('%)')) {\n\t\t\t// Find the last open paren that starts the percentage\n\t\t\tconst percentPos = toPart.lastIndexOf(' (');\n\t\t\tif (percentPos > -1) {\n\t\t\t\ttoPart = toPart.substring(0, percentPos);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tpath: normalizePath(joinPaths(prefix, toPart, suffix).trim()),\n\t\toriginalPath: normalizePath(joinPaths(prefix, fromPart, suffix).trim()),\n\t};\n}\n\nfunction createLogParserSingle(field: string): Parser<string> {\n\tconst args = ['-z', `--format=${field}`];\n\n\tfunction parse(data: string | Iterable<string> | undefined): Iterable<string> {\n\t\tusing _sw = maybeStopWatch('Git.LogParserSingle.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\treturn data ? iterateByDelimiter(data, '\\0') : [];\n\t}\n\n\treturn { arguments: args, separators: { record: '\\0', field: '\\0' }, parse: parse };\n}\n\nfunction createLogParserWithPatch<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFiles<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction parsePatch(content: string): LogParsedFile | undefined {\n\t\tconst lines = iterateByDelimiter(content, '\\n');\n\t\tlet line = lines.next();\n\t\tif (line.done) return undefined;\n\n\t\tconst fileMatch = diffRegex.exec(line.value);\n\t\tif (fileMatch == null) return undefined;\n\n\t\twhile (!line.done && !line.value.startsWith('@@ ')) {\n\t\t\tline = lines.next();\n\t\t}\n\n\t\tconst rangeMatch = line.value.match(diffHunkRegex);\n\n\t\tlet range: LogParsedRange | undefined;\n\t\tlet originalRange: LogParsedRange | undefined;\n\t\tif (rangeMatch != null) {\n\t\t\tlet start = parseInt(rangeMatch[1], 10);\n\t\t\tlet count = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : 1;\n\n\t\t\toriginalRange = start && count ? { startLine: start, endLine: start + count - 1 } : undefined;\n\n\t\t\tstart = parseInt(rangeMatch[3], 10);\n\t\t\tcount = rangeMatch[4] ? parseInt(rangeMatch[4], 10) : 1;\n\n\t\t\trange = start && count ? { startLine: start, endLine: start + count - 1 } : undefined;\n\t\t}\n\n\t\treturn {\n\t\t\tstatus: (fileMatch[1] === 'rename' ? 'R' : 'M') satisfies GitFileIndexStatus,\n\t\t\tpath: fileMatch[2],\n\t\t\toriginalPath: fileMatch[1] === 'rename' ? fileMatch[3] : undefined,\n\t\t\trange: range,\n\t\t\toriginalRange: originalRange,\n\t\t};\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithPatch.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and patch, if any\n\t\t\t\t\tconst patch = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tconst file = parsePatch(patch);\n\t\t\t\t\tentry.files = file != null ? [file] : [];\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithPatch.parseAsync', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\t\t\t// Drop a truncated tail (cancelled/early-closed stream) instead of yielding undefined/partial fields.\n\t\t\tif (!isCompleteRecord(record, keys.length)) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and patch, if any\n\t\t\t\t\tconst patch = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tconst file = parsePatch(patch);\n\t\t\t\t\tentry.files = file != null ? [file] : [];\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n\nfunction createLogParserWithStats<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithStats<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`, '--shortstat'];\n\n\tfunction parseStats(content: string): LogParsedStats {\n\t\tconst stats: LogParsedStats = { files: 0, additions: 0, deletions: 0 };\n\t\tcontent = content?.trim();\n\t\tif (!content?.length) return { files: 0, additions: 0, deletions: 0 };\n\n\t\tlet filesEnd;\n\t\tlet filesIndex = content.indexOf(' files changed');\n\t\tif (filesIndex === -1) {\n\t\t\tfilesIndex = content.indexOf(' file changed');\n\t\t\tif (filesIndex === -1) return stats;\n\n\t\t\tfilesEnd = filesIndex + ' file changed'.length;\n\t\t} else {\n\t\t\tfilesEnd = filesIndex + ' files changed'.length;\n\t\t}\n\n\t\t// Extract number before \" files changed\"\n\t\tconst filesPart = content.substring(0, filesIndex).trim();\n\t\tstats.files = parseInt(filesPart, 10) || 0;\n\n\t\t// Extract additions if present\n\t\tconst additionsIndex = content.indexOf(' insertion', filesEnd);\n\t\tif (additionsIndex !== -1) {\n\t\t\t// Look for the number before \"insertion(+)\" or \"insertions(+)\"\n\t\t\tconst spaceIndex = content.lastIndexOf(' ', additionsIndex - 1);\n\t\t\tif (spaceIndex !== -1) {\n\t\t\t\tconst additionsPart = content.substring(spaceIndex, additionsIndex).trim();\n\t\t\t\tstats.additions = parseInt(additionsPart, 10) || 0;\n\t\t\t}\n\t\t}\n\n\t\t// Extract deletions if present\n\t\tconst deletionsIndex = content.indexOf(' deletion', filesEnd);\n\t\tif (deletionsIndex !== -1) {\n\t\t\t// Look for the number before \"\"deletion(-)\" or \"deletions(-)\"\n\t\t\tconst spaceIndex = content.lastIndexOf(' ', deletionsIndex - 1);\n\t\t\tif (spaceIndex !== -1) {\n\t\t\t\tconst deletionsPart = content.substring(spaceIndex, deletionsIndex).trim();\n\t\t\t\tstats.deletions = parseInt(deletionsPart, 10) || 0;\n\t\t\t}\n\t\t}\n\n\t\treturn stats;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithStats.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithStats<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithStats<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.stats = parseStats(summary);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithStats<T>> {\n\t\tusing sw = maybeStopWatch('Git.LogParserWithStats.parseAsync', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithStats<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithStats<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithStats<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files/summary, if any\n\t\t\t\t\tconst summary = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.stats = parseStats(summary);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n\nfunction createLogParserWithFiles<T extends Record<string, string>>(\n\tmapping: ExtractAll<T, string>,\n): LogParserWithFiles<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`, '--name-only'];\n\n\tfunction parseFileNames(content: string): LogParsedFile[] {\n\t\tconst files: LogParsedFile[] = [];\n\t\tif (!content?.length) return files;\n\n\t\tfor (const line of iterateByDelimiter(content, '\\n')) {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (!trimmed) continue;\n\n\t\t\tfiles.push({ path: trimmed });\n\t\t}\n\n\t\treturn files;\n\t}\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.createLogParserWithFiles.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files, if any\n\t\t\t\t\tconst files = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.files = parseFileNames(files);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\tasync function* parseAsync(stream: AsyncGenerator<string>): AsyncGenerator<LogParsedEntryWithFiles<T>> {\n\t\tusing sw = maybeStopWatch('Git.createLogParserWithFiles.parseAsync', {\n\t\t\tlog: { onlyExit: true, level: 'debug' },\n\t\t});\n\n\t\tconst records = iterateAsyncByDelimiter(stream, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: LogParsedEntryWithFiles<T>;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor await (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as LogParsedEntryWithFiles<T>;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\n\t\t\t\tif (fieldCount < keys.length) {\n\t\t\t\t\tentry[keys[fieldCount++]] = field.value as LogParsedEntryWithFiles<T>[keyof T];\n\t\t\t\t} else if (fieldCount === keys.length) {\n\t\t\t\t\t// Slice off the first newlines between the commit data and files, if any\n\t\t\t\t\tconst files = field.value.startsWith('\\n\\n')\n\t\t\t\t\t\t? field.value.substring(2)\n\t\t\t\t\t\t: field.value.startsWith('\\n')\n\t\t\t\t\t\t\t? field.value.substring(1)\n\t\t\t\t\t\t\t: field.value;\n\t\t\t\t\tentry.files = parseFileNames(files);\n\t\t\t\t} else {\n\t\t\t\t\tdebugger;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn {\n\t\targuments: args,\n\t\tseparators: { record: recordSep, field: fieldSep },\n\t\tparse: parse,\n\t\tparseAsync: parseAsync,\n\t};\n}\n"],"mappings":"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"}
@@ -23,12 +23,14 @@ const refMapping = {
23
23
  authorDate: `%(authordate:iso8601)`,
24
24
  subject: `%(subject)`,
25
25
  worktreePath: void 0,
26
- upstreamRemote: void 0
26
+ upstreamRemote: void 0,
27
+ upstreamRemoteRef: void 0
27
28
  };
28
29
  const refMappingSupportsWorktreePath = {
29
30
  ...refMapping,
30
31
  worktreePath: "%(worktreepath)",
31
- upstreamRemote: "%(upstream:remotename)"
32
+ upstreamRemote: "%(upstream:remotename)",
33
+ upstreamRemoteRef: "%(upstream:remoteref)"
32
34
  };
33
35
  let _refParser;
34
36
  let _refParserWithWorktree;
@@ -1 +1 @@
1
- {"version":3,"file":"refParser.js","names":[],"sources":["../../../../git-cli/src/parsers/refParser.ts"],"sourcesContent":["import type { FilteredGitFeatures } from '@gitlens/git/features.js';\nimport type { RefRecord } from '@gitlens/git/models/reference.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { ExtractAll } from '@gitlens/utils/types.js';\nimport type { Parser } from './logParser.js';\n\n/**\n * Unified `for-each-ref` mapping covering branches, remotes, and tags in a single pass.\n * Fields inapplicable to a given ref-type come back as empty strings from git.\n *\n * NOTE: When adding a new field that is gated on a git feature flag, mirror the entry in\n * `refMappingSupportsWorktreePath` (and any future variants) — both maps MUST stay aligned\n * so callers can switch parsers based on `git.supported('git:for-each-ref')` without losing\n * field positions.\n */\nconst refMapping = {\n\tcurrent: `%(HEAD)`,\n\tname: `%(refname)`,\n\tobjectname: `%(objectname)`,\n\tpeeledObjectname: `%(*objectname)`,\n\tupstream: `%(upstream)`,\n\tupstreamTracking: `%(upstream:track)`,\n\tcommitterDate: `%(committerdate:iso8601)`,\n\tcreatorDate: `%(creatordate:iso8601)`,\n\tauthorDate: `%(authordate:iso8601)`,\n\tsubject: `%(subject)`,\n\tworktreePath: undefined,\n\tupstreamRemote: undefined,\n};\n\nconst refMappingSupportsWorktreePath = {\n\t...refMapping,\n\tworktreePath: '%(worktreepath)',\n\t// `%(upstream:remotename)` needs git 2.16, which the 2.23 `worktreePath` gate already implies\n\tupstreamRemote: '%(upstream:remotename)',\n};\n\ntype RefParser = Parser<RefRecord>;\n\nlet _refParser: RefParser | undefined;\nlet _refParserWithWorktree: RefParser | undefined;\n\nexport function getRefParser(supportedFeatures: FilteredGitFeatures<'git:for-each-ref'>[]): RefParser {\n\tif (supportedFeatures.includes('git:for-each-ref:worktreePath')) {\n\t\t_refParserWithWorktree ??= createRefParser(refMappingSupportsWorktreePath);\n\t\treturn _refParserWithWorktree;\n\t}\n\n\t_refParser ??= createRefParser(refMapping);\n\treturn _refParser;\n}\n\nconst recordSep = '\\x1E'; // ASCII Record Separator character\nconst recordFormatSep = '%1E';\nconst fieldSep = '\\x1D'; // ASCII Group Separator character\nconst fieldFormatSep = '%1D';\n\nfunction createRefParser<T extends Record<string, string | undefined>>(\n\tmapping: ExtractAll<T, string | undefined>,\n): Parser<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tconst value = mapping[key];\n\t\tif (!value) continue;\n\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<T> {\n\t\tusing sw = maybeStopWatch('Git.RefParser.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: T;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as T;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue; // Handle extra newlines at the end\n\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn { arguments: args, separators: { record: recordSep, field: fieldSep }, parse: parse };\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,aAAa;CAClB,SAAS;CACT,MAAM;CACN,YAAY;CACZ,kBAAkB;CAClB,UAAU;CACV,kBAAkB;CAClB,eAAe;CACf,aAAa;CACb,YAAY;CACZ,SAAS;CACT,cAAc,KAAA;CACd,gBAAgB,KAAA;AACjB;AAEA,MAAM,iCAAiC;CACtC,GAAG;CACH,cAAc;CAEd,gBAAgB;AACjB;AAIA,IAAI;AACJ,IAAI;AAEJ,SAAgB,aAAa,mBAAyE;CACrG,IAAI,kBAAkB,SAAS,+BAA+B,GAAG;EAChE,2BAA2B,gBAAgB,8BAA8B;EACzE,OAAO;CACR;CAEA,eAAe,gBAAgB,UAAU;CACzC,OAAO;AACR;AAEA,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,WAAW;AACjB,MAAM,iBAAiB;AAEvB,SAAS,gBACR,SACY;CACZ,IAAI,SAAS;CACb,MAAM,OAAwC,CAAC;CAC/C,KAAK,MAAM,OAAO,SAAS;EAE1B,IAAI,CADU,QAAQ,MACV;EAEZ,KAAK,KAAK,GAAG;EACb,UAAU,GAAG,QAAQ,OAAO;CAC7B;CAEA,MAAM,OAAO,CAAC,YAAY,QAAQ;CAElC,UAAU,MAAM,MAA2D;;;GAC1E,MAAM,KAAA,YAAA,EAAK,eAAe,uBAAuB,EAAE,KAAK;IAAE,UAAU;IAAM,OAAO;GAAQ,EAAE,CAAC,CAAA;GAE5F,IAAI,CAAC,MAAM;IACV,IAAI,KAAK,EAAE,QAAQ,WAAW,CAAC;IAC/B;GACD;GAEA,MAAM,UAAU,mBAAmB,MAAM,SAAS;GAElD,IAAI,QAAQ;GACZ,IAAI;GACJ,IAAI;GAEJ,KAAK,MAAM,UAAU,SAAS;IAC7B,IAAI,CAAC,OAAO,QAAQ;IAEpB;IACA,QAAQ,CAAC;IACT,SAAS,mBAAmB,QAAQ,QAAQ;IAE5C,IAAI,aAAa;IACjB,IAAI;IAEJ,OAAO,MAAM;KACZ,QAAQ,OAAO,KAAK;KACpB,IAAI,MAAM,MAAM;KAChB,IAAI,cAAc,KAAK,QAAQ;KAE/B,MAAM,KAAK,iBAAiB,MAAM;IACnC;IAEA,MAAM;GACP;GAEA,IAAI,KAAK,EAAE,QAAQ,WAAW,MAAM,UAAU,CAAC;;;;;;CAChD;CAEA,OAAO;EAAE,WAAW;EAAM,YAAY;GAAE,QAAQ;GAAW,OAAO;EAAS;EAAU;CAAM;AAC5F"}
1
+ {"version":3,"file":"refParser.js","names":[],"sources":["../../../../git-cli/src/parsers/refParser.ts"],"sourcesContent":["import type { FilteredGitFeatures } from '@gitlens/git/features.js';\nimport type { RefRecord } from '@gitlens/git/models/reference.js';\nimport { maybeStopWatch } from '@gitlens/utils/stopwatch.js';\nimport { iterateByDelimiter } from '@gitlens/utils/string.js';\nimport type { ExtractAll } from '@gitlens/utils/types.js';\nimport type { Parser } from './logParser.js';\n\n/**\n * Unified `for-each-ref` mapping covering branches, remotes, and tags in a single pass.\n * Fields inapplicable to a given ref-type come back as empty strings from git.\n *\n * NOTE: When adding a new field that is gated on a git feature flag, mirror the entry in\n * `refMappingSupportsWorktreePath` (and any future variants) — both maps MUST stay aligned\n * so callers can switch parsers based on `git.supported('git:for-each-ref')` without losing\n * field positions.\n */\nconst refMapping = {\n\tcurrent: `%(HEAD)`,\n\tname: `%(refname)`,\n\tobjectname: `%(objectname)`,\n\tpeeledObjectname: `%(*objectname)`,\n\tupstream: `%(upstream)`,\n\tupstreamTracking: `%(upstream:track)`,\n\tcommitterDate: `%(committerdate:iso8601)`,\n\tcreatorDate: `%(creatordate:iso8601)`,\n\tauthorDate: `%(authordate:iso8601)`,\n\tsubject: `%(subject)`,\n\tworktreePath: undefined,\n\tupstreamRemote: undefined,\n\tupstreamRemoteRef: undefined,\n};\n\nconst refMappingSupportsWorktreePath = {\n\t...refMapping,\n\tworktreePath: '%(worktreepath)',\n\t// `%(upstream:remotename)` and `%(upstream:remoteref)` need git 2.16, which the 2.23 `worktreePath` gate already implies\n\tupstreamRemote: '%(upstream:remotename)',\n\tupstreamRemoteRef: '%(upstream:remoteref)',\n};\n\ntype RefParser = Parser<RefRecord>;\n\nlet _refParser: RefParser | undefined;\nlet _refParserWithWorktree: RefParser | undefined;\n\nexport function getRefParser(supportedFeatures: FilteredGitFeatures<'git:for-each-ref'>[]): RefParser {\n\tif (supportedFeatures.includes('git:for-each-ref:worktreePath')) {\n\t\t_refParserWithWorktree ??= createRefParser(refMappingSupportsWorktreePath);\n\t\treturn _refParserWithWorktree;\n\t}\n\n\t_refParser ??= createRefParser(refMapping);\n\treturn _refParser;\n}\n\nconst recordSep = '\\x1E'; // ASCII Record Separator character\nconst recordFormatSep = '%1E';\nconst fieldSep = '\\x1D'; // ASCII Group Separator character\nconst fieldFormatSep = '%1D';\n\nfunction createRefParser<T extends Record<string, string | undefined>>(\n\tmapping: ExtractAll<T, string | undefined>,\n): Parser<T> {\n\tlet format = recordFormatSep;\n\tconst keys: (keyof ExtractAll<T, string>)[] = [];\n\tfor (const key in mapping) {\n\t\tconst value = mapping[key];\n\t\tif (!value) continue;\n\n\t\tkeys.push(key);\n\t\tformat += `${mapping[key]}${fieldFormatSep}`;\n\t}\n\n\tconst args = [`--format=${format}`];\n\n\tfunction* parse(data: string | Iterable<string> | undefined): Generator<T> {\n\t\tusing sw = maybeStopWatch('Git.RefParser.parse', { log: { onlyExit: true, level: 'debug' } });\n\n\t\tif (!data) {\n\t\t\tsw?.stop({ suffix: ` no data` });\n\t\t\treturn;\n\t\t}\n\n\t\tconst records = iterateByDelimiter(data, recordSep);\n\n\t\tlet count = 0;\n\t\tlet entry: T;\n\t\tlet fields: IterableIterator<string>;\n\n\t\tfor (const record of records) {\n\t\t\tif (!record.length) continue;\n\n\t\t\tcount++;\n\t\t\tentry = {} as unknown as T;\n\t\t\tfields = iterateByDelimiter(record, fieldSep);\n\n\t\t\tlet fieldCount = 0;\n\t\t\tlet field;\n\n\t\t\twhile (true) {\n\t\t\t\tfield = fields.next();\n\t\t\t\tif (field.done) break;\n\t\t\t\tif (fieldCount >= keys.length) continue; // Handle extra newlines at the end\n\n\t\t\t\tentry[keys[fieldCount++]] = field.value as T[keyof T];\n\t\t\t}\n\n\t\t\tyield entry;\n\t\t}\n\n\t\tsw?.stop({ suffix: ` parsed ${count} records` });\n\t}\n\n\treturn { arguments: args, separators: { record: recordSep, field: fieldSep }, parse: parse };\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,MAAM,aAAa;CAClB,SAAS;CACT,MAAM;CACN,YAAY;CACZ,kBAAkB;CAClB,UAAU;CACV,kBAAkB;CAClB,eAAe;CACf,aAAa;CACb,YAAY;CACZ,SAAS;CACT,cAAc,KAAA;CACd,gBAAgB,KAAA;CAChB,mBAAmB,KAAA;AACpB;AAEA,MAAM,iCAAiC;CACtC,GAAG;CACH,cAAc;CAEd,gBAAgB;CAChB,mBAAmB;AACpB;AAIA,IAAI;AACJ,IAAI;AAEJ,SAAgB,aAAa,mBAAyE;CACrG,IAAI,kBAAkB,SAAS,+BAA+B,GAAG;EAChE,2BAA2B,gBAAgB,8BAA8B;EACzE,OAAO;CACR;CAEA,eAAe,gBAAgB,UAAU;CACzC,OAAO;AACR;AAEA,MAAM,YAAY;AAClB,MAAM,kBAAkB;AACxB,MAAM,WAAW;AACjB,MAAM,iBAAiB;AAEvB,SAAS,gBACR,SACY;CACZ,IAAI,SAAS;CACb,MAAM,OAAwC,CAAC;CAC/C,KAAK,MAAM,OAAO,SAAS;EAE1B,IAAI,CADU,QAAQ,MACV;EAEZ,KAAK,KAAK,GAAG;EACb,UAAU,GAAG,QAAQ,OAAO;CAC7B;CAEA,MAAM,OAAO,CAAC,YAAY,QAAQ;CAElC,UAAU,MAAM,MAA2D;;;GAC1E,MAAM,KAAA,YAAA,EAAK,eAAe,uBAAuB,EAAE,KAAK;IAAE,UAAU;IAAM,OAAO;GAAQ,EAAE,CAAC,CAAA;GAE5F,IAAI,CAAC,MAAM;IACV,IAAI,KAAK,EAAE,QAAQ,WAAW,CAAC;IAC/B;GACD;GAEA,MAAM,UAAU,mBAAmB,MAAM,SAAS;GAElD,IAAI,QAAQ;GACZ,IAAI;GACJ,IAAI;GAEJ,KAAK,MAAM,UAAU,SAAS;IAC7B,IAAI,CAAC,OAAO,QAAQ;IAEpB;IACA,QAAQ,CAAC;IACT,SAAS,mBAAmB,QAAQ,QAAQ;IAE5C,IAAI,aAAa;IACjB,IAAI;IAEJ,OAAO,MAAM;KACZ,QAAQ,OAAO,KAAK;KACpB,IAAI,MAAM,MAAM;KAChB,IAAI,cAAc,KAAK,QAAQ;KAE/B,MAAM,KAAK,iBAAiB,MAAM;IACnC;IAEA,MAAM;GACP;GAEA,IAAI,KAAK,EAAE,QAAQ,WAAW,MAAM,UAAU,CAAC;;;;;;CAChD;CAEA,OAAO;EAAE,WAAW;EAAM,YAAY;GAAE,QAAQ;GAAW,OAAO;EAAS;EAAU;CAAM;AAC5F"}
@@ -4,11 +4,23 @@ import { CliGitProviderInternal } from "../cliGitProvider.js";
4
4
  /**
5
5
  * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain
6
6
  * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.
7
+ *
8
+ * Git reports whether an upstream is a local branch on every version, and a local upstream never has a
9
+ * remote, so it is never split (`release/2.0` is not remote `release`).
10
+ *
11
+ * A reference that carries neither `local` nor `remote` on its upstream (one a host built itself) is looked
12
+ * up fresh in the branch list, since guessing from the name can't tell a local `release/2.0` from remote
13
+ * `release`'s `2.0`.
14
+ *
15
+ * A remote a local branch's reference carries may have been removed since the reference was read, and
16
+ * git would then take the stale name as a path, so it must be one of the repository's remotes. A failed
17
+ * remote read (`getRemotes` answers `[]`) refuses too: a retry beats a push to a path.
7
18
  */
8
- export declare function resolveBranchRemote(provider: Pick<CliGitProviderInternal, 'remotes'>, repoPath: string, ref: GitBranchReference, cancellation?: AbortSignal): Promise<{
19
+ export declare function resolveBranchRemote(provider: Pick<CliGitProviderInternal, 'remotes' | 'branches'>, repoPath: string, ref: GitBranchReference, cancellation?: AbortSignal): Promise<{
9
20
  name: string;
10
21
  remote: string | undefined;
11
22
  upstream: string | undefined;
23
+ local: boolean;
12
24
  }>;
13
25
  //#endregion
14
26
  //# sourceMappingURL=branchRemote.d.ts.map
@@ -3,31 +3,60 @@ import { getBranchNameAndRemote, getBranchTrackingWithoutRemote, splitRemoteBran
3
3
  /**
4
4
  * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain
5
5
  * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.
6
+ *
7
+ * Git reports whether an upstream is a local branch on every version, and a local upstream never has a
8
+ * remote, so it is never split (`release/2.0` is not remote `release`).
9
+ *
10
+ * A reference that carries neither `local` nor `remote` on its upstream (one a host built itself) is looked
11
+ * up fresh in the branch list, since guessing from the name can't tell a local `release/2.0` from remote
12
+ * `release`'s `2.0`.
13
+ *
14
+ * A remote a local branch's reference carries may have been removed since the reference was read, and
15
+ * git would then take the stale name as a path, so it must be one of the repository's remotes. A failed
16
+ * remote read (`getRemotes` answers `[]`) refuses too: a retry beats a push to a path.
6
17
  */
7
18
  async function resolveBranchRemote(provider, repoPath, ref, cancellation) {
19
+ if (!ref.remote && ref.upstream != null && !ref.upstream.local && ref.upstream.remote == null) {
20
+ const fresh = await provider.branches.getBranch(repoPath, ref.name, void 0, cancellation);
21
+ if (fresh != null && !fresh.remote && (fresh.upstream != null || !ref.upstream.missing)) ref = fresh;
22
+ }
8
23
  let [name, remote] = getBranchNameAndRemote(ref);
9
24
  let upstream = getBranchTrackingWithoutRemote(ref);
10
- if (ref.upstream?.remote || !(ref.remote ? name : upstream)?.includes("/")) return {
25
+ const local = !ref.remote && ref.upstream?.local === true;
26
+ if (local) return {
27
+ name,
28
+ remote: void 0,
29
+ upstream,
30
+ local: true
31
+ };
32
+ const needsSplit = !ref.upstream?.remote && (ref.remote ? name : upstream)?.includes("/");
33
+ const needsVerify = !ref.remote && remote != null;
34
+ if (!needsSplit && !needsVerify) return {
11
35
  name,
12
36
  remote,
13
- upstream
37
+ upstream,
38
+ local
14
39
  };
15
40
  const remoteNames = (await provider.remotes.getRemotes(repoPath, void 0, cancellation)).map((r) => r.name);
16
- if (ref.remote) {
17
- const split = splitRemoteBranchName(ref.name, remoteNames);
18
- if (split != null) [remote, name] = split;
19
- }
20
- if (ref.upstream != null) {
21
- const split = splitRemoteBranchName(ref.upstream.name, remoteNames);
22
- if (split != null) {
23
- if (!ref.remote) remote = split[0];
24
- upstream = split[1];
41
+ if (needsSplit) {
42
+ if (ref.remote) {
43
+ const split = splitRemoteBranchName(ref.name, remoteNames);
44
+ if (split != null) [remote, name] = split;
45
+ }
46
+ if (ref.upstream != null) {
47
+ const split = splitRemoteBranchName(ref.upstream.name, remoteNames);
48
+ if (split != null) {
49
+ if (!ref.remote) remote = split[0];
50
+ upstream = split[1];
51
+ }
25
52
  }
26
53
  }
54
+ if (!ref.remote && remote != null && !remoteNames.includes(remote)) remote = void 0;
27
55
  return {
28
56
  name,
29
57
  remote,
30
- upstream
58
+ upstream,
59
+ local
31
60
  };
32
61
  }
33
62
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"branchRemote.js","names":[],"sources":["../../../../git-cli/src/providers/branchRemote.ts"],"sourcesContent":["import type { GitBranchReference } from '@gitlens/git/models/reference.js';\nimport {\n\tgetBranchNameAndRemote,\n\tgetBranchTrackingWithoutRemote,\n\tsplitRemoteBranchName,\n} from '@gitlens/git/utils/branch.utils.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\n\n/**\n * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain\n * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.\n */\nexport async function resolveBranchRemote(\n\tprovider: Pick<CliGitProviderInternal, 'remotes'>,\n\trepoPath: string,\n\tref: GitBranchReference,\n\tcancellation?: AbortSignal,\n): Promise<{ name: string; remote: string | undefined; upstream: string | undefined }> {\n\tlet [name, remote] = getBranchNameAndRemote(ref);\n\tlet upstream = getBranchTrackingWithoutRemote(ref);\n\n\tif (ref.upstream?.remote || !(ref.remote ? name : upstream)?.includes('/')) {\n\t\treturn { name: name, remote: remote, upstream: upstream };\n\t}\n\n\tconst remoteNames = (await provider.remotes.getRemotes(repoPath, undefined, cancellation)).map(r => r.name);\n\n\tif (ref.remote) {\n\t\tconst split = splitRemoteBranchName(ref.name, remoteNames);\n\t\tif (split != null) {\n\t\t\t[remote, name] = split;\n\t\t}\n\t}\n\n\tif (ref.upstream != null) {\n\t\tconst split = splitRemoteBranchName(ref.upstream.name, remoteNames);\n\t\tif (split != null) {\n\t\t\tif (!ref.remote) {\n\t\t\t\tremote = split[0];\n\t\t\t}\n\t\t\tupstream = split[1];\n\t\t}\n\t}\n\n\treturn { name: name, remote: remote, upstream: upstream };\n}\n"],"mappings":";;;;;;AAYA,eAAsB,oBACrB,UACA,UACA,KACA,cACsF;CACtF,IAAI,CAAC,MAAM,UAAU,uBAAuB,GAAG;CAC/C,IAAI,WAAW,+BAA+B,GAAG;CAEjD,IAAI,IAAI,UAAU,UAAU,EAAE,IAAI,SAAS,OAAO,SAAA,EAAW,SAAS,GAAG,GACxE,OAAO;EAAQ;EAAc;EAAkB;CAAS;CAGzD,MAAM,eAAe,MAAM,SAAS,QAAQ,WAAW,UAAU,KAAA,GAAW,YAAY,EAAA,CAAG,KAAI,MAAK,EAAE,IAAI;CAE1G,IAAI,IAAI,QAAQ;EACf,MAAM,QAAQ,sBAAsB,IAAI,MAAM,WAAW;EACzD,IAAI,SAAS,MACZ,CAAC,QAAQ,QAAQ;CAEnB;CAEA,IAAI,IAAI,YAAY,MAAM;EACzB,MAAM,QAAQ,sBAAsB,IAAI,SAAS,MAAM,WAAW;EAClE,IAAI,SAAS,MAAM;GAClB,IAAI,CAAC,IAAI,QACR,SAAS,MAAM;GAEhB,WAAW,MAAM;EAClB;CACD;CAEA,OAAO;EAAQ;EAAc;EAAkB;CAAS;AACzD"}
1
+ {"version":3,"file":"branchRemote.js","names":[],"sources":["../../../../git-cli/src/providers/branchRemote.ts"],"sourcesContent":["import type { GitBranchReference } from '@gitlens/git/models/reference.js';\nimport {\n\tgetBranchNameAndRemote,\n\tgetBranchTrackingWithoutRemote,\n\tsplitRemoteBranchName,\n} from '@gitlens/git/utils/branch.utils.js';\nimport type { CliGitProviderInternal } from '../cliGitProvider.js';\n\n/**\n * Splits a branch reference into its name, remote and upstream branch. A remote's own name can contain\n * a `/`, so the cheap first-`/` split only stands when it leaves no further `/` to be ambiguous.\n *\n * Git reports whether an upstream is a local branch on every version, and a local upstream never has a\n * remote, so it is never split (`release/2.0` is not remote `release`).\n *\n * A reference that carries neither `local` nor `remote` on its upstream (one a host built itself) is looked\n * up fresh in the branch list, since guessing from the name can't tell a local `release/2.0` from remote\n * `release`'s `2.0`.\n *\n * A remote a local branch's reference carries may have been removed since the reference was read, and\n * git would then take the stale name as a path, so it must be one of the repository's remotes. A failed\n * remote read (`getRemotes` answers `[]`) refuses too: a retry beats a push to a path.\n */\nexport async function resolveBranchRemote(\n\tprovider: Pick<CliGitProviderInternal, 'remotes' | 'branches'>,\n\trepoPath: string,\n\tref: GitBranchReference,\n\tcancellation?: AbortSignal,\n): Promise<{ name: string; remote: string | undefined; upstream: string | undefined; local: boolean }> {\n\tif (!ref.remote && ref.upstream != null && !ref.upstream.local && ref.upstream.remote == null) {\n\t\tconst fresh = await provider.branches.getBranch(repoPath, ref.name, undefined, cancellation);\n\t\t// `getBranch` matches by short name, which a remote-tracking ref can share. A missing upstream is one the\n\t\t// caller is about to set, which the branch doesn't track yet\n\t\tif (fresh != null && !fresh.remote && (fresh.upstream != null || !ref.upstream.missing)) {\n\t\t\tref = fresh;\n\t\t}\n\t}\n\n\tlet [name, remote] = getBranchNameAndRemote(ref);\n\tlet upstream = getBranchTrackingWithoutRemote(ref);\n\n\tconst local = !ref.remote && ref.upstream?.local === true;\n\tif (local) return { name: name, remote: undefined, upstream: upstream, local: true };\n\n\tconst needsSplit = !ref.upstream?.remote && (ref.remote ? name : upstream)?.includes('/');\n\tconst needsVerify = !ref.remote && remote != null;\n\tif (!needsSplit && !needsVerify) return { name: name, remote: remote, upstream: upstream, local: local };\n\n\tconst remoteNames = (await provider.remotes.getRemotes(repoPath, undefined, cancellation)).map(r => r.name);\n\n\tif (needsSplit) {\n\t\tif (ref.remote) {\n\t\t\tconst split = splitRemoteBranchName(ref.name, remoteNames);\n\t\t\tif (split != null) {\n\t\t\t\t[remote, name] = split;\n\t\t\t}\n\t\t}\n\n\t\tif (ref.upstream != null) {\n\t\t\tconst split = splitRemoteBranchName(ref.upstream.name, remoteNames);\n\t\t\tif (split != null) {\n\t\t\t\tif (!ref.remote) {\n\t\t\t\t\tremote = split[0];\n\t\t\t\t}\n\t\t\t\tupstream = split[1];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (!ref.remote && remote != null && !remoteNames.includes(remote)) {\n\t\tremote = undefined;\n\t}\n\n\treturn { name: name, remote: remote, upstream: upstream, local: local };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAuBA,eAAsB,oBACrB,UACA,UACA,KACA,cACsG;CACtG,IAAI,CAAC,IAAI,UAAU,IAAI,YAAY,QAAQ,CAAC,IAAI,SAAS,SAAS,IAAI,SAAS,UAAU,MAAM;EAC9F,MAAM,QAAQ,MAAM,SAAS,SAAS,UAAU,UAAU,IAAI,MAAM,KAAA,GAAW,YAAY;EAG3F,IAAI,SAAS,QAAQ,CAAC,MAAM,WAAW,MAAM,YAAY,QAAQ,CAAC,IAAI,SAAS,UAC9E,MAAM;CAER;CAEA,IAAI,CAAC,MAAM,UAAU,uBAAuB,GAAG;CAC/C,IAAI,WAAW,+BAA+B,GAAG;CAEjD,MAAM,QAAQ,CAAC,IAAI,UAAU,IAAI,UAAU,UAAU;CACrD,IAAI,OAAO,OAAO;EAAQ;EAAM,QAAQ,KAAA;EAAqB;EAAU,OAAO;CAAK;CAEnF,MAAM,aAAa,CAAC,IAAI,UAAU,WAAW,IAAI,SAAS,OAAO,SAAA,EAAW,SAAS,GAAG;CACxF,MAAM,cAAc,CAAC,IAAI,UAAU,UAAU;CAC7C,IAAI,CAAC,cAAc,CAAC,aAAa,OAAO;EAAQ;EAAc;EAAkB;EAAiB;CAAM;CAEvG,MAAM,eAAe,MAAM,SAAS,QAAQ,WAAW,UAAU,KAAA,GAAW,YAAY,EAAA,CAAG,KAAI,MAAK,EAAE,IAAI;CAE1G,IAAI,YAAY;EACf,IAAI,IAAI,QAAQ;GACf,MAAM,QAAQ,sBAAsB,IAAI,MAAM,WAAW;GACzD,IAAI,SAAS,MACZ,CAAC,QAAQ,QAAQ;EAEnB;EAEA,IAAI,IAAI,YAAY,MAAM;GACzB,MAAM,QAAQ,sBAAsB,IAAI,SAAS,MAAM,WAAW;GAClE,IAAI,SAAS,MAAM;IAClB,IAAI,CAAC,IAAI,QACR,SAAS,MAAM;IAEhB,WAAW,MAAM;GAClB;EACD;CACD;CAEA,IAAI,CAAC,IAAI,UAAU,UAAU,QAAQ,CAAC,YAAY,SAAS,MAAM,GAChE,SAAS,KAAA;CAGV,OAAO;EAAQ;EAAc;EAAkB;EAAiB;CAAM;AACvE"}