@gitkraken/core-gitlens 0.15.0 → 0.15.2

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 (77) hide show
  1. package/CHANGELOG.md +32 -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/utils/revision.utils.d.ts +3 -0
  17. package/dist/git/utils/revision.utils.js +5 -1
  18. package/dist/git/utils/revision.utils.js.map +1 -1
  19. package/dist/git/watching/worktreeSummary.d.ts +7 -4
  20. package/dist/git/watching/worktreeSummary.js +22 -3
  21. package/dist/git/watching/worktreeSummary.js.map +1 -1
  22. package/dist/git-cli/cliGitProvider.d.ts +6 -0
  23. package/dist/git-cli/cliGitProvider.js +48 -2
  24. package/dist/git-cli/cliGitProvider.js.map +1 -1
  25. package/dist/git-cli/exec/git.d.ts +1 -0
  26. package/dist/git-cli/exec/git.js +8 -10
  27. package/dist/git-cli/exec/git.js.map +1 -1
  28. package/dist/git-cli/exec/indexLock.d.ts +9 -1
  29. package/dist/git-cli/exec/indexLock.js +19 -5
  30. package/dist/git-cli/exec/indexLock.js.map +1 -1
  31. package/dist/git-cli/parsers/logParser.d.ts +4 -0
  32. package/dist/git-cli/parsers/logParser.js +12 -2
  33. package/dist/git-cli/parsers/logParser.js.map +1 -1
  34. package/dist/git-cli/parsers/refParser.js +4 -2
  35. package/dist/git-cli/parsers/refParser.js.map +1 -1
  36. package/dist/git-cli/providers/branchRemote.d.ts +13 -1
  37. package/dist/git-cli/providers/branchRemote.js +41 -12
  38. package/dist/git-cli/providers/branchRemote.js.map +1 -1
  39. package/dist/git-cli/providers/branches.js +55 -21
  40. package/dist/git-cli/providers/branches.js.map +1 -1
  41. package/dist/git-cli/providers/graph.js +53 -32
  42. package/dist/git-cli/providers/graph.js.map +1 -1
  43. package/dist/git-cli/providers/operations.js +22 -4
  44. package/dist/git-cli/providers/operations.js.map +1 -1
  45. package/dist/git-cli/providers/revision.d.ts +8 -1
  46. package/dist/git-cli/providers/revision.js +22 -8
  47. package/dist/git-cli/providers/revision.js.map +1 -1
  48. package/dist/git-cli/providers/stash.js +3 -3
  49. package/dist/git-cli/providers/stash.js.map +1 -1
  50. package/dist/git-cli/providers/worktrees.js +3 -3
  51. package/dist/git-cli/providers/worktrees.js.map +1 -1
  52. package/dist/plus/git-github/api/github.d.ts +4 -0
  53. package/dist/plus/git-github/api/github.js +31 -4
  54. package/dist/plus/git-github/api/github.js.map +1 -1
  55. package/dist/plus/git-github/api/types.d.ts +4 -0
  56. package/dist/plus/git-github/providers/github/revision.js +12 -1
  57. package/dist/plus/git-github/providers/github/revision.js.map +1 -1
  58. package/dist/plus/integrations/collectionMetadata.d.ts +22 -7
  59. package/dist/plus/integrations/collectionMetadata.js +49 -6
  60. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  61. package/dist/plus/integrations/providers/providersApi.js +5 -4
  62. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  63. package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +4 -21
  64. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +13 -48
  65. package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
  66. package/dist/plus/integrations/providers/utils/providerPaging.d.ts +20 -6
  67. package/dist/plus/integrations/providers/utils/providerPaging.js +50 -13
  68. package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
  69. package/dist/plus/integrations/reads/issues.js +1 -1
  70. package/dist/plus/integrations/reads/issues.js.map +1 -1
  71. package/dist/plus/integrations/reads/paging.js +1 -1
  72. package/dist/plus/integrations/reads/paging.js.map +1 -1
  73. package/dist/plus/integrations/results.d.ts +12 -7
  74. package/dist/plus/integrations/results.js +3 -4
  75. package/dist/plus/integrations/results.js.map +1 -1
  76. package/docs/integrations.md +9 -5
  77. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"reference.utils.js","names":[],"sources":["../../../../git/src/utils/reference.utils.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport { getBranchNameWithoutRemote, getRemoteNameFromBranchName } from '@gitlens/utils/gitRefs.js';\nimport type {\n\tGitBranchReference,\n\tGitReference,\n\tGitRevisionReference,\n\tGitStashReference,\n\tGitTagReference,\n} from '../models/reference.js';\nimport { isRevisionRange, isShaWithParentSuffix, shortenRevision } from './revision.utils.js';\n\ninterface GitBranchReferenceOptions {\n\trefType: 'branch';\n\tid?: string;\n\tname: string;\n\tremote: boolean;\n\tsha?: string;\n\tupstream?: { name: string; missing: boolean; remote?: string };\n\tworktree?: { path: string; isDefault: boolean } | boolean;\n}\n\ninterface GitCommitReferenceOptions {\n\trefType: 'revision';\n\tname?: string;\n\tmessage?: string;\n}\n\ninterface GitStashReferenceOptions {\n\trefType: 'stash';\n\tname: string;\n\tnumber: string | undefined;\n\tmessage?: string;\n\tstashOnRef?: string;\n}\n\ninterface GitTagReferenceOptions {\n\trefType: 'tag';\n\tid?: string;\n\tname: string;\n\tsha?: string;\n}\n\nexport function createReference(ref: string, repoPath: string, options: GitBranchReferenceOptions): GitBranchReference;\nexport function createReference(ref: string, repoPath: string, options: GitStashReferenceOptions): GitStashReference;\nexport function createReference(ref: string, repoPath: string, options: GitTagReferenceOptions): GitTagReference;\nexport function createReference(\n\tref: string,\n\trepoPath: string,\n\toptions?: GitCommitReferenceOptions,\n): GitRevisionReference;\nexport function createReference(\n\tref: string,\n\trepoPath: string,\n\toptions:\n\t\t| GitBranchReferenceOptions\n\t\t| GitStashReferenceOptions\n\t\t| GitTagReferenceOptions\n\t\t| GitCommitReferenceOptions = { refType: 'revision' },\n): GitReference {\n\tswitch (options.refType) {\n\t\tcase 'branch':\n\t\t\treturn {\n\t\t\t\trefType: 'branch',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tname: options.name,\n\t\t\t\tid: options.id,\n\t\t\t\tremote: options.remote,\n\t\t\t\tsha: options.sha,\n\t\t\t\tupstream: options.upstream,\n\t\t\t\tworktree: options.worktree,\n\t\t\t};\n\t\tcase 'stash':\n\t\t\treturn {\n\t\t\t\trefType: 'stash',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tsha: ref,\n\t\t\t\tname: options.name,\n\t\t\t\tstashNumber: options.number,\n\t\t\t\tmessage: options.message,\n\t\t\t\tstashOnRef: options.stashOnRef,\n\t\t\t};\n\t\tcase 'tag':\n\t\t\treturn {\n\t\t\t\trefType: 'tag',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tname: options.name,\n\t\t\t\tid: options.id,\n\t\t\t\tsha: options.sha,\n\t\t\t};\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\trefType: 'revision',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tsha: ref,\n\t\t\t\tname:\n\t\t\t\t\toptions.name ??\n\t\t\t\t\tshortenRevision(ref, {\n\t\t\t\t\t\tstrings: {\n\t\t\t\t\t\t\tuncommitted: l10n.t('Working Tree'),\n\t\t\t\t\t\t\tuncommittedStaged: l10n.t('Index'),\n\t\t\t\t\t\t\tworking: l10n.t('Working Tree'),\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\tmessage: options.message,\n\t\t\t};\n\t}\n}\n\nexport function getReferenceNameWithoutRemote(ref: GitReference): string {\n\tif (ref.refType === 'branch') {\n\t\treturn ref.remote ? getBranchNameWithoutRemote(ref.name) : ref.name;\n\t}\n\treturn ref.name;\n}\n\nexport function getReferenceTypeLabel(ref: GitReference | undefined): string {\n\tswitch (ref?.refType) {\n\t\tcase 'branch':\n\t\t\treturn l10n.t('Branch');\n\t\tcase 'stash':\n\t\t\treturn l10n.t('Stash');\n\t\tcase 'tag':\n\t\t\treturn l10n.t('Tag');\n\t\tdefault:\n\t\t\treturn l10n.t('Commit');\n\t}\n}\n\nexport function isBranchReference(ref: GitReference | undefined): ref is GitBranchReference {\n\treturn ref?.refType === 'branch';\n}\n\nexport function isGitReference(ref: unknown): ref is GitReference {\n\tif (ref == null || typeof ref !== 'object') return false;\n\n\tconst r = ref as GitReference;\n\treturn (\n\t\ttypeof r.refType === 'string' &&\n\t\ttypeof r.repoPath === 'string' &&\n\t\ttypeof r.ref === 'string' &&\n\t\ttypeof r.name === 'string'\n\t);\n}\n\nexport function isRevisionReference(ref: GitReference | undefined): ref is GitRevisionReference {\n\treturn ref?.refType === 'revision';\n}\n\nexport function isRevisionRangeReference(ref: GitReference | undefined): ref is GitRevisionReference {\n\treturn ref?.refType === 'revision' && isRevisionRange(ref.ref);\n}\n\nexport function isStashReference(ref: GitReference | undefined): ref is GitStashReference {\n\treturn ref?.refType === 'stash' || (ref?.refType === 'revision' && Boolean('stashName' in ref && ref.stashName));\n}\n\nexport function isTagReference(ref: GitReference | undefined): ref is GitTagReference {\n\treturn ref?.refType === 'tag';\n}\n\nexport function getReferenceLabel(\n\trefs: GitReference | GitReference[] | undefined,\n\toptions?: { capitalize?: boolean; expand?: boolean; icon?: boolean; label?: boolean; quoted?: boolean } | false,\n): string {\n\tif (refs == null) return '';\n\n\toptions =\n\t\toptions === false\n\t\t\t? {}\n\t\t\t: { expand: true, icon: true, label: options?.label ?? options?.expand ?? true, ...options };\n\n\tlet result;\n\tif (!Array.isArray(refs) || refs.length === 1) {\n\t\tconst ref = Array.isArray(refs) ? refs[0] : refs;\n\t\tlet refName = options?.quoted ? `'${ref.name}'` : ref.name;\n\t\tswitch (ref.refType) {\n\t\t\tcase 'branch': {\n\t\t\t\tif (ref.remote) {\n\t\t\t\t\trefName = `${getRemoteNameFromBranchName(refName)}: ${getBranchNameWithoutRemote(refName)}`;\n\t\t\t\t\trefName = options?.quoted ? `'${refName}'` : refName;\n\t\t\t\t}\n\n\t\t\t\tif (options.label) {\n\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\tif (options.capitalize && options.expand) {\n\t\t\t\t\t\t\tresult = ref.remote\n\t\t\t\t\t\t\t\t? l10n.t('Remote Branch $(git-branch)\\u00a0{0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('Branch $(git-branch)\\u00a0{0}', refName);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult = ref.remote\n\t\t\t\t\t\t\t\t? l10n.t('remote branch $(git-branch)\\u00a0{0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('branch $(git-branch)\\u00a0{0}', refName);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (options.capitalize && options.expand) {\n\t\t\t\t\t\tresult = ref.remote ? l10n.t('Remote Branch {0}', refName) : l10n.t('Branch {0}', refName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = ref.remote ? l10n.t('remote branch {0}', refName) : l10n.t('branch {0}', refName);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult = options.icon ? `$(git-branch)\\u00a0${refName}` : refName;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'tag':\n\t\t\t\tif (options.label) {\n\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t? l10n.t('Tag $(tag)\\u00a0{0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('tag $(tag)\\u00a0{0}', refName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t? l10n.t('Tag {0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('tag {0}', refName);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult = options.icon ? `$(tag)\\u00a0${refName}` : refName;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tif (isStashReference(ref)) {\n\t\t\t\t\tlet message;\n\t\t\t\t\tif (options.expand && ref.message) {\n\t\t\t\t\t\tmessage = `${ref.stashNumber != null ? `#${ref.stashNumber}: ` : ''}${\n\t\t\t\t\t\t\tref.message.length > 20 ? `${ref.message.substring(0, 20).trimEnd()}\\u2026` : ref.message\n\t\t\t\t\t\t}`;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst stashName = message ?? (ref.stashNumber ? `#${ref.stashNumber}` : ref.name);\n\t\t\t\t\tif (options.label) {\n\t\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t? l10n.t('Stash $(archive)\\u00a0{0}', message ?? ref.name)\n\t\t\t\t\t\t\t\t\t: l10n.t('stash $(archive)\\u00a0{0}', message ?? ref.name);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t? l10n.t('Stash {0}', stashName)\n\t\t\t\t\t\t\t\t\t: l10n.t('stash {0}', stashName);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = options.icon ? `$(archive)\\u00a0${message ?? ref.name}` : stashName;\n\t\t\t\t\t}\n\t\t\t\t} else if (isRevisionRange(ref.ref)) {\n\t\t\t\t\tresult = refName;\n\t\t\t\t} else {\n\t\t\t\t\tlet message;\n\t\t\t\t\tif (options.expand && ref.message) {\n\t\t\t\t\t\tmessage =\n\t\t\t\t\t\t\tref.message.length > 20\n\t\t\t\t\t\t\t\t? ` (${ref.message.substring(0, 20).trimEnd()}\\u2026)`\n\t\t\t\t\t\t\t\t: ` (${ref.message})`;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet before = false;\n\t\t\t\t\tif (options.expand && options.label && isShaWithParentSuffix(ref.ref)) {\n\t\t\t\t\t\trefName = ref.name.endsWith('^') ? ref.name.substring(0, ref.name.length - 1) : ref.name;\n\t\t\t\t\t\tif (options?.quoted) {\n\t\t\t\t\t\t\trefName = `'${refName}'`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbefore = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (options.label) {\n\t\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\t\tif (before) {\n\t\t\t\t\t\t\t\tresult = options.capitalize\n\t\t\t\t\t\t\t\t\t? l10n.t('Before commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t\t: l10n.t('before commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '');\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t\t? l10n.t('Commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t\t\t: l10n.t('commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (before) {\n\t\t\t\t\t\t\tresult = options.capitalize\n\t\t\t\t\t\t\t\t? l10n.t('Before commit {0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t: l10n.t('before commit {0}{1}', refName, message ?? '');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t? l10n.t('Commit {0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t\t: l10n.t('commit {0}{1}', refName, message ?? '');\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = options.icon\n\t\t\t\t\t\t\t? `$(git-commit)\\u00a0${refName}${message ?? ''}`\n\t\t\t\t\t\t\t: `${refName}${message ?? ''}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tconst expanded = options.expand ? ` (${refs.map(r => r.name).join(', ')})` : '';\n\tswitch (refs[0].refType) {\n\t\tcase 'branch':\n\t\t\treturn l10n.t('{0} branches{1}', refs.length, expanded);\n\t\tcase 'tag':\n\t\t\treturn l10n.t('{0} tags{1}', refs.length, expanded);\n\t\tdefault:\n\t\t\treturn isStashReference(refs[0])\n\t\t\t\t? l10n.t('{0} stashes{1}', refs.length, expanded)\n\t\t\t\t: l10n.t('{0} commits{1}', refs.length, expanded);\n\t}\n}\n\nexport function getReferenceTypeIcon(ref: GitReference | undefined, webview?: boolean): string {\n\tswitch (ref?.refType) {\n\t\tcase 'branch':\n\t\t\tif (ref.remote) return 'cloud';\n\t\t\tif (ref.worktree) return webview ? 'gl-worktree' : 'gitlens-worktree';\n\t\t\treturn 'git-branch';\n\t\tcase 'tag':\n\t\t\treturn 'tag';\n\t\tcase 'stash':\n\t\t\treturn 'archive';\n\t\tdefault:\n\t\t\treturn 'git-commit';\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"reference.utils.js","names":[],"sources":["../../../../git/src/utils/reference.utils.ts"],"sourcesContent":["import * as l10n from '@vscode/l10n';\nimport { getBranchNameWithoutRemote, getRemoteNameFromBranchName } from '@gitlens/utils/gitRefs.js';\nimport type {\n\tGitBranchReference,\n\tGitReference,\n\tGitRevisionReference,\n\tGitStashReference,\n\tGitTagReference,\n} from '../models/reference.js';\nimport { isRevisionRange, isShaWithParentSuffix, shortenRevision } from './revision.utils.js';\n\ninterface GitBranchReferenceOptions {\n\trefType: 'branch';\n\tid?: string;\n\tname: string;\n\tremote: boolean;\n\tsha?: string;\n\tupstream?: { name: string; missing: boolean; remote?: string; remoteBranch?: string; local?: boolean };\n\tworktree?: { path: string; isDefault: boolean } | boolean;\n}\n\ninterface GitCommitReferenceOptions {\n\trefType: 'revision';\n\tname?: string;\n\tmessage?: string;\n}\n\ninterface GitStashReferenceOptions {\n\trefType: 'stash';\n\tname: string;\n\tnumber: string | undefined;\n\tmessage?: string;\n\tstashOnRef?: string;\n}\n\ninterface GitTagReferenceOptions {\n\trefType: 'tag';\n\tid?: string;\n\tname: string;\n\tsha?: string;\n}\n\nexport function createReference(ref: string, repoPath: string, options: GitBranchReferenceOptions): GitBranchReference;\nexport function createReference(ref: string, repoPath: string, options: GitStashReferenceOptions): GitStashReference;\nexport function createReference(ref: string, repoPath: string, options: GitTagReferenceOptions): GitTagReference;\nexport function createReference(\n\tref: string,\n\trepoPath: string,\n\toptions?: GitCommitReferenceOptions,\n): GitRevisionReference;\nexport function createReference(\n\tref: string,\n\trepoPath: string,\n\toptions:\n\t\t| GitBranchReferenceOptions\n\t\t| GitStashReferenceOptions\n\t\t| GitTagReferenceOptions\n\t\t| GitCommitReferenceOptions = { refType: 'revision' },\n): GitReference {\n\tswitch (options.refType) {\n\t\tcase 'branch':\n\t\t\treturn {\n\t\t\t\trefType: 'branch',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tname: options.name,\n\t\t\t\tid: options.id,\n\t\t\t\tremote: options.remote,\n\t\t\t\tsha: options.sha,\n\t\t\t\tupstream: options.upstream,\n\t\t\t\tworktree: options.worktree,\n\t\t\t};\n\t\tcase 'stash':\n\t\t\treturn {\n\t\t\t\trefType: 'stash',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tsha: ref,\n\t\t\t\tname: options.name,\n\t\t\t\tstashNumber: options.number,\n\t\t\t\tmessage: options.message,\n\t\t\t\tstashOnRef: options.stashOnRef,\n\t\t\t};\n\t\tcase 'tag':\n\t\t\treturn {\n\t\t\t\trefType: 'tag',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tname: options.name,\n\t\t\t\tid: options.id,\n\t\t\t\tsha: options.sha,\n\t\t\t};\n\t\tdefault:\n\t\t\treturn {\n\t\t\t\trefType: 'revision',\n\t\t\t\trepoPath: repoPath,\n\t\t\t\tref: ref,\n\t\t\t\tsha: ref,\n\t\t\t\tname:\n\t\t\t\t\toptions.name ??\n\t\t\t\t\tshortenRevision(ref, {\n\t\t\t\t\t\tstrings: {\n\t\t\t\t\t\t\tuncommitted: l10n.t('Working Tree'),\n\t\t\t\t\t\t\tuncommittedStaged: l10n.t('Index'),\n\t\t\t\t\t\t\tworking: l10n.t('Working Tree'),\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t\tmessage: options.message,\n\t\t\t};\n\t}\n}\n\nexport function getReferenceNameWithoutRemote(ref: GitReference): string {\n\tif (ref.refType === 'branch') {\n\t\treturn ref.remote ? getBranchNameWithoutRemote(ref.name) : ref.name;\n\t}\n\treturn ref.name;\n}\n\nexport function getReferenceTypeLabel(ref: GitReference | undefined): string {\n\tswitch (ref?.refType) {\n\t\tcase 'branch':\n\t\t\treturn l10n.t('Branch');\n\t\tcase 'stash':\n\t\t\treturn l10n.t('Stash');\n\t\tcase 'tag':\n\t\t\treturn l10n.t('Tag');\n\t\tdefault:\n\t\t\treturn l10n.t('Commit');\n\t}\n}\n\nexport function isBranchReference(ref: GitReference | undefined): ref is GitBranchReference {\n\treturn ref?.refType === 'branch';\n}\n\nexport function isGitReference(ref: unknown): ref is GitReference {\n\tif (ref == null || typeof ref !== 'object') return false;\n\n\tconst r = ref as GitReference;\n\treturn (\n\t\ttypeof r.refType === 'string' &&\n\t\ttypeof r.repoPath === 'string' &&\n\t\ttypeof r.ref === 'string' &&\n\t\ttypeof r.name === 'string'\n\t);\n}\n\nexport function isRevisionReference(ref: GitReference | undefined): ref is GitRevisionReference {\n\treturn ref?.refType === 'revision';\n}\n\nexport function isRevisionRangeReference(ref: GitReference | undefined): ref is GitRevisionReference {\n\treturn ref?.refType === 'revision' && isRevisionRange(ref.ref);\n}\n\nexport function isStashReference(ref: GitReference | undefined): ref is GitStashReference {\n\treturn ref?.refType === 'stash' || (ref?.refType === 'revision' && Boolean('stashName' in ref && ref.stashName));\n}\n\nexport function isTagReference(ref: GitReference | undefined): ref is GitTagReference {\n\treturn ref?.refType === 'tag';\n}\n\nexport function getReferenceLabel(\n\trefs: GitReference | GitReference[] | undefined,\n\toptions?: { capitalize?: boolean; expand?: boolean; icon?: boolean; label?: boolean; quoted?: boolean } | false,\n): string {\n\tif (refs == null) return '';\n\n\toptions =\n\t\toptions === false\n\t\t\t? {}\n\t\t\t: { expand: true, icon: true, label: options?.label ?? options?.expand ?? true, ...options };\n\n\tlet result;\n\tif (!Array.isArray(refs) || refs.length === 1) {\n\t\tconst ref = Array.isArray(refs) ? refs[0] : refs;\n\t\tlet refName = options?.quoted ? `'${ref.name}'` : ref.name;\n\t\tswitch (ref.refType) {\n\t\t\tcase 'branch': {\n\t\t\t\tif (ref.remote) {\n\t\t\t\t\trefName = `${getRemoteNameFromBranchName(refName)}: ${getBranchNameWithoutRemote(refName)}`;\n\t\t\t\t\trefName = options?.quoted ? `'${refName}'` : refName;\n\t\t\t\t}\n\n\t\t\t\tif (options.label) {\n\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\tif (options.capitalize && options.expand) {\n\t\t\t\t\t\t\tresult = ref.remote\n\t\t\t\t\t\t\t\t? l10n.t('Remote Branch $(git-branch)\\u00a0{0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('Branch $(git-branch)\\u00a0{0}', refName);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult = ref.remote\n\t\t\t\t\t\t\t\t? l10n.t('remote branch $(git-branch)\\u00a0{0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('branch $(git-branch)\\u00a0{0}', refName);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (options.capitalize && options.expand) {\n\t\t\t\t\t\tresult = ref.remote ? l10n.t('Remote Branch {0}', refName) : l10n.t('Branch {0}', refName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = ref.remote ? l10n.t('remote branch {0}', refName) : l10n.t('branch {0}', refName);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult = options.icon ? `$(git-branch)\\u00a0${refName}` : refName;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'tag':\n\t\t\t\tif (options.label) {\n\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t? l10n.t('Tag $(tag)\\u00a0{0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('tag $(tag)\\u00a0{0}', refName);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t? l10n.t('Tag {0}', refName)\n\t\t\t\t\t\t\t\t: l10n.t('tag {0}', refName);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tresult = options.icon ? `$(tag)\\u00a0${refName}` : refName;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tif (isStashReference(ref)) {\n\t\t\t\t\tlet message;\n\t\t\t\t\tif (options.expand && ref.message) {\n\t\t\t\t\t\tmessage = `${ref.stashNumber != null ? `#${ref.stashNumber}: ` : ''}${\n\t\t\t\t\t\t\tref.message.length > 20 ? `${ref.message.substring(0, 20).trimEnd()}\\u2026` : ref.message\n\t\t\t\t\t\t}`;\n\t\t\t\t\t}\n\n\t\t\t\t\tconst stashName = message ?? (ref.stashNumber ? `#${ref.stashNumber}` : ref.name);\n\t\t\t\t\tif (options.label) {\n\t\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t? l10n.t('Stash $(archive)\\u00a0{0}', message ?? ref.name)\n\t\t\t\t\t\t\t\t\t: l10n.t('stash $(archive)\\u00a0{0}', message ?? ref.name);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t? l10n.t('Stash {0}', stashName)\n\t\t\t\t\t\t\t\t\t: l10n.t('stash {0}', stashName);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = options.icon ? `$(archive)\\u00a0${message ?? ref.name}` : stashName;\n\t\t\t\t\t}\n\t\t\t\t} else if (isRevisionRange(ref.ref)) {\n\t\t\t\t\tresult = refName;\n\t\t\t\t} else {\n\t\t\t\t\tlet message;\n\t\t\t\t\tif (options.expand && ref.message) {\n\t\t\t\t\t\tmessage =\n\t\t\t\t\t\t\tref.message.length > 20\n\t\t\t\t\t\t\t\t? ` (${ref.message.substring(0, 20).trimEnd()}\\u2026)`\n\t\t\t\t\t\t\t\t: ` (${ref.message})`;\n\t\t\t\t\t}\n\n\t\t\t\t\tlet before = false;\n\t\t\t\t\tif (options.expand && options.label && isShaWithParentSuffix(ref.ref)) {\n\t\t\t\t\t\trefName = ref.name.endsWith('^') ? ref.name.substring(0, ref.name.length - 1) : ref.name;\n\t\t\t\t\t\tif (options?.quoted) {\n\t\t\t\t\t\t\trefName = `'${refName}'`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbefore = true;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (options.label) {\n\t\t\t\t\t\tif (options.icon) {\n\t\t\t\t\t\t\tif (before) {\n\t\t\t\t\t\t\t\tresult = options.capitalize\n\t\t\t\t\t\t\t\t\t? l10n.t('Before commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t\t: l10n.t('before commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '');\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t\t? l10n.t('Commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t\t\t: l10n.t('commit $(git-commit)\\u00a0{0}{1}', refName, message ?? '');\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else if (before) {\n\t\t\t\t\t\t\tresult = options.capitalize\n\t\t\t\t\t\t\t\t? l10n.t('Before commit {0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t: l10n.t('before commit {0}{1}', refName, message ?? '');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\t\toptions.capitalize && options.expand\n\t\t\t\t\t\t\t\t\t? l10n.t('Commit {0}{1}', refName, message ?? '')\n\t\t\t\t\t\t\t\t\t: l10n.t('commit {0}{1}', refName, message ?? '');\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresult = options.icon\n\t\t\t\t\t\t\t? `$(git-commit)\\u00a0${refName}${message ?? ''}`\n\t\t\t\t\t\t\t: `${refName}${message ?? ''}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tconst expanded = options.expand ? ` (${refs.map(r => r.name).join(', ')})` : '';\n\tswitch (refs[0].refType) {\n\t\tcase 'branch':\n\t\t\treturn l10n.t('{0} branches{1}', refs.length, expanded);\n\t\tcase 'tag':\n\t\t\treturn l10n.t('{0} tags{1}', refs.length, expanded);\n\t\tdefault:\n\t\t\treturn isStashReference(refs[0])\n\t\t\t\t? l10n.t('{0} stashes{1}', refs.length, expanded)\n\t\t\t\t: l10n.t('{0} commits{1}', refs.length, expanded);\n\t}\n}\n\nexport function getReferenceTypeIcon(ref: GitReference | undefined, webview?: boolean): string {\n\tswitch (ref?.refType) {\n\t\tcase 'branch':\n\t\t\tif (ref.remote) return 'cloud';\n\t\t\tif (ref.worktree) return webview ? 'gl-worktree' : 'gitlens-worktree';\n\t\t\treturn 'git-branch';\n\t\tcase 'tag':\n\t\t\treturn 'tag';\n\t\tcase 'stash':\n\t\t\treturn 'archive';\n\t\tdefault:\n\t\t\treturn 'git-commit';\n\t}\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { GitRevisionRange, GitRevisionRangeNotation, uncommitted } from "../models/revision.js";
2
+ import { ResolvedRevision } from "../providers/revision.js";
2
3
  //#region ../git/src/utils/revision.utils.d.ts
3
4
  /** Checks if the rev looks like a revision with a navigation suffixes (like ^, @, ~, or :) */
4
5
  export declare function isRevisionWithSuffix(rev: string): boolean;
@@ -33,5 +34,7 @@ export declare function getRevisionRangeParts(revRange: GitRevisionRange): {
33
34
  } | undefined;
34
35
  export declare function isRevisionRange(rev: string | undefined, rangeType?: 'any' | 'qualified' | 'qualified-double-dot' | 'qualified-triple-dot'): rev is GitRevisionRange;
35
36
  export declare function stripOrigin<T extends string | GitRevisionRange | undefined>(ref: T): T;
37
+ /** Whether the file at `relativePath` doesn't exist at a resolved revision — nothing touched it yet, or its last change there deleted it or moved it away (a copy is reported instead of a rename when `diff.renames=copies`) */
38
+ export declare function isResolvedRevisionMissing(resolved: ResolvedRevision, relativePath: string): boolean;
36
39
  //#endregion
37
40
  //# sourceMappingURL=revision.utils.d.ts.map
@@ -94,7 +94,11 @@ function isRevisionRange(rev, rangeType = "any") {
94
94
  function stripOrigin(ref) {
95
95
  return ref?.replace(/(?:^|(?<=..))origin\//, "");
96
96
  }
97
+ /** Whether the file at `relativePath` doesn't exist at a resolved revision — nothing touched it yet, or its last change there deleted it or moved it away (a copy is reported instead of a rename when `diff.renames=copies`) */
98
+ function isResolvedRevisionMissing(resolved, relativePath) {
99
+ return resolved.sha === "0000000000000000000000000000000000000000-" || resolved.status === "D" || (resolved.status === "R" || resolved.status === "C") && resolved.path != null && resolved.path !== relativePath;
100
+ }
97
101
  //#endregion
98
- export { createRevisionRange, getAbbreviatedShaLength, getRevisionRangeParts, isRevisionRange, isRevisionWithSuffix, isSha, isShaWithOptionalRevisionSuffix, isShaWithParentSuffix, isUncommitted, isUncommittedStaged, isUncommittedStagedWithParentSuffix, isUncommittedWithParentSuffix, resetAbbreviatedShaLength, setAbbreviatedShaLength, shortenRevision, stripOrigin };
102
+ export { createRevisionRange, getAbbreviatedShaLength, getRevisionRangeParts, isResolvedRevisionMissing, isRevisionRange, isRevisionWithSuffix, isSha, isShaWithOptionalRevisionSuffix, isShaWithParentSuffix, isUncommitted, isUncommittedStaged, isUncommittedStagedWithParentSuffix, isUncommittedWithParentSuffix, resetAbbreviatedShaLength, setAbbreviatedShaLength, shortenRevision, stripOrigin };
99
103
 
100
104
  //# sourceMappingURL=revision.utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"revision.utils.js","names":[],"sources":["../../../../git/src/utils/revision.utils.ts"],"sourcesContent":["import type { GitRevisionRange, GitRevisionRangeNotation } from '../models/revision.js';\nimport { deletedOrMissing, uncommitted, uncommittedStaged } from '../models/revision.js';\n\nconst rangeRegex = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)?(\\.\\.\\.?)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)?$/;\nconst qualifiedRangeRegex = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)(\\.\\.\\.?)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)$/;\nconst qualifiedDoubleDotRange = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)(\\.\\.)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)$/;\nconst qualifiedTripleDotRange = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)(\\.\\.\\.)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)$/;\nconst shaWithOptionalRevisionSuffixRegex = /(^[0-9a-f]{40}([\\^@~:]\\S*)?$)|(^[0]{40}(:|-)$)/;\nconst revisionWithSuffixRegex = /^\\S+[\\^@~:]\\S*$/;\nconst shaRegex = /(^[0-9a-f]{40}$)|(^[0]{40}(:|-)$)/;\nconst shaShortRegex = /(^[0-9a-f]{7,40}$)|(^[0]{40}(:|-)$)/;\nconst shaParentRegex = /(^[0-9a-f]{40})\\^[0-3]?$/;\nconst shaShortenRegex = /^(.*?)([\\^@~:].*)?$/;\nconst uncommittedRegex = /^[0]{40}(?:[\\^@~:]\\S*)?:?$/;\nconst uncommittedStagedRegex = /^[0]{40}([\\^@~]\\S*)?:$/;\n\nfunction isMatch(regex: RegExp, rev: string | undefined) {\n\treturn !rev ? false : regex.test(rev);\n}\n\n/** Checks if the rev looks like a revision with a navigation suffixes (like ^, @, ~, or :) */\nexport function isRevisionWithSuffix(rev: string): boolean {\n\treturn isMatch(revisionWithSuffixRegex, rev) && rev.length > 40;\n}\n\n/** Checks if the rev looks like a SHA-1 hash\n * @param allowShort If true, allows short SHAs (7-40 characters)\n */\nexport function isSha(rev: string, allowShort: boolean = false): boolean {\n\treturn isMatch(allowShort ? shaShortRegex : shaRegex, rev);\n}\n\n/** Checks if the rev looks like a SHA-1 hash with an optional revision navigation suffixes (like ^, @, ~, or :) */\nexport function isShaWithOptionalRevisionSuffix(rev: string): boolean {\n\treturn isMatch(shaWithOptionalRevisionSuffixRegex, rev);\n}\n\n/** Checks if the rev looks like a SHA-1 hash with a ^ parent suffix */\nexport function isShaWithParentSuffix(rev: string): boolean {\n\treturn isMatch(shaParentRegex, rev);\n}\n\nexport function isUncommitted(rev: string | undefined, exact: boolean = false): boolean {\n\treturn rev === uncommitted || rev === uncommittedStaged || (!exact && isMatch(uncommittedRegex, rev));\n}\n\nexport function isUncommittedStaged(rev: string | undefined, exact: boolean = false): boolean {\n\treturn rev === uncommittedStaged || (!exact && isMatch(uncommittedStagedRegex, rev));\n}\n\nexport function isUncommittedWithParentSuffix(rev: string | undefined): rev is `${typeof uncommitted}${'^' | ':^'}` {\n\treturn rev === `${uncommitted}^` || rev === `${uncommittedStaged}^`;\n}\n\nexport function isUncommittedStagedWithParentSuffix(\n\trev: string | undefined,\n): rev is `${typeof uncommitted}${'^' | ':^'}` {\n\treturn rev === `${uncommittedStaged}^`;\n}\n\nlet abbreviatedShaLength = 7;\nexport function getAbbreviatedShaLength(): number {\n\treturn abbreviatedShaLength;\n}\n\nexport function setAbbreviatedShaLength(length: number): void {\n\tabbreviatedShaLength = length;\n}\n\n/** Resets the abbreviated SHA length to the default (7). For test isolation. */\nexport function resetAbbreviatedShaLength(): void {\n\tabbreviatedShaLength = 7;\n}\n\nexport function shortenRevision(\n\trev: string | undefined,\n\toptions?: {\n\t\tstrings?: { uncommitted?: string; uncommittedStaged?: string; working?: string };\n\t},\n): string {\n\tif (rev === deletedOrMissing) return '(deleted)';\n\tif (!rev) return options?.strings?.working ?? '';\n\tif (isUncommitted(rev)) {\n\t\treturn isUncommittedStaged(rev)\n\t\t\t? (options?.strings?.uncommittedStaged ?? 'Index')\n\t\t\t: (options?.strings?.uncommitted ?? 'Working Tree');\n\t}\n\tif (isRevisionRange(rev) || !isShaWithOptionalRevisionSuffix(rev)) return rev;\n\n\t// Don't allow shas to be shortened to less than 5 characters\n\tconst len = Math.max(5, getAbbreviatedShaLength());\n\n\t// If we have a suffix, append it\n\tconst match = shaShortenRegex.exec(rev);\n\tif (match != null) {\n\t\tconst [, rev, suffix] = match;\n\n\t\tif (suffix != null) {\n\t\t\treturn `${rev.substring(0, len)}${suffix}`;\n\t\t}\n\t}\n\n\treturn rev.substring(0, len);\n}\n\nexport function createRevisionRange(\n\tleft: string | undefined,\n\tright: string | undefined,\n\tnotation: GitRevisionRangeNotation,\n): GitRevisionRange {\n\treturn `${left ?? ''}${notation}${right ?? ''}`;\n}\n\nexport function getRevisionRangeParts(\n\trevRange: GitRevisionRange,\n): { left: string | undefined; right: string | undefined; notation: GitRevisionRangeNotation } | undefined {\n\tconst match = rangeRegex.exec(revRange);\n\tif (match == null) return undefined;\n\n\tconst [, left, notation, right] = match;\n\treturn {\n\t\tleft: left || undefined,\n\t\tright: right || undefined,\n\t\tnotation: notation as GitRevisionRangeNotation,\n\t};\n}\n\nexport function isRevisionRange(\n\trev: string | undefined,\n\trangeType: 'any' | 'qualified' | 'qualified-double-dot' | 'qualified-triple-dot' = 'any',\n): rev is GitRevisionRange {\n\tif (rev == null) return false;\n\n\tswitch (rangeType) {\n\t\tcase 'qualified':\n\t\t\treturn qualifiedRangeRegex.test(rev);\n\t\tcase 'qualified-double-dot':\n\t\t\treturn qualifiedDoubleDotRange.test(rev);\n\t\tcase 'qualified-triple-dot':\n\t\t\treturn qualifiedTripleDotRange.test(rev);\n\t\tdefault:\n\t\t\treturn rangeRegex.test(rev);\n\t}\n}\n\n//** Strips `origin/` from a reference or range, because we \"fake\" origin as the default remote */\nexport function stripOrigin<T extends string | GitRevisionRange | undefined>(ref: T): T {\n\treturn ref?.replace(/(?:^|(?<=..))origin\\//, '') as T;\n}\n"],"mappings":";;AAGA,MAAM,aAAa;AACnB,MAAM,sBAAsB;AAC5B,MAAM,0BAA0B;AAChC,MAAM,0BAA0B;AAChC,MAAM,qCAAqC;AAC3C,MAAM,0BAA0B;AAChC,MAAM,WAAW;AACjB,MAAM,gBAAgB;AACtB,MAAM,iBAAiB;AACvB,MAAM,kBAAkB;AACxB,MAAM,mBAAmB;AACzB,MAAM,yBAAyB;AAE/B,SAAS,QAAQ,OAAe,KAAyB;CACxD,OAAO,CAAC,MAAM,QAAQ,MAAM,KAAK,GAAG;AACrC;;AAGA,SAAgB,qBAAqB,KAAsB;CAC1D,OAAO,QAAQ,yBAAyB,GAAG,KAAK,IAAI,SAAS;AAC9D;;;;AAKA,SAAgB,MAAM,KAAa,aAAsB,OAAgB;CACxE,OAAO,QAAQ,aAAa,gBAAgB,UAAU,GAAG;AAC1D;;AAGA,SAAgB,gCAAgC,KAAsB;CACrE,OAAO,QAAQ,oCAAoC,GAAG;AACvD;;AAGA,SAAgB,sBAAsB,KAAsB;CAC3D,OAAO,QAAQ,gBAAgB,GAAG;AACnC;AAEA,SAAgB,cAAc,KAAyB,QAAiB,OAAgB;CACvF,OAAO,QAAA,8CAAuB,QAAA,+CAA8B,CAAC,SAAS,QAAQ,kBAAkB,GAAG;AACpG;AAEA,SAAgB,oBAAoB,KAAyB,QAAiB,OAAgB;CAC7F,OAAO,QAAA,+CAA8B,CAAC,SAAS,QAAQ,wBAAwB,GAAG;AACnF;AAEA,SAAgB,8BAA8B,KAAsE;CACnH,OAAO,QAAQ,+CAAqB,QAAQ;AAC7C;AAEA,SAAgB,oCACf,KAC8C;CAC9C,OAAO,QAAQ,GAAG,kBAAkB;AACrC;AAEA,IAAI,uBAAuB;AAC3B,SAAgB,0BAAkC;CACjD,OAAO;AACR;AAEA,SAAgB,wBAAwB,QAAsB;CAC7D,uBAAuB;AACxB;;AAGA,SAAgB,4BAAkC;CACjD,uBAAuB;AACxB;AAEA,SAAgB,gBACf,KACA,SAGS;CACT,IAAI,QAAA,6CAA0B,OAAO;CACrC,IAAI,CAAC,KAAK,OAAO,SAAS,SAAS,WAAW;CAC9C,IAAI,cAAc,GAAG,GACpB,OAAO,oBAAoB,GAAG,IAC1B,SAAS,SAAS,qBAAqB,UACvC,SAAS,SAAS,eAAe;CAEtC,IAAI,gBAAgB,GAAG,KAAK,CAAC,gCAAgC,GAAG,GAAG,OAAO;CAG1E,MAAM,MAAM,KAAK,IAAI,GAAG,wBAAwB,CAAC;CAGjD,MAAM,QAAQ,gBAAgB,KAAK,GAAG;CACtC,IAAI,SAAS,MAAM;EAClB,MAAM,GAAG,KAAK,UAAU;EAExB,IAAI,UAAU,MACb,OAAO,GAAG,IAAI,UAAU,GAAG,GAAG,IAAI;CAEpC;CAEA,OAAO,IAAI,UAAU,GAAG,GAAG;AAC5B;AAEA,SAAgB,oBACf,MACA,OACA,UACmB;CACnB,OAAO,GAAG,QAAQ,KAAK,WAAW,SAAS;AAC5C;AAEA,SAAgB,sBACf,UAC0G;CAC1G,MAAM,QAAQ,WAAW,KAAK,QAAQ;CACtC,IAAI,SAAS,MAAM,OAAO,KAAA;CAE1B,MAAM,GAAG,MAAM,UAAU,SAAS;CAClC,OAAO;EACN,MAAM,QAAQ,KAAA;EACd,OAAO,SAAS,KAAA;EACN;CACX;AACD;AAEA,SAAgB,gBACf,KACA,YAAmF,OACzD;CAC1B,IAAI,OAAO,MAAM,OAAO;CAExB,QAAQ,WAAR;EACC,KAAK,aACJ,OAAO,oBAAoB,KAAK,GAAG;EACpC,KAAK,wBACJ,OAAO,wBAAwB,KAAK,GAAG;EACxC,KAAK,wBACJ,OAAO,wBAAwB,KAAK,GAAG;EACxC,SACC,OAAO,WAAW,KAAK,GAAG;CAC5B;AACD;AAGA,SAAgB,YAA6D,KAAW;CACvF,OAAO,KAAK,QAAQ,yBAAyB,EAAE;AAChD"}
1
+ {"version":3,"file":"revision.utils.js","names":[],"sources":["../../../../git/src/utils/revision.utils.ts"],"sourcesContent":["import type { GitRevisionRange, GitRevisionRangeNotation } from '../models/revision.js';\nimport { deletedOrMissing, uncommitted, uncommittedStaged } from '../models/revision.js';\nimport type { ResolvedRevision } from '../providers/revision.js';\n\nconst rangeRegex = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)?(\\.\\.\\.?)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)?$/;\nconst qualifiedRangeRegex = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)(\\.\\.\\.?)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)$/;\nconst qualifiedDoubleDotRange = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)(\\.\\.)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)$/;\nconst qualifiedTripleDotRange = /^([\\w\\-/]+(?:\\.[\\w\\-/]+)*)(\\.\\.\\.)([\\w\\-/]+(?:\\.[\\w\\-/]+)*)$/;\nconst shaWithOptionalRevisionSuffixRegex = /(^[0-9a-f]{40}([\\^@~:]\\S*)?$)|(^[0]{40}(:|-)$)/;\nconst revisionWithSuffixRegex = /^\\S+[\\^@~:]\\S*$/;\nconst shaRegex = /(^[0-9a-f]{40}$)|(^[0]{40}(:|-)$)/;\nconst shaShortRegex = /(^[0-9a-f]{7,40}$)|(^[0]{40}(:|-)$)/;\nconst shaParentRegex = /(^[0-9a-f]{40})\\^[0-3]?$/;\nconst shaShortenRegex = /^(.*?)([\\^@~:].*)?$/;\nconst uncommittedRegex = /^[0]{40}(?:[\\^@~:]\\S*)?:?$/;\nconst uncommittedStagedRegex = /^[0]{40}([\\^@~]\\S*)?:$/;\n\nfunction isMatch(regex: RegExp, rev: string | undefined) {\n\treturn !rev ? false : regex.test(rev);\n}\n\n/** Checks if the rev looks like a revision with a navigation suffixes (like ^, @, ~, or :) */\nexport function isRevisionWithSuffix(rev: string): boolean {\n\treturn isMatch(revisionWithSuffixRegex, rev) && rev.length > 40;\n}\n\n/** Checks if the rev looks like a SHA-1 hash\n * @param allowShort If true, allows short SHAs (7-40 characters)\n */\nexport function isSha(rev: string, allowShort: boolean = false): boolean {\n\treturn isMatch(allowShort ? shaShortRegex : shaRegex, rev);\n}\n\n/** Checks if the rev looks like a SHA-1 hash with an optional revision navigation suffixes (like ^, @, ~, or :) */\nexport function isShaWithOptionalRevisionSuffix(rev: string): boolean {\n\treturn isMatch(shaWithOptionalRevisionSuffixRegex, rev);\n}\n\n/** Checks if the rev looks like a SHA-1 hash with a ^ parent suffix */\nexport function isShaWithParentSuffix(rev: string): boolean {\n\treturn isMatch(shaParentRegex, rev);\n}\n\nexport function isUncommitted(rev: string | undefined, exact: boolean = false): boolean {\n\treturn rev === uncommitted || rev === uncommittedStaged || (!exact && isMatch(uncommittedRegex, rev));\n}\n\nexport function isUncommittedStaged(rev: string | undefined, exact: boolean = false): boolean {\n\treturn rev === uncommittedStaged || (!exact && isMatch(uncommittedStagedRegex, rev));\n}\n\nexport function isUncommittedWithParentSuffix(rev: string | undefined): rev is `${typeof uncommitted}${'^' | ':^'}` {\n\treturn rev === `${uncommitted}^` || rev === `${uncommittedStaged}^`;\n}\n\nexport function isUncommittedStagedWithParentSuffix(\n\trev: string | undefined,\n): rev is `${typeof uncommitted}${'^' | ':^'}` {\n\treturn rev === `${uncommittedStaged}^`;\n}\n\nlet abbreviatedShaLength = 7;\nexport function getAbbreviatedShaLength(): number {\n\treturn abbreviatedShaLength;\n}\n\nexport function setAbbreviatedShaLength(length: number): void {\n\tabbreviatedShaLength = length;\n}\n\n/** Resets the abbreviated SHA length to the default (7). For test isolation. */\nexport function resetAbbreviatedShaLength(): void {\n\tabbreviatedShaLength = 7;\n}\n\nexport function shortenRevision(\n\trev: string | undefined,\n\toptions?: {\n\t\tstrings?: { uncommitted?: string; uncommittedStaged?: string; working?: string };\n\t},\n): string {\n\tif (rev === deletedOrMissing) return '(deleted)';\n\tif (!rev) return options?.strings?.working ?? '';\n\tif (isUncommitted(rev)) {\n\t\treturn isUncommittedStaged(rev)\n\t\t\t? (options?.strings?.uncommittedStaged ?? 'Index')\n\t\t\t: (options?.strings?.uncommitted ?? 'Working Tree');\n\t}\n\tif (isRevisionRange(rev) || !isShaWithOptionalRevisionSuffix(rev)) return rev;\n\n\t// Don't allow shas to be shortened to less than 5 characters\n\tconst len = Math.max(5, getAbbreviatedShaLength());\n\n\t// If we have a suffix, append it\n\tconst match = shaShortenRegex.exec(rev);\n\tif (match != null) {\n\t\tconst [, rev, suffix] = match;\n\n\t\tif (suffix != null) {\n\t\t\treturn `${rev.substring(0, len)}${suffix}`;\n\t\t}\n\t}\n\n\treturn rev.substring(0, len);\n}\n\nexport function createRevisionRange(\n\tleft: string | undefined,\n\tright: string | undefined,\n\tnotation: GitRevisionRangeNotation,\n): GitRevisionRange {\n\treturn `${left ?? ''}${notation}${right ?? ''}`;\n}\n\nexport function getRevisionRangeParts(\n\trevRange: GitRevisionRange,\n): { left: string | undefined; right: string | undefined; notation: GitRevisionRangeNotation } | undefined {\n\tconst match = rangeRegex.exec(revRange);\n\tif (match == null) return undefined;\n\n\tconst [, left, notation, right] = match;\n\treturn {\n\t\tleft: left || undefined,\n\t\tright: right || undefined,\n\t\tnotation: notation as GitRevisionRangeNotation,\n\t};\n}\n\nexport function isRevisionRange(\n\trev: string | undefined,\n\trangeType: 'any' | 'qualified' | 'qualified-double-dot' | 'qualified-triple-dot' = 'any',\n): rev is GitRevisionRange {\n\tif (rev == null) return false;\n\n\tswitch (rangeType) {\n\t\tcase 'qualified':\n\t\t\treturn qualifiedRangeRegex.test(rev);\n\t\tcase 'qualified-double-dot':\n\t\t\treturn qualifiedDoubleDotRange.test(rev);\n\t\tcase 'qualified-triple-dot':\n\t\t\treturn qualifiedTripleDotRange.test(rev);\n\t\tdefault:\n\t\t\treturn rangeRegex.test(rev);\n\t}\n}\n\n//** Strips `origin/` from a reference or range, because we \"fake\" origin as the default remote */\nexport function stripOrigin<T extends string | GitRevisionRange | undefined>(ref: T): T {\n\treturn ref?.replace(/(?:^|(?<=..))origin\\//, '') as T;\n}\n\n/** Whether the file at `relativePath` doesn't exist at a resolved revision — nothing touched it yet, or its last change there deleted it or moved it away (a copy is reported instead of a rename when `diff.renames=copies`) */\nexport function isResolvedRevisionMissing(resolved: ResolvedRevision, relativePath: string): boolean {\n\treturn (\n\t\tresolved.sha === deletedOrMissing ||\n\t\tresolved.status === 'D' ||\n\t\t((resolved.status === 'R' || resolved.status === 'C') &&\n\t\t\tresolved.path != null &&\n\t\t\tresolved.path !== relativePath)\n\t);\n}\n"],"mappings":"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"}
@@ -10,12 +10,15 @@ export interface WorktreeSummaryUpstream {
10
10
  /** The upstream's short name (`origin/main`) */
11
11
  readonly name: string;
12
12
  /**
13
- * The remote it tracks: the longest of the repository's remote names that {@link name} starts with, followed by
14
- * `/` (a remote name can hold a slash). `undefined`, with {@link branch}, when it names no known remote (a branch
15
- * tracking another local branch, or a remote since removed)
13
+ * The remote it tracks, as git reported it; on git too old to report it, the longest of the repository's remote
14
+ * names that {@link name} starts with, followed by `/` (a remote name can hold a slash). `undefined`, with
15
+ * {@link branch}, when it names no known remote (a branch tracking another local branch, or a remote since removed)
16
16
  */
17
17
  readonly remote: string | undefined;
18
- /** The branch on {@link remote}; `undefined` whenever {@link remote} is */
18
+ /**
19
+ * The branch on {@link remote} — git's name for it there, which a custom fetch refspec can make differ from the end
20
+ * of {@link name}; `undefined` whenever {@link remote} is
21
+ */
19
22
  readonly branch: string | undefined;
20
23
  readonly missing: boolean;
21
24
  }
@@ -434,9 +434,28 @@ function readDirty(repo, signal) {
434
434
  return read;
435
435
  }
436
436
  function noop() {}
437
- /** Splits the upstream at the longest remote name it starts with; no split when none matches or nothing follows it */
437
+ /**
438
+ * Uses the remote and branch git reported for the upstream; only when it reported neither does this split the name
439
+ * at the longest remote name it starts with (no split when none matches or nothing follows it)
440
+ */
438
441
  async function toSummaryUpstream(repo, upstream, signal) {
439
- const name = upstream.name;
442
+ const { name, missing } = upstream;
443
+ if (upstream.local) return {
444
+ name,
445
+ remote: void 0,
446
+ branch: void 0,
447
+ missing
448
+ };
449
+ if (upstream.remote) {
450
+ const { remote } = upstream;
451
+ const branch = upstream.remoteBranch || (name.startsWith(`${remote}/`) ? name.substring(remote.length + 1) : void 0);
452
+ return {
453
+ name,
454
+ remote: branch ? remote : void 0,
455
+ branch: branch || void 0,
456
+ missing
457
+ };
458
+ }
440
459
  let remote;
441
460
  for (const r of await repo.remotes.getRemotes(void 0, signal)) if (r.name && name.startsWith(`${r.name}/`) && (remote == null || r.name.length > remote.length)) remote = r.name;
442
461
  const branch = remote != null ? name.substring(remote.length + 1) : void 0;
@@ -444,7 +463,7 @@ async function toSummaryUpstream(repo, upstream, signal) {
444
463
  name,
445
464
  remote: branch ? remote : void 0,
446
465
  branch: branch || void 0,
447
- missing: upstream.missing
466
+ missing
448
467
  };
449
468
  }
450
469
  function summariesEqual(a, b) {
@@ -1 +1 @@
1
- {"version":3,"file":"worktreeSummary.js","names":[],"sources":["../../../../git/src/watching/worktreeSummary.ts"],"sourcesContent":["import { areEqual } from '@gitlens/utils/array.js';\nimport { CancellationError, isCancellationError, raceWithSignal } from '@gitlens/utils/cancellation.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ResourceUsage } from '@gitlens/utils/resourceUsage.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from '../cache.js';\nimport type { GitTrackingUpstream } from '../models/branch.js';\nimport type { GitDir } from '../models/repository.js';\nimport type { AheadOfBaseTier } from '../providers/branches.js';\nimport type { RepositoryService } from '../repositoryService.js';\nimport type { WatcherRepoChangeEvent } from './changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle } from './watchService.js';\nimport type { RepositoryWatchSession } from './watchSession.js';\n\nexport interface WorktreeSummaryUpstream {\n\t/** The upstream's short name (`origin/main`) */\n\treadonly name: string;\n\t/**\n\t * The remote it tracks: the longest of the repository's remote names that {@link name} starts with, followed by\n\t * `/` (a remote name can hold a slash). `undefined`, with {@link branch}, when it names no known remote (a branch\n\t * tracking another local branch, or a remote since removed)\n\t */\n\treadonly remote: string | undefined;\n\t/** The branch on {@link remote}; `undefined` whenever {@link remote} is */\n\treadonly branch: string | undefined;\n\treadonly missing: boolean;\n}\n\nexport interface WorktreeSummaryBase {\n\t/** The ref the count was taken against (see `AheadOfBase.base`) */\n\treadonly ref: string;\n\treadonly ahead: number;\n\t/** The commit {@link ref} pointed at when the count was read; `undefined` when it couldn't be resolved */\n\treadonly sha: string | undefined;\n\t/** Where HEAD forked from {@link ref} (see `AheadOfBase.base`) */\n\treadonly forkPoint: string | undefined;\n\t/** The source the base came from (see `AheadOfBase.base`) */\n\treadonly tier: AheadOfBaseTier;\n}\n\nexport interface WorktreeSummary {\n\t/** The repository path in {@link getRepositoryKey} form */\n\treadonly repoPath: string;\n\t/** The checked-out branch; `undefined` when detached (or unborn with no name) */\n\treadonly branch: string | undefined;\n\treadonly detached: boolean;\n\t/** HEAD's commit; `undefined` when HEAD has no commit yet */\n\treadonly sha: string | undefined;\n\treadonly upstream: WorktreeSummaryUpstream | undefined;\n\t/** Commits ahead of the upstream; 0 without one */\n\treadonly ahead: number;\n\t/** Commits behind the upstream; 0 without one */\n\treadonly behind: number;\n\t/** Whether there is any staged, unstaged or untracked (non-ignored) change */\n\treadonly dirty: boolean;\n\t/**\n\t * How far the branch — or a detached HEAD, measured against the default branch — is past its base; `undefined` when\n\t * unsupported or there is no base\n\t */\n\treadonly base: WorktreeSummaryBase | undefined;\n\t/**\n\t * Full ref names whose movement can change {@link base}, as `getAheadOfBase` reports them (see\n\t * `AheadOfBase.dependsOn`) — given with or without a base, so a watcher also learns when one appears. Empty when\n\t * the base isn't read (an unborn HEAD with no name, or a provider without `getAheadOfBase`), and when the base read\n\t * failed with no earlier list for this HEAD (the first read, or the one after a checkout).\n\t */\n\treadonly baseDependsOn: readonly string[];\n\t/**\n\t * The latest recompute failed (worktree gone, lock contention); the other fields hold the last values read. Also set\n\t * when only the base read failed: the other fields are fresh, and `base` and `baseDependsOn` are their last values,\n\t * or `undefined` and empty if the branch changed.\n\t */\n\treadonly stale: boolean;\n}\n\nexport interface WorktreeSummarySubscribeOptions {\n\t/** Debounce for repo (`.git`) changes this subscriber asks of the watch session; the shortest across subscribers wins */\n\treadonly delayMs?: number;\n\t/** Cap on how long continuous repo activity can defer a recompute; `Infinity` means no cap */\n\treadonly maxWaitMs?: number;\n\t/** Debounce for working-tree changes this subscriber asks of the watch session */\n\treadonly workingTreeDelayMs?: number;\n\t/** Cap on how long continuous working-tree activity can defer a recompute; `Infinity` means no cap */\n\treadonly workingTreeMaxWaitMs?: number;\n}\n\nexport interface WorktreeSummarySubscription extends UnifiedDisposable {\n\t/** The latest summary; `undefined` until the first read succeeds, or after disposal */\n\treadonly current: WorktreeSummary | undefined;\n\t/** Fires when the summary's value changes */\n\treadonly onDidChange: Event<WorktreeSummary>;\n}\n\nexport interface WorktreeSummaryServiceOptions {\n\treadonly watchService: RepositoryWatchService;\n\treadonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\t/**\n\t * Fires as core writes a branch's merge target or base, before the store's promise resolves; the summary re-reads\n\t * that branch's stored values then, so a `getSummary` right after an awaited store sees the new base\n\t */\n\treadonly onDidChangeMergeTargets?: Event<MergeTargetsChangeEvent>;\n\t/**\n\t * Maps a worktree path to the repository common path that {@link onDidChangeMergeTargets} events carry; only that\n\t * repository's summaries react. Without it, or when it returns `undefined`, every summary reacts.\n\t */\n\treadonly getCommonPath?: (repoPath: string) => string | undefined;\n}\n\n/** Read groups, as bit flags so a pending set is a number rather than an allocation per event */\nconst dirtyField = 1;\nconst branchField = 2;\nconst baseField = 4;\n/** Re-reads only the stored values the base read consults, and the base itself only when they changed */\nconst baseKeysField = 8;\n/** Every group a full read covers — a base read refreshes the {@link BaseKeys} snapshot itself */\nconst allFields = dirtyField | branchField | baseField;\n\nconst emptyRefs: readonly string[] = Object.freeze([]);\n\n/**\n * The current branch's stored merge targets (user, detected) and recorded base, as `getAheadOfBase` consults them.\n * A detached HEAD has no stored values, so it holds {@link noBaseKeys}.\n */\ntype BaseKeys = readonly [string | undefined, string | undefined, string | undefined];\n\nconst noBaseKeys: BaseKeys = [undefined, undefined, undefined];\n\ninterface ReadResult {\n\treadonly summary: WorktreeSummary;\n\treadonly baseKeys: BaseKeys | undefined;\n\t/** Why the base read failed when the rest of the summary was read; the summary is then stale */\n\treadonly baseError: unknown;\n}\n\ninterface SummaryEntry {\n\treadonly key: string;\n\t/** Aborted when the last subscriber leaves, cancelling any in-flight read */\n\treadonly abort: AbortController;\n\treadonly subscribers: Set<SummarySubscription>;\n\thandle: WatchHandle | undefined;\n\t/** The timing-neutral listening subscriptions and their event listeners */\n\tlisteners: UnifiedDisposable[] | undefined;\n\tvalue: WorktreeSummary | undefined;\n\t/** The stored base values as of the last base read; a `gk` config change re-reads the base only when they differ */\n\tbaseKeys: BaseKeys | undefined;\n\t/** Why the latest read failed while there was no value to keep */\n\terror: Error | undefined;\n\t/** Read groups due on the next run */\n\tpending: number;\n\t/** The active run (or the start-up, which ends in one); cleared in the same turn the loop sees nothing pending */\n\trunning: Promise<void> | undefined;\n\tdisposed: boolean;\n}\n\nclass SummarySubscription implements WorktreeSummarySubscription {\n\tprivate readonly emitter = new Emitter<WorktreeSummary>();\n\t/** Listener-less session subscriptions that carry this subscriber's timing into the session */\n\tprivate timing: UnifiedDisposable[] | undefined;\n\tprivate disposed = false;\n\n\tconstructor(\n\t\tprivate readonly entry: SummaryEntry,\n\t\tprivate readonly options: WorktreeSummarySubscribeOptions | undefined,\n\t\tprivate readonly onDispose: (subscription: SummarySubscription) => void,\n\t) {}\n\n\tget current(): WorktreeSummary | undefined {\n\t\treturn this.disposed ? undefined : this.entry.value;\n\t}\n\n\tget onDidChange(): Event<WorktreeSummary> {\n\t\treturn this.emitter.event;\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tif (this.timing != null) {\n\t\t\tfor (const d of this.timing) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tthis.timing = undefined;\n\t\t}\n\t\tthis.emitter.dispose();\n\t\tthis.onDispose(this);\n\t}\n\n\tattach(session: RepositoryWatchSession): void {\n\t\tif (this.disposed || this.timing != null) return;\n\n\t\tthis.timing = [\n\t\t\tsession.subscribe({ delayMs: this.options?.delayMs, maxWaitMs: this.options?.maxWaitMs }),\n\t\t\tsession.subscribeToWorkingTree({\n\t\t\t\tdelayMs: this.options?.workingTreeDelayMs,\n\t\t\t\tmaxWaitMs: this.options?.workingTreeMaxWaitMs,\n\t\t\t}),\n\t\t];\n\t}\n\n\tfire(summary: WorktreeSummary): void {\n\t\tthis.emitter.fire(summary);\n\t}\n}\n\n/**\n * A cached, change-notifying summary of each subscribed worktree — its branch, upstream and ahead/behind, whether it\n * has working changes, and how far it is past its base — kept current by the repository watch layer.\n *\n * **Lifecycle.** Entries are per repository key and ref-counted across {@link subscribe} calls. The first subscriber\n * starts watching the worktree and reads everything; the last one to leave aborts any in-flight read, releases the\n * watch and drops the value. Each subscriber's timing options join the watch session's cadence (the shortest wins);\n * the service's own listening subscriptions never constrain it.\n *\n * **Field-scoped recompute.** The summary is read in three groups — `dirty` (working changes), `branch` (branch,\n * HEAD's commit, upstream, ahead/behind) and `base` (ahead of base, the base's tip, and the refs it depends on) — and\n * an event recomputes only the groups it can affect: a working-tree edit, an index or ignore-rule change reads only\n * `dirty`; the upstream moving reads only `branch`; a ref the base depends on reads only `base`; a `gk` config change\n * (mostly bookkeeping writes to the shared `.git/gk/config`), or core's own write of the branch's merge target or\n * base, re-reads the branch's stored merge targets and recorded base, and reads `base` only when one of them changed;\n * the checked-out branch moving, or a HEAD change, reads all of them. Stash, unrelated tags and branches, and the like\n * read nothing. One run is in flight per worktree at a time; events during a run are merged into a single follow-up\n * run.\n *\n * **Value-change-only notification.** {@link WorktreeSummarySubscription.onDidChange} and {@link onDidChangeSummary}\n * fire only when some field's value differs from the previous summary — a recompute that reads the same answer is\n * silent, so a consumer can re-render on every event without diffing. A failed recompute keeps the last values and\n * sets `stale` (firing once); the next successful one clears it.\n */\nexport class WorktreeSummaryService implements UnifiedDisposable {\n\tprivate readonly _onDidChangeSummary = new Emitter<WorktreeSummary>();\n\t/** Fires when the summary of any subscribed worktree changes value */\n\tget onDidChangeSummary(): Event<WorktreeSummary> {\n\t\treturn this._onDidChangeSummary.event;\n\t}\n\n\tprivate readonly entries = new Map<string, SummaryEntry>();\n\tprivate readonly watchService: RepositoryWatchService;\n\tprivate readonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\tprivate readonly getCommonPath: ((repoPath: string) => string | undefined) | undefined;\n\tprivate readonly mergeTargetsListener: UnifiedDisposable | undefined;\n\tprivate _disposed = false;\n\n\tconstructor(options: WorktreeSummaryServiceOptions) {\n\t\tthis.watchService = options.watchService;\n\t\tthis.getRepository = options.getRepository;\n\t\tthis.getCommonPath = options.getCommonPath;\n\t\tthis.mergeTargetsListener = options.onDidChangeMergeTargets?.(e => this.onMergeTargetsChanged(e));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\t\tthis.mergeTargetsListener?.dispose();\n\t\tfor (const entry of [...this.entries.values()]) {\n\t\t\tfor (const subscription of [...entry.subscribers]) {\n\t\t\t\tsubscription.dispose();\n\t\t\t}\n\t\t}\n\t\tthis.entries.clear();\n\t\tthis._onDidChangeSummary.dispose();\n\t}\n\n\t/** Subscribes to a worktree's summary, starting to watch and read it if this is its first subscriber */\n\tsubscribe(repoPath: string, options?: WorktreeSummarySubscribeOptions): WorktreeSummarySubscription {\n\t\tconst key = getRepositoryKey(repoPath);\n\n\t\tlet entry = this._disposed ? undefined : this.entries.get(key);\n\t\tconst isNew = entry == null;\n\t\tif (entry == null) {\n\t\t\tentry = createEntry(key);\n\t\t\tif (this._disposed) {\n\t\t\t\tentry.disposed = true;\n\t\t\t} else {\n\t\t\t\tthis.entries.set(key, entry);\n\t\t\t}\n\t\t}\n\n\t\tconst target = entry;\n\t\tconst subscription = new SummarySubscription(target, options, s => this.release(target, s));\n\t\ttarget.subscribers.add(subscription);\n\n\t\tif (isNew) {\n\t\t\tif (!target.disposed) {\n\t\t\t\ttarget.pending = allFields;\n\t\t\t\ttarget.running = this.start(target);\n\t\t\t}\n\t\t} else if (target.handle != null) {\n\t\t\tsubscription.attach(target.handle.session);\n\t\t} else if (target.running == null && !target.disposed) {\n\t\t\t// The start failed (no repository or git directory yet); a new subscriber retries it\n\t\t\ttarget.pending = allFields;\n\t\t\ttarget.running = this.start(target);\n\t\t}\n\n\t\treturn subscription;\n\t}\n\n\t/**\n\t * Returns a worktree's summary. For a subscribed worktree, the cached value — after waiting for any run in flight\n\t * or pending; it rejects with the read's error when there is still no value. Otherwise a one-off full read that\n\t * is neither cached nor watched.\n\t */\n\tasync getSummary(repoPath: string, cancellation?: AbortSignal): Promise<WorktreeSummary> {\n\t\tconst key = getRepositoryKey(repoPath);\n\t\tconst entry = this.entries.get(key);\n\t\tif (entry == null) {\n\t\t\tconst repo = this.getRepository(key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${key}'`);\n\n\t\t\tconst result = await readSummary(repo, key, allFields, undefined, undefined, cancellation);\n\t\t\treturn result.summary;\n\t\t}\n\n\t\tif (entry.running != null) {\n\t\t\tawait (cancellation != null ? raceWithSignal(entry.running, cancellation) : entry.running);\n\t\t}\n\t\tif (entry.disposed) throw new CancellationError();\n\t\tif (entry.value != null) return entry.value;\n\n\t\tthrow entry.error ?? new Error(`No summary available for '${key}'`);\n\t}\n\n\tgetResourceUsage(): ResourceUsage {\n\t\treturn { 'worktreeSummary.entries.count': this.entries.size };\n\t}\n\n\tprivate async start(entry: SummaryEntry): Promise<void> {\n\t\tlet gitDir: GitDir | undefined;\n\t\ttry {\n\t\t\tgitDir = await this.getRepository(entry.key)?.config.getGitDir?.();\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed) return;\n\n\t\t\tentry.running = undefined;\n\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\tthis.fail(entry, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tconst handle = gitDir != null ? this.watchService.watch(entry.key, gitDir) : undefined;\n\t\tif (handle == null) {\n\t\t\tentry.running = undefined;\n\t\t\tthis.fail(entry, new Error(`Unable to watch '${entry.key}': no repository or git directory`));\n\t\t\treturn;\n\t\t}\n\n\t\tentry.handle = handle;\n\t\tconst session = handle.session;\n\t\t// Timing-neutral: the cadence comes from the subscribers' own session subscriptions (see `attach`)\n\t\tconst repoSub = session.subscribe({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tconst workingTreeSub = session.subscribeToWorkingTree({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tentry.listeners = [\n\t\t\trepoSub.onDidChange(e => this.onRepoChanged(entry, e)),\n\t\t\tworkingTreeSub.onDidChangeWorkingTree(() => this.queue(entry, dirtyField)),\n\t\t\trepoSub,\n\t\t\tworkingTreeSub,\n\t\t];\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.attach(session);\n\t\t}\n\n\t\tawait this.drain(entry);\n\t}\n\n\tprivate onRepoChanged(entry: SummaryEntry, e: WatcherRepoChangeEvent): void {\n\t\tconst value = entry.value;\n\t\tif (value == null || e.changed('unknown', 'head')) {\n\t\t\tthis.queue(entry, allFields);\n\t\t\treturn;\n\t\t}\n\n\t\tlet fields = 0;\n\t\tif (e.changed('index', 'ignores')) {\n\t\t\tfields |= dirtyField;\n\t\t}\n\t\tif (e.changed('config')) {\n\t\t\tfields |= branchField | baseField;\n\t\t}\n\t\tif (e.changed('gkConfig')) {\n\t\t\tfields |= baseKeysField;\n\t\t}\n\t\tif (value.branch != null && e.mayHaveChangedRef(`refs/heads/${value.branch}`)) {\n\t\t\tfields |= allFields;\n\t\t}\n\t\t// The upstream's short name doesn't say whether it's remote or local (`--track main`), so test both forms\n\t\tif (\n\t\t\tvalue.upstream != null &&\n\t\t\t(e.mayHaveChangedRef(`refs/remotes/${value.upstream.name}`) ||\n\t\t\t\te.mayHaveChangedRef(`refs/heads/${value.upstream.name}`))\n\t\t) {\n\t\t\tfields |= branchField;\n\t\t}\n\t\tif ((fields & baseField) === 0) {\n\t\t\tfor (const ref of value.baseDependsOn) {\n\t\t\t\tif (e.mayHaveChangedRef(ref)) {\n\t\t\t\t\tfields |= baseField;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.queue(entry, fields);\n\t}\n\n\t/**\n\t * Queued synchronously, so a store awaited before `getSummary` finds the run in flight. The watcher's later\n\t * `gkConfig` event for the same write then finds the keys unchanged and leaves the base alone.\n\t */\n\tprivate onMergeTargetsChanged(e: MergeTargetsChangeEvent): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\t// Before the watch starts there's nothing to refresh: the start's own read comes after the write\n\t\t\tif (entry.handle == null) continue;\n\n\t\t\t// With no value yet, the first read may have read the stored values before the write, so `queue` reads\n\t\t\t// everything again\n\t\t\tconst value = entry.value;\n\t\t\tif (value != null && (value.branch == null || (e.refs != null && !e.refs.has(value.branch)))) continue;\n\n\t\t\tconst commonPath = this.getCommonPath?.(entry.key);\n\t\t\tif (commonPath != null && commonPath !== e.repoPath) continue;\n\n\t\t\tthis.queue(entry, baseKeysField);\n\t\t}\n\t}\n\n\tprivate queue(entry: SummaryEntry, fields: number): void {\n\t\tif (entry.disposed || fields === 0) return;\n\n\t\t// Without a value a partial read has nothing to merge into, and a stale value's fields can't be trusted: a\n\t\t// partial read would clear `stale` while vouching for groups it never re-read\n\t\tentry.pending |= entry.value == null || entry.value.stale ? allFields : fields;\n\t\tentry.running ??= this.drain(entry);\n\t}\n\n\t/** Runs until nothing is pending. Never rejects — every failure becomes entry state. */\n\tprivate async drain(entry: SummaryEntry): Promise<void> {\n\t\twhile (entry.pending !== 0 && !entry.disposed) {\n\t\t\tconst fields = entry.pending;\n\t\t\tentry.pending = 0;\n\t\t\tawait this.run(entry, fields);\n\t\t}\n\t\t// Same turn as the loop's last check, so an event can't queue fields that no run will pick up\n\t\tentry.running = undefined;\n\t}\n\n\tprivate async run(entry: SummaryEntry, fields: number): Promise<void> {\n\t\tconst previous = entry.value;\n\n\t\tlet result: ReadResult;\n\t\ttry {\n\t\t\tconst repo = this.getRepository(entry.key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${entry.key}'`);\n\n\t\t\tresult = await readSummary(repo, entry.key, fields, previous, entry.baseKeys, entry.abort.signal);\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed || isCancellationError(ex)) return;\n\n\t\t\tthis.fail(entry, ex);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tif (result.summary.stale && (previous == null || !previous.stale)) {\n\t\t\tLogger.warn(\n\t\t\t\t`WorktreeSummaryService: reading the base of '${entry.key}' failed: ${String(result.baseError)}`,\n\t\t\t);\n\t\t}\n\n\t\tentry.error = undefined;\n\t\tentry.baseKeys = result.baseKeys;\n\t\tif (previous != null && summariesEqual(previous, result.summary)) return;\n\n\t\tentry.value = result.summary;\n\t\tthis.fireChange(entry, result.summary);\n\t}\n\n\tprivate fail(entry: SummaryEntry, ex: unknown): void {\n\t\tconst previous = entry.value;\n\t\tif (previous == null) {\n\t\t\tif (entry.error == null) {\n\t\t\t\tLogger.warn(`WorktreeSummaryService: reading '${entry.key}' failed: ${String(ex)}`);\n\t\t\t}\n\t\t\tentry.error = ex instanceof Error ? ex : new Error(String(ex));\n\t\t\treturn;\n\t\t}\n\t\tif (previous.stale) return;\n\n\t\tLogger.warn(`WorktreeSummaryService: refreshing '${entry.key}' failed: ${String(ex)}`);\n\t\tconst summary: WorktreeSummary = { ...previous, stale: true };\n\t\tentry.value = summary;\n\t\tthis.fireChange(entry, summary);\n\t}\n\n\tprivate fireChange(entry: SummaryEntry, summary: WorktreeSummary): void {\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.fire(summary);\n\t\t}\n\t\tthis._onDidChangeSummary.fire(summary);\n\t}\n\n\tprivate release(entry: SummaryEntry, subscription: SummarySubscription): void {\n\t\tentry.subscribers.delete(subscription);\n\t\tif (entry.subscribers.size !== 0) return;\n\n\t\tentry.disposed = true;\n\t\tentry.abort.abort();\n\t\tif (entry.listeners != null) {\n\t\t\tfor (const d of entry.listeners) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tentry.listeners = undefined;\n\t\t}\n\t\tentry.handle?.dispose();\n\t\tentry.handle = undefined;\n\t\tentry.value = undefined;\n\t\tentry.baseKeys = undefined;\n\t\tentry.pending = 0;\n\t\tentry.running = undefined;\n\n\t\tif (this.entries.get(entry.key) === entry) {\n\t\t\tthis.entries.delete(entry.key);\n\t\t}\n\t}\n}\n\nfunction createEntry(key: string): SummaryEntry {\n\treturn {\n\t\tkey: key,\n\t\tabort: new AbortController(),\n\t\tsubscribers: new Set(),\n\t\thandle: undefined,\n\t\tlisteners: undefined,\n\t\tvalue: undefined,\n\t\tbaseKeys: undefined,\n\t\terror: undefined,\n\t\tpending: 0,\n\t\trunning: undefined,\n\t\tdisposed: false,\n\t};\n}\n\n/**\n * Reads the due groups and fills the rest from `previous` (every group when there is none). The branch read comes\n * before the base read, which needs its name; the dirty read runs alongside both. A branch read that finds a\n * different branch (or detached state) also reads the base and dirty groups, and stored base values that differ\n * from `previousBaseKeys` read the base group. A failed base read doesn't fail the rest: the summary comes back stale,\n * keeping the previous base and `baseDependsOn` only while the branch is the same.\n */\nasync function readSummary(\n\trepo: RepositoryService,\n\tkey: string,\n\tfields: number,\n\tprevious: WorktreeSummary | undefined,\n\tpreviousBaseKeys: BaseKeys | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadResult> {\n\tif (previous == null) {\n\t\tfields = allFields;\n\t}\n\n\tlet dirtyRead = fields & dirtyField ? readDirty(repo, signal) : undefined;\n\n\tlet branch = previous?.branch;\n\tlet detached = previous?.detached ?? false;\n\tlet sha = previous?.sha;\n\tlet upstream = previous?.upstream;\n\tlet ahead = previous?.ahead ?? 0;\n\tlet behind = previous?.behind ?? 0;\n\tif (fields & branchField) {\n\t\tconst current = await repo.branches.getBranch(undefined, undefined, signal);\n\t\tdetached = current?.detached ?? false;\n\t\tbranch = detached ? undefined : current?.name || undefined;\n\t\tsha = current?.sha;\n\t\tupstream = current?.upstream != null ? await toSummaryUpstream(repo, current.upstream, signal) : undefined;\n\t\tahead = current?.upstream?.state.ahead ?? 0;\n\t\tbehind = current?.upstream?.state.behind ?? 0;\n\n\t\tif (previous != null && (branch !== previous.branch || detached !== previous.detached)) {\n\t\t\tfields |= baseField;\n\t\t\tdirtyRead ??= readDirty(repo, signal);\n\t\t}\n\t}\n\n\tlet base = previous?.base;\n\tlet dependsOn = previous?.baseDependsOn ?? emptyRefs;\n\tlet baseKeys = previousBaseKeys;\n\tlet baseKeysRead = false;\n\tlet baseFailed = false;\n\tlet baseError: unknown;\n\t// A detached HEAD is measured as `undefined`; an unborn HEAD with no name has nothing to measure\n\tconst baseReadable = repo.branches.getAheadOfBase != null && (detached || branch != null);\n\tif (fields & baseKeysField && !(fields & baseField) && baseReadable) {\n\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\tbaseKeysRead = true;\n\t\tif (baseKeys == null || previousBaseKeys == null || !baseKeysEqual(baseKeys, previousBaseKeys)) {\n\t\t\tfields |= baseField;\n\t\t}\n\t}\n\tif (fields & baseField) {\n\t\tif (!baseReadable) {\n\t\t\tbase = undefined;\n\t\t\tdependsOn = emptyRefs;\n\t\t\tbaseKeys = undefined;\n\t\t} else {\n\t\t\t// Snapshot before the base read, so a write it misses is one the next `gk` config event sees as a change\n\t\t\tif (!baseKeysRead) {\n\t\t\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst aheadOfBase = await repo.branches.getAheadOfBase?.(branch, undefined, signal);\n\t\t\t\tbase = aheadOfBase?.base;\n\t\t\t\tdependsOn = aheadOfBase?.dependsOn ?? emptyRefs;\n\t\t\t} catch (ex) {\n\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\tbaseFailed = true;\n\t\t\t\tbaseError = ex;\n\t\t\t\t// Keep `baseDependsOn` for the same branch: a fetch moving one of its refs is what retries the base\n\t\t\t\tconst sameBranch = previous != null && branch === previous.branch && detached === previous.detached;\n\t\t\t\tbase = sameBranch ? previous.base : undefined;\n\t\t\t\tdependsOn = sameBranch ? previous.baseDependsOn : emptyRefs;\n\t\t\t\tbaseKeys = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst dirty = dirtyRead != null ? await dirtyRead : (previous?.dirty ?? false);\n\n\treturn {\n\t\tsummary: {\n\t\t\trepoPath: key,\n\t\t\tbranch: branch,\n\t\t\tdetached: detached,\n\t\t\tsha: sha,\n\t\t\tupstream: upstream,\n\t\t\tahead: ahead,\n\t\t\tbehind: behind,\n\t\t\tdirty: dirty,\n\t\t\tbase: base,\n\t\t\tbaseDependsOn: dependsOn,\n\t\t\tstale: baseFailed,\n\t\t},\n\t\tbaseKeys: baseKeys,\n\t\tbaseError: baseError,\n\t};\n}\n\n/**\n * Reads {@link BaseKeys} for `branch`, normalized the way `getAheadOfBase` reads them. `undefined` when any read\n * failed — an unknown snapshot, so the next `gk` config change reads the base rather than trusting it. A detached\n * HEAD (`branch` undefined) has no stored values to read.\n */\nasync function readBaseKeys(repo: RepositoryService, branch: string | undefined): Promise<BaseKeys | undefined> {\n\tif (branch == null) return noBaseKeys;\n\n\ttry {\n\t\tconst [user, detected, recorded] = await Promise.all([\n\t\t\trepo.branches.getStoredUserMergeTargetBranchName?.(branch),\n\t\t\trepo.branches.getStoredDetectedMergeTargetBranchName?.(branch),\n\t\t\trepo.config.getGkConfig?.(`branch.${branch}.gk-merge-base`),\n\t\t]);\n\t\treturn [user?.trim() || undefined, detected?.trim() || undefined, recorded?.trim() || undefined];\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\nfunction baseKeysEqual(a: BaseKeys, b: BaseKeys): boolean {\n\treturn a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n}\n\nfunction readDirty(repo: RepositoryService, signal: AbortSignal | undefined): Promise<boolean> {\n\tconst read = repo.status.hasWorkingChanges({ untracked: true }, signal);\n\t// Awaited only after the branch and base reads; if one of those throws first, this rejection has no other handler\n\tread.catch(noop);\n\treturn read;\n}\n\nfunction noop(): void {}\n\n/** Splits the upstream at the longest remote name it starts with; no split when none matches or nothing follows it */\nasync function toSummaryUpstream(\n\trepo: RepositoryService,\n\tupstream: GitTrackingUpstream,\n\tsignal: AbortSignal | undefined,\n): Promise<WorktreeSummaryUpstream> {\n\tconst name = upstream.name;\n\n\tlet remote: string | undefined;\n\tfor (const r of await repo.remotes.getRemotes(undefined, signal)) {\n\t\tif (r.name && name.startsWith(`${r.name}/`) && (remote == null || r.name.length > remote.length)) {\n\t\t\tremote = r.name;\n\t\t}\n\t}\n\n\tconst branch = remote != null ? name.substring(remote.length + 1) : undefined;\n\treturn {\n\t\tname: name,\n\t\tremote: branch ? remote : undefined,\n\t\tbranch: branch || undefined,\n\t\tmissing: upstream.missing,\n\t};\n}\n\nfunction summariesEqual(a: WorktreeSummary, b: WorktreeSummary): boolean {\n\treturn (\n\t\ta.repoPath === b.repoPath &&\n\t\ta.branch === b.branch &&\n\t\ta.detached === b.detached &&\n\t\ta.sha === b.sha &&\n\t\ta.ahead === b.ahead &&\n\t\ta.behind === b.behind &&\n\t\ta.dirty === b.dirty &&\n\t\ta.stale === b.stale &&\n\t\tareEqual(a.baseDependsOn, b.baseDependsOn) &&\n\t\t(a.upstream === b.upstream ||\n\t\t\t(a.upstream != null &&\n\t\t\t\tb.upstream != null &&\n\t\t\t\ta.upstream.name === b.upstream.name &&\n\t\t\t\ta.upstream.remote === b.upstream.remote &&\n\t\t\t\ta.upstream.branch === b.upstream.branch &&\n\t\t\t\ta.upstream.missing === b.upstream.missing)) &&\n\t\t(a.base === b.base ||\n\t\t\t(a.base != null &&\n\t\t\t\tb.base != null &&\n\t\t\t\ta.base.ref === b.base.ref &&\n\t\t\t\ta.base.ahead === b.base.ahead &&\n\t\t\t\ta.base.sha === b.base.sha &&\n\t\t\t\ta.base.forkPoint === b.base.forkPoint &&\n\t\t\t\ta.base.tier === b.base.tier))\n\t);\n}\n"],"mappings":";;;;;;;AAgHA,MAAM,aAAa;AACnB,MAAM,cAAc;AACpB,MAAM,YAAY;;AAElB,MAAM,gBAAgB;;AAEtB,MAAM,YAAY;AAElB,MAAM,YAA+B,OAAO,OAAO,CAAC,CAAC;AAQrD,MAAM,aAAuB;CAAC,KAAA;CAAW,KAAA;CAAW,KAAA;AAAS;AA6B7D,IAAM,sBAAN,MAAiE;CAO9C;CACA;CACA;CARlB,UAA2B,IAAI,QAAyB;;CAExD;CACA,WAAmB;CAEnB,YACC,OACA,SACA,WACC;EAHgB,KAAA,QAAA;EACA,KAAA,UAAA;EACA,KAAA,YAAA;CACf;CAEH,IAAI,UAAuC;EAC1C,OAAO,KAAK,WAAW,KAAA,IAAY,KAAK,MAAM;CAC/C;CAEA,IAAI,cAAsC;EACzC,OAAO,KAAK,QAAQ;CACrB;CAEA,CAAC,OAAO,WAAiB;EACxB,KAAK,QAAQ;CACd;CAEA,UAAgB;EACf,IAAI,KAAK,UAAU;EAEnB,KAAK,WAAW;EAChB,IAAI,KAAK,UAAU,MAAM;GACxB,KAAK,MAAM,KAAK,KAAK,QACpB,EAAE,QAAQ;GAEX,KAAK,SAAS,KAAA;EACf;EACA,KAAK,QAAQ,QAAQ;EACrB,KAAK,UAAU,IAAI;CACpB;CAEA,OAAO,SAAuC;EAC7C,IAAI,KAAK,YAAY,KAAK,UAAU,MAAM;EAE1C,KAAK,SAAS,CACb,QAAQ,UAAU;GAAE,SAAS,KAAK,SAAS;GAAS,WAAW,KAAK,SAAS;EAAU,CAAC,GACxF,QAAQ,uBAAuB;GAC9B,SAAS,KAAK,SAAS;GACvB,WAAW,KAAK,SAAS;EAC1B,CAAC,CACF;CACD;CAEA,KAAK,SAAgC;EACpC,KAAK,QAAQ,KAAK,OAAO;CAC1B;AACD;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,IAAa,yBAAb,MAAiE;CAChE,sBAAuC,IAAI,QAAyB;;CAEpE,IAAI,qBAA6C;EAChD,OAAO,KAAK,oBAAoB;CACjC;CAEA,0BAA2B,IAAI,IAA0B;CACzD;CACA;CACA;CACA;CACA,YAAoB;CAEpB,YAAY,SAAwC;EACnD,KAAK,eAAe,QAAQ;EAC5B,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,uBAAuB,QAAQ,2BAA0B,MAAK,KAAK,sBAAsB,CAAC,CAAC;CACjG;CAEA,CAAC,OAAO,WAAiB;EACxB,KAAK,QAAQ;CACd;CAEA,UAAgB;EACf,IAAI,KAAK,WAAW;EAEpB,KAAK,YAAY;EACjB,KAAK,sBAAsB,QAAQ;EACnC,KAAK,MAAM,SAAS,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,GAC5C,KAAK,MAAM,gBAAgB,CAAC,GAAG,MAAM,WAAW,GAC/C,aAAa,QAAQ;EAGvB,KAAK,QAAQ,MAAM;EACnB,KAAK,oBAAoB,QAAQ;CAClC;;CAGA,UAAU,UAAkB,SAAwE;EACnG,MAAM,MAAM,iBAAiB,QAAQ;EAErC,IAAI,QAAQ,KAAK,YAAY,KAAA,IAAY,KAAK,QAAQ,IAAI,GAAG;EAC7D,MAAM,QAAQ,SAAS;EACvB,IAAI,SAAS,MAAM;GAClB,QAAQ,YAAY,GAAG;GACvB,IAAI,KAAK,WACR,MAAM,WAAW;QAEjB,KAAK,QAAQ,IAAI,KAAK,KAAK;EAE7B;EAEA,MAAM,SAAS;EACf,MAAM,eAAe,IAAI,oBAAoB,QAAQ,UAAS,MAAK,KAAK,QAAQ,QAAQ,CAAC,CAAC;EAC1F,OAAO,YAAY,IAAI,YAAY;EAEnC,IAAI,OACC;OAAA,CAAC,OAAO,UAAU;IACrB,OAAO,UAAU;IACjB,OAAO,UAAU,KAAK,MAAM,MAAM;GACnC;SACM,IAAI,OAAO,UAAU,MAC3B,aAAa,OAAO,OAAO,OAAO,OAAO;OACnC,IAAI,OAAO,WAAW,QAAQ,CAAC,OAAO,UAAU;GAEtD,OAAO,UAAU;GACjB,OAAO,UAAU,KAAK,MAAM,MAAM;EACnC;EAEA,OAAO;CACR;;;;;;CAOA,MAAM,WAAW,UAAkB,cAAsD;EACxF,MAAM,MAAM,iBAAiB,QAAQ;EACrC,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;EAClC,IAAI,SAAS,MAAM;GAClB,MAAM,OAAO,KAAK,cAAc,GAAG;GACnC,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;GAGpE,QAAO,MADc,YAAY,MAAM,KAAK,WAAW,KAAA,GAAW,KAAA,GAAW,YAAY,EAAA,CAC3E;EACf;EAEA,IAAI,MAAM,WAAW,MACpB,OAAO,gBAAgB,OAAO,eAAe,MAAM,SAAS,YAAY,IAAI,MAAM;EAEnF,IAAI,MAAM,UAAU,MAAM,IAAI,kBAAkB;EAChD,IAAI,MAAM,SAAS,MAAM,OAAO,MAAM;EAEtC,MAAM,MAAM,yBAAS,IAAI,MAAM,6BAA6B,IAAI,EAAE;CACnE;CAEA,mBAAkC;EACjC,OAAO,EAAE,iCAAiC,KAAK,QAAQ,KAAK;CAC7D;CAEA,MAAc,MAAM,OAAoC;EACvD,IAAI;EACJ,IAAI;GACH,SAAS,MAAM,KAAK,cAAc,MAAM,GAAG,CAAC,EAAE,OAAO,YAAY;EAClE,SAAS,IAAI;GACZ,IAAI,MAAM,UAAU;GAEpB,MAAM,UAAU,KAAA;GAChB,IAAI,CAAC,oBAAoB,EAAE,GAC1B,KAAK,KAAK,OAAO,EAAE;GAEpB;EACD;EACA,IAAI,MAAM,UAAU;EAEpB,MAAM,SAAS,UAAU,OAAO,KAAK,aAAa,MAAM,MAAM,KAAK,MAAM,IAAI,KAAA;EAC7E,IAAI,UAAU,MAAM;GACnB,MAAM,UAAU,KAAA;GAChB,KAAK,KAAK,uBAAO,IAAI,MAAM,oBAAoB,MAAM,IAAI,kCAAkC,CAAC;GAC5F;EACD;EAEA,MAAM,SAAS;EACf,MAAM,UAAU,OAAO;EAEvB,MAAM,UAAU,QAAQ,UAAU;GAAE,SAAS;GAAU,WAAW;EAAS,CAAC;EAC5E,MAAM,iBAAiB,QAAQ,uBAAuB;GAAE,SAAS;GAAU,WAAW;EAAS,CAAC;EAChG,MAAM,YAAY;GACjB,QAAQ,aAAY,MAAK,KAAK,cAAc,OAAO,CAAC,CAAC;GACrD,eAAe,6BAA6B,KAAK,MAAM,OAAO,UAAU,CAAC;GACzE;GACA;EACD;EACA,KAAK,MAAM,gBAAgB,MAAM,aAChC,aAAa,OAAO,OAAO;EAG5B,MAAM,KAAK,MAAM,KAAK;CACvB;CAEA,cAAsB,OAAqB,GAAiC;EAC3E,MAAM,QAAQ,MAAM;EACpB,IAAI,SAAS,QAAQ,EAAE,QAAQ,WAAW,MAAM,GAAG;GAClD,KAAK,MAAM,OAAO,SAAS;GAC3B;EACD;EAEA,IAAI,SAAS;EACb,IAAI,EAAE,QAAQ,SAAS,SAAS,GAC/B,UAAU;EAEX,IAAI,EAAE,QAAQ,QAAQ,GACrB,UAAU;EAEX,IAAI,EAAE,QAAQ,UAAU,GACvB,UAAU;EAEX,IAAI,MAAM,UAAU,QAAQ,EAAE,kBAAkB,cAAc,MAAM,QAAQ,GAC3E,UAAU;EAGX,IACC,MAAM,YAAY,SACjB,EAAE,kBAAkB,gBAAgB,MAAM,SAAS,MAAM,KACzD,EAAE,kBAAkB,cAAc,MAAM,SAAS,MAAM,IAExD,UAAU;EAEX,KAAK,SAAS,eAAe,GACvB;QAAA,MAAM,OAAO,MAAM,eACvB,IAAI,EAAE,kBAAkB,GAAG,GAAG;IAC7B,UAAU;IACV;GACD;;EAIF,KAAK,MAAM,OAAO,MAAM;CACzB;;;;;CAMA,sBAA8B,GAAkC;EAC/D,KAAK,MAAM,SAAS,KAAK,QAAQ,OAAO,GAAG;GAE1C,IAAI,MAAM,UAAU,MAAM;GAI1B,MAAM,QAAQ,MAAM;GACpB,IAAI,SAAS,SAAS,MAAM,UAAU,QAAS,EAAE,QAAQ,QAAQ,CAAC,EAAE,KAAK,IAAI,MAAM,MAAM,IAAK;GAE9F,MAAM,aAAa,KAAK,gBAAgB,MAAM,GAAG;GACjD,IAAI,cAAc,QAAQ,eAAe,EAAE,UAAU;GAErD,KAAK,MAAM,OAAO,aAAa;EAChC;CACD;CAEA,MAAc,OAAqB,QAAsB;EACxD,IAAI,MAAM,YAAY,WAAW,GAAG;EAIpC,MAAM,WAAW,MAAM,SAAS,QAAQ,MAAM,MAAM,QAAQ,YAAY;EACxE,MAAM,YAAY,KAAK,MAAM,KAAK;CACnC;;CAGA,MAAc,MAAM,OAAoC;EACvD,OAAO,MAAM,YAAY,KAAK,CAAC,MAAM,UAAU;GAC9C,MAAM,SAAS,MAAM;GACrB,MAAM,UAAU;GAChB,MAAM,KAAK,IAAI,OAAO,MAAM;EAC7B;EAEA,MAAM,UAAU,KAAA;CACjB;CAEA,MAAc,IAAI,OAAqB,QAA+B;EACrE,MAAM,WAAW,MAAM;EAEvB,IAAI;EACJ,IAAI;GACH,MAAM,OAAO,KAAK,cAAc,MAAM,GAAG;GACzC,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,4BAA4B,MAAM,IAAI,EAAE;GAE1E,SAAS,MAAM,YAAY,MAAM,MAAM,KAAK,QAAQ,UAAU,MAAM,UAAU,MAAM,MAAM,MAAM;EACjG,SAAS,IAAI;GACZ,IAAI,MAAM,YAAY,oBAAoB,EAAE,GAAG;GAE/C,KAAK,KAAK,OAAO,EAAE;GACnB;EACD;EACA,IAAI,MAAM,UAAU;EAEpB,IAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,CAAC,SAAS,QAC1D,QAAO,KACN,gDAAgD,MAAM,IAAI,YAAY,OAAO,OAAO,SAAS,GAC9F;EAGD,MAAM,QAAQ,KAAA;EACd,MAAM,WAAW,OAAO;EACxB,IAAI,YAAY,QAAQ,eAAe,UAAU,OAAO,OAAO,GAAG;EAElE,MAAM,QAAQ,OAAO;EACrB,KAAK,WAAW,OAAO,OAAO,OAAO;CACtC;CAEA,KAAa,OAAqB,IAAmB;EACpD,MAAM,WAAW,MAAM;EACvB,IAAI,YAAY,MAAM;GACrB,IAAI,MAAM,SAAS,MAClB,QAAO,KAAK,oCAAoC,MAAM,IAAI,YAAY,OAAO,EAAE,GAAG;GAEnF,MAAM,QAAQ,cAAc,QAAQ,KAAK,IAAI,MAAM,OAAO,EAAE,CAAC;GAC7D;EACD;EACA,IAAI,SAAS,OAAO;EAEpB,QAAO,KAAK,uCAAuC,MAAM,IAAI,YAAY,OAAO,EAAE,GAAG;EACrF,MAAM,UAA2B;GAAE,GAAG;GAAU,OAAO;EAAK;EAC5D,MAAM,QAAQ;EACd,KAAK,WAAW,OAAO,OAAO;CAC/B;CAEA,WAAmB,OAAqB,SAAgC;EACvE,KAAK,MAAM,gBAAgB,MAAM,aAChC,aAAa,KAAK,OAAO;EAE1B,KAAK,oBAAoB,KAAK,OAAO;CACtC;CAEA,QAAgB,OAAqB,cAAyC;EAC7E,MAAM,YAAY,OAAO,YAAY;EACrC,IAAI,MAAM,YAAY,SAAS,GAAG;EAElC,MAAM,WAAW;EACjB,MAAM,MAAM,MAAM;EAClB,IAAI,MAAM,aAAa,MAAM;GAC5B,KAAK,MAAM,KAAK,MAAM,WACrB,EAAE,QAAQ;GAEX,MAAM,YAAY,KAAA;EACnB;EACA,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,KAAA;EACf,MAAM,QAAQ,KAAA;EACd,MAAM,WAAW,KAAA;EACjB,MAAM,UAAU;EAChB,MAAM,UAAU,KAAA;EAEhB,IAAI,KAAK,QAAQ,IAAI,MAAM,GAAG,MAAM,OACnC,KAAK,QAAQ,OAAO,MAAM,GAAG;CAE/B;AACD;AAEA,SAAS,YAAY,KAA2B;CAC/C,OAAO;EACD;EACL,OAAO,IAAI,gBAAgB;EAC3B,6BAAa,IAAI,IAAI;EACrB,QAAQ,KAAA;EACR,WAAW,KAAA;EACX,OAAO,KAAA;EACP,UAAU,KAAA;EACV,OAAO,KAAA;EACP,SAAS;EACT,SAAS,KAAA;EACT,UAAU;CACX;AACD;;;;;;;;AASA,eAAe,YACd,MACA,KACA,QACA,UACA,kBACA,QACsB;CACtB,IAAI,YAAY,MACf,SAAS;CAGV,IAAI,YAAY,SAAS,aAAa,UAAU,MAAM,MAAM,IAAI,KAAA;CAEhE,IAAI,SAAS,UAAU;CACvB,IAAI,WAAW,UAAU,YAAY;CACrC,IAAI,MAAM,UAAU;CACpB,IAAI,WAAW,UAAU;CACzB,IAAI,QAAQ,UAAU,SAAS;CAC/B,IAAI,SAAS,UAAU,UAAU;CACjC,IAAI,SAAS,aAAa;EACzB,MAAM,UAAU,MAAM,KAAK,SAAS,UAAU,KAAA,GAAW,KAAA,GAAW,MAAM;EAC1E,WAAW,SAAS,YAAY;EAChC,SAAS,WAAW,KAAA,IAAY,SAAS,QAAQ,KAAA;EACjD,MAAM,SAAS;EACf,WAAW,SAAS,YAAY,OAAO,MAAM,kBAAkB,MAAM,QAAQ,UAAU,MAAM,IAAI,KAAA;EACjG,QAAQ,SAAS,UAAU,MAAM,SAAS;EAC1C,SAAS,SAAS,UAAU,MAAM,UAAU;EAE5C,IAAI,YAAY,SAAS,WAAW,SAAS,UAAU,aAAa,SAAS,WAAW;GACvF,UAAU;GACV,cAAc,UAAU,MAAM,MAAM;EACrC;CACD;CAEA,IAAI,OAAO,UAAU;CACrB,IAAI,YAAY,UAAU,iBAAiB;CAC3C,IAAI,WAAW;CACf,IAAI,eAAe;CACnB,IAAI,aAAa;CACjB,IAAI;CAEJ,MAAM,eAAe,KAAK,SAAS,kBAAkB,SAAS,YAAY,UAAU;CACpF,IAAI,SAAS,iBAAiB,EAAE,SAAS,cAAc,cAAc;EACpE,WAAW,MAAM,aAAa,MAAM,MAAM;EAC1C,eAAe;EACf,IAAI,YAAY,QAAQ,oBAAoB,QAAQ,CAAC,cAAc,UAAU,gBAAgB,GAC5F,UAAU;CAEZ;CACA,IAAI,SAAS,WAAW;EACvB,IAAI,CAAC,cAAc;GAClB,OAAO,KAAA;GACP,YAAY;GACZ,WAAW,KAAA;EACZ,OAAO;GAEN,IAAI,CAAC,cACJ,WAAW,MAAM,aAAa,MAAM,MAAM;GAE3C,IAAI;IACH,MAAM,cAAc,MAAM,KAAK,SAAS,iBAAiB,QAAQ,KAAA,GAAW,MAAM;IAClF,OAAO,aAAa;IACpB,YAAY,aAAa,aAAa;GACvC,SAAS,IAAI;IACZ,IAAI,oBAAoB,EAAE,GAAG,MAAM;IAEnC,aAAa;IACb,YAAY;IAEZ,MAAM,aAAa,YAAY,QAAQ,WAAW,SAAS,UAAU,aAAa,SAAS;IAC3F,OAAO,aAAa,SAAS,OAAO,KAAA;IACpC,YAAY,aAAa,SAAS,gBAAgB;IAClD,WAAW,KAAA;GACZ;EACD;CACD;CAEA,MAAM,QAAQ,aAAa,OAAO,MAAM,YAAa,UAAU,SAAS;CAExE,OAAO;EACN,SAAS;GACR,UAAU;GACF;GACE;GACL;GACK;GACH;GACC;GACD;GACD;GACN,eAAe;GACf,OAAO;EACR;EACU;EACC;CACZ;AACD;;;;;;AAOA,eAAe,aAAa,MAAyB,QAA2D;CAC/G,IAAI,UAAU,MAAM,OAAO;CAE3B,IAAI;EACH,MAAM,CAAC,MAAM,UAAU,YAAY,MAAM,QAAQ,IAAI;GACpD,KAAK,SAAS,qCAAqC,MAAM;GACzD,KAAK,SAAS,yCAAyC,MAAM;GAC7D,KAAK,OAAO,cAAc,UAAU,OAAO,eAAe;EAC3D,CAAC;EACD,OAAO;GAAC,MAAM,KAAK,KAAK,KAAA;GAAW,UAAU,KAAK,KAAK,KAAA;GAAW,UAAU,KAAK,KAAK,KAAA;EAAS;CAChG,SAAS,IAAI;EACZ,IAAI,oBAAoB,EAAE,GAAG,MAAM;EAEnC;CACD;AACD;AAEA,SAAS,cAAc,GAAa,GAAsB;CACzD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;AACrD;AAEA,SAAS,UAAU,MAAyB,QAAmD;CAC9F,MAAM,OAAO,KAAK,OAAO,kBAAkB,EAAE,WAAW,KAAK,GAAG,MAAM;CAEtE,KAAK,MAAM,IAAI;CACf,OAAO;AACR;AAEA,SAAS,OAAa,CAAC;;AAGvB,eAAe,kBACd,MACA,UACA,QACmC;CACnC,MAAM,OAAO,SAAS;CAEtB,IAAI;CACJ,KAAK,MAAM,KAAK,MAAM,KAAK,QAAQ,WAAW,KAAA,GAAW,MAAM,GAC9D,IAAI,EAAE,QAAQ,KAAK,WAAW,GAAG,EAAE,KAAK,EAAE,MAAM,UAAU,QAAQ,EAAE,KAAK,SAAS,OAAO,SACxF,SAAS,EAAE;CAIb,MAAM,SAAS,UAAU,OAAO,KAAK,UAAU,OAAO,SAAS,CAAC,IAAI,KAAA;CACpE,OAAO;EACA;EACN,QAAQ,SAAS,SAAS,KAAA;EAC1B,QAAQ,UAAU,KAAA;EAClB,SAAS,SAAS;CACnB;AACD;AAEA,SAAS,eAAe,GAAoB,GAA6B;CACxE,OACC,EAAE,aAAa,EAAE,YACjB,EAAE,WAAW,EAAE,UACf,EAAE,aAAa,EAAE,YACjB,EAAE,QAAQ,EAAE,OACZ,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,UAAU,EAAE,SACd,EAAE,UAAU,EAAE,SACd,SAAS,EAAE,eAAe,EAAE,aAAa,MACxC,EAAE,aAAa,EAAE,YAChB,EAAE,YAAY,QACd,EAAE,YAAY,QACd,EAAE,SAAS,SAAS,EAAE,SAAS,QAC/B,EAAE,SAAS,WAAW,EAAE,SAAS,UACjC,EAAE,SAAS,WAAW,EAAE,SAAS,UACjC,EAAE,SAAS,YAAY,EAAE,SAAS,aACnC,EAAE,SAAS,EAAE,QACZ,EAAE,QAAQ,QACV,EAAE,QAAQ,QACV,EAAE,KAAK,QAAQ,EAAE,KAAK,OACtB,EAAE,KAAK,UAAU,EAAE,KAAK,SACxB,EAAE,KAAK,QAAQ,EAAE,KAAK,OACtB,EAAE,KAAK,cAAc,EAAE,KAAK,aAC5B,EAAE,KAAK,SAAS,EAAE,KAAK;AAE3B"}
1
+ {"version":3,"file":"worktreeSummary.js","names":[],"sources":["../../../../git/src/watching/worktreeSummary.ts"],"sourcesContent":["import { areEqual } from '@gitlens/utils/array.js';\nimport { CancellationError, isCancellationError, raceWithSignal } from '@gitlens/utils/cancellation.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { Logger } from '@gitlens/utils/logger.js';\nimport type { ResourceUsage } from '@gitlens/utils/resourceUsage.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from '../cache.js';\nimport type { GitTrackingUpstream } from '../models/branch.js';\nimport type { GitDir } from '../models/repository.js';\nimport type { AheadOfBaseTier } from '../providers/branches.js';\nimport type { RepositoryService } from '../repositoryService.js';\nimport type { WatcherRepoChangeEvent } from './changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle } from './watchService.js';\nimport type { RepositoryWatchSession } from './watchSession.js';\n\nexport interface WorktreeSummaryUpstream {\n\t/** The upstream's short name (`origin/main`) */\n\treadonly name: string;\n\t/**\n\t * The remote it tracks, as git reported it; on git too old to report it, the longest of the repository's remote\n\t * names that {@link name} starts with, followed by `/` (a remote name can hold a slash). `undefined`, with\n\t * {@link branch}, when it names no known remote (a branch tracking another local branch, or a remote since removed)\n\t */\n\treadonly remote: string | undefined;\n\t/**\n\t * The branch on {@link remote} — git's name for it there, which a custom fetch refspec can make differ from the end\n\t * of {@link name}; `undefined` whenever {@link remote} is\n\t */\n\treadonly branch: string | undefined;\n\treadonly missing: boolean;\n}\n\nexport interface WorktreeSummaryBase {\n\t/** The ref the count was taken against (see `AheadOfBase.base`) */\n\treadonly ref: string;\n\treadonly ahead: number;\n\t/** The commit {@link ref} pointed at when the count was read; `undefined` when it couldn't be resolved */\n\treadonly sha: string | undefined;\n\t/** Where HEAD forked from {@link ref} (see `AheadOfBase.base`) */\n\treadonly forkPoint: string | undefined;\n\t/** The source the base came from (see `AheadOfBase.base`) */\n\treadonly tier: AheadOfBaseTier;\n}\n\nexport interface WorktreeSummary {\n\t/** The repository path in {@link getRepositoryKey} form */\n\treadonly repoPath: string;\n\t/** The checked-out branch; `undefined` when detached (or unborn with no name) */\n\treadonly branch: string | undefined;\n\treadonly detached: boolean;\n\t/** HEAD's commit; `undefined` when HEAD has no commit yet */\n\treadonly sha: string | undefined;\n\treadonly upstream: WorktreeSummaryUpstream | undefined;\n\t/** Commits ahead of the upstream; 0 without one */\n\treadonly ahead: number;\n\t/** Commits behind the upstream; 0 without one */\n\treadonly behind: number;\n\t/** Whether there is any staged, unstaged or untracked (non-ignored) change */\n\treadonly dirty: boolean;\n\t/**\n\t * How far the branch — or a detached HEAD, measured against the default branch — is past its base; `undefined` when\n\t * unsupported or there is no base\n\t */\n\treadonly base: WorktreeSummaryBase | undefined;\n\t/**\n\t * Full ref names whose movement can change {@link base}, as `getAheadOfBase` reports them (see\n\t * `AheadOfBase.dependsOn`) — given with or without a base, so a watcher also learns when one appears. Empty when\n\t * the base isn't read (an unborn HEAD with no name, or a provider without `getAheadOfBase`), and when the base read\n\t * failed with no earlier list for this HEAD (the first read, or the one after a checkout).\n\t */\n\treadonly baseDependsOn: readonly string[];\n\t/**\n\t * The latest recompute failed (worktree gone, lock contention); the other fields hold the last values read. Also set\n\t * when only the base read failed: the other fields are fresh, and `base` and `baseDependsOn` are their last values,\n\t * or `undefined` and empty if the branch changed.\n\t */\n\treadonly stale: boolean;\n}\n\nexport interface WorktreeSummarySubscribeOptions {\n\t/** Debounce for repo (`.git`) changes this subscriber asks of the watch session; the shortest across subscribers wins */\n\treadonly delayMs?: number;\n\t/** Cap on how long continuous repo activity can defer a recompute; `Infinity` means no cap */\n\treadonly maxWaitMs?: number;\n\t/** Debounce for working-tree changes this subscriber asks of the watch session */\n\treadonly workingTreeDelayMs?: number;\n\t/** Cap on how long continuous working-tree activity can defer a recompute; `Infinity` means no cap */\n\treadonly workingTreeMaxWaitMs?: number;\n}\n\nexport interface WorktreeSummarySubscription extends UnifiedDisposable {\n\t/** The latest summary; `undefined` until the first read succeeds, or after disposal */\n\treadonly current: WorktreeSummary | undefined;\n\t/** Fires when the summary's value changes */\n\treadonly onDidChange: Event<WorktreeSummary>;\n}\n\nexport interface WorktreeSummaryServiceOptions {\n\treadonly watchService: RepositoryWatchService;\n\treadonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\t/**\n\t * Fires as core writes a branch's merge target or base, before the store's promise resolves; the summary re-reads\n\t * that branch's stored values then, so a `getSummary` right after an awaited store sees the new base\n\t */\n\treadonly onDidChangeMergeTargets?: Event<MergeTargetsChangeEvent>;\n\t/**\n\t * Maps a worktree path to the repository common path that {@link onDidChangeMergeTargets} events carry; only that\n\t * repository's summaries react. Without it, or when it returns `undefined`, every summary reacts.\n\t */\n\treadonly getCommonPath?: (repoPath: string) => string | undefined;\n}\n\n/** Read groups, as bit flags so a pending set is a number rather than an allocation per event */\nconst dirtyField = 1;\nconst branchField = 2;\nconst baseField = 4;\n/** Re-reads only the stored values the base read consults, and the base itself only when they changed */\nconst baseKeysField = 8;\n/** Every group a full read covers — a base read refreshes the {@link BaseKeys} snapshot itself */\nconst allFields = dirtyField | branchField | baseField;\n\nconst emptyRefs: readonly string[] = Object.freeze([]);\n\n/**\n * The current branch's stored merge targets (user, detected) and recorded base, as `getAheadOfBase` consults them.\n * A detached HEAD has no stored values, so it holds {@link noBaseKeys}.\n */\ntype BaseKeys = readonly [string | undefined, string | undefined, string | undefined];\n\nconst noBaseKeys: BaseKeys = [undefined, undefined, undefined];\n\ninterface ReadResult {\n\treadonly summary: WorktreeSummary;\n\treadonly baseKeys: BaseKeys | undefined;\n\t/** Why the base read failed when the rest of the summary was read; the summary is then stale */\n\treadonly baseError: unknown;\n}\n\ninterface SummaryEntry {\n\treadonly key: string;\n\t/** Aborted when the last subscriber leaves, cancelling any in-flight read */\n\treadonly abort: AbortController;\n\treadonly subscribers: Set<SummarySubscription>;\n\thandle: WatchHandle | undefined;\n\t/** The timing-neutral listening subscriptions and their event listeners */\n\tlisteners: UnifiedDisposable[] | undefined;\n\tvalue: WorktreeSummary | undefined;\n\t/** The stored base values as of the last base read; a `gk` config change re-reads the base only when they differ */\n\tbaseKeys: BaseKeys | undefined;\n\t/** Why the latest read failed while there was no value to keep */\n\terror: Error | undefined;\n\t/** Read groups due on the next run */\n\tpending: number;\n\t/** The active run (or the start-up, which ends in one); cleared in the same turn the loop sees nothing pending */\n\trunning: Promise<void> | undefined;\n\tdisposed: boolean;\n}\n\nclass SummarySubscription implements WorktreeSummarySubscription {\n\tprivate readonly emitter = new Emitter<WorktreeSummary>();\n\t/** Listener-less session subscriptions that carry this subscriber's timing into the session */\n\tprivate timing: UnifiedDisposable[] | undefined;\n\tprivate disposed = false;\n\n\tconstructor(\n\t\tprivate readonly entry: SummaryEntry,\n\t\tprivate readonly options: WorktreeSummarySubscribeOptions | undefined,\n\t\tprivate readonly onDispose: (subscription: SummarySubscription) => void,\n\t) {}\n\n\tget current(): WorktreeSummary | undefined {\n\t\treturn this.disposed ? undefined : this.entry.value;\n\t}\n\n\tget onDidChange(): Event<WorktreeSummary> {\n\t\treturn this.emitter.event;\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tif (this.timing != null) {\n\t\t\tfor (const d of this.timing) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tthis.timing = undefined;\n\t\t}\n\t\tthis.emitter.dispose();\n\t\tthis.onDispose(this);\n\t}\n\n\tattach(session: RepositoryWatchSession): void {\n\t\tif (this.disposed || this.timing != null) return;\n\n\t\tthis.timing = [\n\t\t\tsession.subscribe({ delayMs: this.options?.delayMs, maxWaitMs: this.options?.maxWaitMs }),\n\t\t\tsession.subscribeToWorkingTree({\n\t\t\t\tdelayMs: this.options?.workingTreeDelayMs,\n\t\t\t\tmaxWaitMs: this.options?.workingTreeMaxWaitMs,\n\t\t\t}),\n\t\t];\n\t}\n\n\tfire(summary: WorktreeSummary): void {\n\t\tthis.emitter.fire(summary);\n\t}\n}\n\n/**\n * A cached, change-notifying summary of each subscribed worktree — its branch, upstream and ahead/behind, whether it\n * has working changes, and how far it is past its base — kept current by the repository watch layer.\n *\n * **Lifecycle.** Entries are per repository key and ref-counted across {@link subscribe} calls. The first subscriber\n * starts watching the worktree and reads everything; the last one to leave aborts any in-flight read, releases the\n * watch and drops the value. Each subscriber's timing options join the watch session's cadence (the shortest wins);\n * the service's own listening subscriptions never constrain it.\n *\n * **Field-scoped recompute.** The summary is read in three groups — `dirty` (working changes), `branch` (branch,\n * HEAD's commit, upstream, ahead/behind) and `base` (ahead of base, the base's tip, and the refs it depends on) — and\n * an event recomputes only the groups it can affect: a working-tree edit, an index or ignore-rule change reads only\n * `dirty`; the upstream moving reads only `branch`; a ref the base depends on reads only `base`; a `gk` config change\n * (mostly bookkeeping writes to the shared `.git/gk/config`), or core's own write of the branch's merge target or\n * base, re-reads the branch's stored merge targets and recorded base, and reads `base` only when one of them changed;\n * the checked-out branch moving, or a HEAD change, reads all of them. Stash, unrelated tags and branches, and the like\n * read nothing. One run is in flight per worktree at a time; events during a run are merged into a single follow-up\n * run.\n *\n * **Value-change-only notification.** {@link WorktreeSummarySubscription.onDidChange} and {@link onDidChangeSummary}\n * fire only when some field's value differs from the previous summary — a recompute that reads the same answer is\n * silent, so a consumer can re-render on every event without diffing. A failed recompute keeps the last values and\n * sets `stale` (firing once); the next successful one clears it.\n */\nexport class WorktreeSummaryService implements UnifiedDisposable {\n\tprivate readonly _onDidChangeSummary = new Emitter<WorktreeSummary>();\n\t/** Fires when the summary of any subscribed worktree changes value */\n\tget onDidChangeSummary(): Event<WorktreeSummary> {\n\t\treturn this._onDidChangeSummary.event;\n\t}\n\n\tprivate readonly entries = new Map<string, SummaryEntry>();\n\tprivate readonly watchService: RepositoryWatchService;\n\tprivate readonly getRepository: (repoPath: string) => RepositoryService | undefined;\n\tprivate readonly getCommonPath: ((repoPath: string) => string | undefined) | undefined;\n\tprivate readonly mergeTargetsListener: UnifiedDisposable | undefined;\n\tprivate _disposed = false;\n\n\tconstructor(options: WorktreeSummaryServiceOptions) {\n\t\tthis.watchService = options.watchService;\n\t\tthis.getRepository = options.getRepository;\n\t\tthis.getCommonPath = options.getCommonPath;\n\t\tthis.mergeTargetsListener = options.onDidChangeMergeTargets?.(e => this.onMergeTargetsChanged(e));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\t\tthis.mergeTargetsListener?.dispose();\n\t\tfor (const entry of [...this.entries.values()]) {\n\t\t\tfor (const subscription of [...entry.subscribers]) {\n\t\t\t\tsubscription.dispose();\n\t\t\t}\n\t\t}\n\t\tthis.entries.clear();\n\t\tthis._onDidChangeSummary.dispose();\n\t}\n\n\t/** Subscribes to a worktree's summary, starting to watch and read it if this is its first subscriber */\n\tsubscribe(repoPath: string, options?: WorktreeSummarySubscribeOptions): WorktreeSummarySubscription {\n\t\tconst key = getRepositoryKey(repoPath);\n\n\t\tlet entry = this._disposed ? undefined : this.entries.get(key);\n\t\tconst isNew = entry == null;\n\t\tif (entry == null) {\n\t\t\tentry = createEntry(key);\n\t\t\tif (this._disposed) {\n\t\t\t\tentry.disposed = true;\n\t\t\t} else {\n\t\t\t\tthis.entries.set(key, entry);\n\t\t\t}\n\t\t}\n\n\t\tconst target = entry;\n\t\tconst subscription = new SummarySubscription(target, options, s => this.release(target, s));\n\t\ttarget.subscribers.add(subscription);\n\n\t\tif (isNew) {\n\t\t\tif (!target.disposed) {\n\t\t\t\ttarget.pending = allFields;\n\t\t\t\ttarget.running = this.start(target);\n\t\t\t}\n\t\t} else if (target.handle != null) {\n\t\t\tsubscription.attach(target.handle.session);\n\t\t} else if (target.running == null && !target.disposed) {\n\t\t\t// The start failed (no repository or git directory yet); a new subscriber retries it\n\t\t\ttarget.pending = allFields;\n\t\t\ttarget.running = this.start(target);\n\t\t}\n\n\t\treturn subscription;\n\t}\n\n\t/**\n\t * Returns a worktree's summary. For a subscribed worktree, the cached value — after waiting for any run in flight\n\t * or pending; it rejects with the read's error when there is still no value. Otherwise a one-off full read that\n\t * is neither cached nor watched.\n\t */\n\tasync getSummary(repoPath: string, cancellation?: AbortSignal): Promise<WorktreeSummary> {\n\t\tconst key = getRepositoryKey(repoPath);\n\t\tconst entry = this.entries.get(key);\n\t\tif (entry == null) {\n\t\t\tconst repo = this.getRepository(key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${key}'`);\n\n\t\t\tconst result = await readSummary(repo, key, allFields, undefined, undefined, cancellation);\n\t\t\treturn result.summary;\n\t\t}\n\n\t\tif (entry.running != null) {\n\t\t\tawait (cancellation != null ? raceWithSignal(entry.running, cancellation) : entry.running);\n\t\t}\n\t\tif (entry.disposed) throw new CancellationError();\n\t\tif (entry.value != null) return entry.value;\n\n\t\tthrow entry.error ?? new Error(`No summary available for '${key}'`);\n\t}\n\n\tgetResourceUsage(): ResourceUsage {\n\t\treturn { 'worktreeSummary.entries.count': this.entries.size };\n\t}\n\n\tprivate async start(entry: SummaryEntry): Promise<void> {\n\t\tlet gitDir: GitDir | undefined;\n\t\ttry {\n\t\t\tgitDir = await this.getRepository(entry.key)?.config.getGitDir?.();\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed) return;\n\n\t\t\tentry.running = undefined;\n\t\t\tif (!isCancellationError(ex)) {\n\t\t\t\tthis.fail(entry, ex);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tconst handle = gitDir != null ? this.watchService.watch(entry.key, gitDir) : undefined;\n\t\tif (handle == null) {\n\t\t\tentry.running = undefined;\n\t\t\tthis.fail(entry, new Error(`Unable to watch '${entry.key}': no repository or git directory`));\n\t\t\treturn;\n\t\t}\n\n\t\tentry.handle = handle;\n\t\tconst session = handle.session;\n\t\t// Timing-neutral: the cadence comes from the subscribers' own session subscriptions (see `attach`)\n\t\tconst repoSub = session.subscribe({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tconst workingTreeSub = session.subscribeToWorkingTree({ delayMs: Infinity, maxWaitMs: Infinity });\n\t\tentry.listeners = [\n\t\t\trepoSub.onDidChange(e => this.onRepoChanged(entry, e)),\n\t\t\tworkingTreeSub.onDidChangeWorkingTree(() => this.queue(entry, dirtyField)),\n\t\t\trepoSub,\n\t\t\tworkingTreeSub,\n\t\t];\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.attach(session);\n\t\t}\n\n\t\tawait this.drain(entry);\n\t}\n\n\tprivate onRepoChanged(entry: SummaryEntry, e: WatcherRepoChangeEvent): void {\n\t\tconst value = entry.value;\n\t\tif (value == null || e.changed('unknown', 'head')) {\n\t\t\tthis.queue(entry, allFields);\n\t\t\treturn;\n\t\t}\n\n\t\tlet fields = 0;\n\t\tif (e.changed('index', 'ignores')) {\n\t\t\tfields |= dirtyField;\n\t\t}\n\t\tif (e.changed('config')) {\n\t\t\tfields |= branchField | baseField;\n\t\t}\n\t\tif (e.changed('gkConfig')) {\n\t\t\tfields |= baseKeysField;\n\t\t}\n\t\tif (value.branch != null && e.mayHaveChangedRef(`refs/heads/${value.branch}`)) {\n\t\t\tfields |= allFields;\n\t\t}\n\t\t// The upstream's short name doesn't say whether it's remote or local (`--track main`), so test both forms\n\t\tif (\n\t\t\tvalue.upstream != null &&\n\t\t\t(e.mayHaveChangedRef(`refs/remotes/${value.upstream.name}`) ||\n\t\t\t\te.mayHaveChangedRef(`refs/heads/${value.upstream.name}`))\n\t\t) {\n\t\t\tfields |= branchField;\n\t\t}\n\t\tif ((fields & baseField) === 0) {\n\t\t\tfor (const ref of value.baseDependsOn) {\n\t\t\t\tif (e.mayHaveChangedRef(ref)) {\n\t\t\t\t\tfields |= baseField;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tthis.queue(entry, fields);\n\t}\n\n\t/**\n\t * Queued synchronously, so a store awaited before `getSummary` finds the run in flight. The watcher's later\n\t * `gkConfig` event for the same write then finds the keys unchanged and leaves the base alone.\n\t */\n\tprivate onMergeTargetsChanged(e: MergeTargetsChangeEvent): void {\n\t\tfor (const entry of this.entries.values()) {\n\t\t\t// Before the watch starts there's nothing to refresh: the start's own read comes after the write\n\t\t\tif (entry.handle == null) continue;\n\n\t\t\t// With no value yet, the first read may have read the stored values before the write, so `queue` reads\n\t\t\t// everything again\n\t\t\tconst value = entry.value;\n\t\t\tif (value != null && (value.branch == null || (e.refs != null && !e.refs.has(value.branch)))) continue;\n\n\t\t\tconst commonPath = this.getCommonPath?.(entry.key);\n\t\t\tif (commonPath != null && commonPath !== e.repoPath) continue;\n\n\t\t\tthis.queue(entry, baseKeysField);\n\t\t}\n\t}\n\n\tprivate queue(entry: SummaryEntry, fields: number): void {\n\t\tif (entry.disposed || fields === 0) return;\n\n\t\t// Without a value a partial read has nothing to merge into, and a stale value's fields can't be trusted: a\n\t\t// partial read would clear `stale` while vouching for groups it never re-read\n\t\tentry.pending |= entry.value == null || entry.value.stale ? allFields : fields;\n\t\tentry.running ??= this.drain(entry);\n\t}\n\n\t/** Runs until nothing is pending. Never rejects — every failure becomes entry state. */\n\tprivate async drain(entry: SummaryEntry): Promise<void> {\n\t\twhile (entry.pending !== 0 && !entry.disposed) {\n\t\t\tconst fields = entry.pending;\n\t\t\tentry.pending = 0;\n\t\t\tawait this.run(entry, fields);\n\t\t}\n\t\t// Same turn as the loop's last check, so an event can't queue fields that no run will pick up\n\t\tentry.running = undefined;\n\t}\n\n\tprivate async run(entry: SummaryEntry, fields: number): Promise<void> {\n\t\tconst previous = entry.value;\n\n\t\tlet result: ReadResult;\n\t\ttry {\n\t\t\tconst repo = this.getRepository(entry.key);\n\t\t\tif (repo == null) throw new Error(`No repository found for '${entry.key}'`);\n\n\t\t\tresult = await readSummary(repo, entry.key, fields, previous, entry.baseKeys, entry.abort.signal);\n\t\t} catch (ex) {\n\t\t\tif (entry.disposed || isCancellationError(ex)) return;\n\n\t\t\tthis.fail(entry, ex);\n\t\t\treturn;\n\t\t}\n\t\tif (entry.disposed) return;\n\n\t\tif (result.summary.stale && (previous == null || !previous.stale)) {\n\t\t\tLogger.warn(\n\t\t\t\t`WorktreeSummaryService: reading the base of '${entry.key}' failed: ${String(result.baseError)}`,\n\t\t\t);\n\t\t}\n\n\t\tentry.error = undefined;\n\t\tentry.baseKeys = result.baseKeys;\n\t\tif (previous != null && summariesEqual(previous, result.summary)) return;\n\n\t\tentry.value = result.summary;\n\t\tthis.fireChange(entry, result.summary);\n\t}\n\n\tprivate fail(entry: SummaryEntry, ex: unknown): void {\n\t\tconst previous = entry.value;\n\t\tif (previous == null) {\n\t\t\tif (entry.error == null) {\n\t\t\t\tLogger.warn(`WorktreeSummaryService: reading '${entry.key}' failed: ${String(ex)}`);\n\t\t\t}\n\t\t\tentry.error = ex instanceof Error ? ex : new Error(String(ex));\n\t\t\treturn;\n\t\t}\n\t\tif (previous.stale) return;\n\n\t\tLogger.warn(`WorktreeSummaryService: refreshing '${entry.key}' failed: ${String(ex)}`);\n\t\tconst summary: WorktreeSummary = { ...previous, stale: true };\n\t\tentry.value = summary;\n\t\tthis.fireChange(entry, summary);\n\t}\n\n\tprivate fireChange(entry: SummaryEntry, summary: WorktreeSummary): void {\n\t\tfor (const subscription of entry.subscribers) {\n\t\t\tsubscription.fire(summary);\n\t\t}\n\t\tthis._onDidChangeSummary.fire(summary);\n\t}\n\n\tprivate release(entry: SummaryEntry, subscription: SummarySubscription): void {\n\t\tentry.subscribers.delete(subscription);\n\t\tif (entry.subscribers.size !== 0) return;\n\n\t\tentry.disposed = true;\n\t\tentry.abort.abort();\n\t\tif (entry.listeners != null) {\n\t\t\tfor (const d of entry.listeners) {\n\t\t\t\td.dispose();\n\t\t\t}\n\t\t\tentry.listeners = undefined;\n\t\t}\n\t\tentry.handle?.dispose();\n\t\tentry.handle = undefined;\n\t\tentry.value = undefined;\n\t\tentry.baseKeys = undefined;\n\t\tentry.pending = 0;\n\t\tentry.running = undefined;\n\n\t\tif (this.entries.get(entry.key) === entry) {\n\t\t\tthis.entries.delete(entry.key);\n\t\t}\n\t}\n}\n\nfunction createEntry(key: string): SummaryEntry {\n\treturn {\n\t\tkey: key,\n\t\tabort: new AbortController(),\n\t\tsubscribers: new Set(),\n\t\thandle: undefined,\n\t\tlisteners: undefined,\n\t\tvalue: undefined,\n\t\tbaseKeys: undefined,\n\t\terror: undefined,\n\t\tpending: 0,\n\t\trunning: undefined,\n\t\tdisposed: false,\n\t};\n}\n\n/**\n * Reads the due groups and fills the rest from `previous` (every group when there is none). The branch read comes\n * before the base read, which needs its name; the dirty read runs alongside both. A branch read that finds a\n * different branch (or detached state) also reads the base and dirty groups, and stored base values that differ\n * from `previousBaseKeys` read the base group. A failed base read doesn't fail the rest: the summary comes back stale,\n * keeping the previous base and `baseDependsOn` only while the branch is the same.\n */\nasync function readSummary(\n\trepo: RepositoryService,\n\tkey: string,\n\tfields: number,\n\tprevious: WorktreeSummary | undefined,\n\tpreviousBaseKeys: BaseKeys | undefined,\n\tsignal: AbortSignal | undefined,\n): Promise<ReadResult> {\n\tif (previous == null) {\n\t\tfields = allFields;\n\t}\n\n\tlet dirtyRead = fields & dirtyField ? readDirty(repo, signal) : undefined;\n\n\tlet branch = previous?.branch;\n\tlet detached = previous?.detached ?? false;\n\tlet sha = previous?.sha;\n\tlet upstream = previous?.upstream;\n\tlet ahead = previous?.ahead ?? 0;\n\tlet behind = previous?.behind ?? 0;\n\tif (fields & branchField) {\n\t\tconst current = await repo.branches.getBranch(undefined, undefined, signal);\n\t\tdetached = current?.detached ?? false;\n\t\tbranch = detached ? undefined : current?.name || undefined;\n\t\tsha = current?.sha;\n\t\tupstream = current?.upstream != null ? await toSummaryUpstream(repo, current.upstream, signal) : undefined;\n\t\tahead = current?.upstream?.state.ahead ?? 0;\n\t\tbehind = current?.upstream?.state.behind ?? 0;\n\n\t\tif (previous != null && (branch !== previous.branch || detached !== previous.detached)) {\n\t\t\tfields |= baseField;\n\t\t\tdirtyRead ??= readDirty(repo, signal);\n\t\t}\n\t}\n\n\tlet base = previous?.base;\n\tlet dependsOn = previous?.baseDependsOn ?? emptyRefs;\n\tlet baseKeys = previousBaseKeys;\n\tlet baseKeysRead = false;\n\tlet baseFailed = false;\n\tlet baseError: unknown;\n\t// A detached HEAD is measured as `undefined`; an unborn HEAD with no name has nothing to measure\n\tconst baseReadable = repo.branches.getAheadOfBase != null && (detached || branch != null);\n\tif (fields & baseKeysField && !(fields & baseField) && baseReadable) {\n\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\tbaseKeysRead = true;\n\t\tif (baseKeys == null || previousBaseKeys == null || !baseKeysEqual(baseKeys, previousBaseKeys)) {\n\t\t\tfields |= baseField;\n\t\t}\n\t}\n\tif (fields & baseField) {\n\t\tif (!baseReadable) {\n\t\t\tbase = undefined;\n\t\t\tdependsOn = emptyRefs;\n\t\t\tbaseKeys = undefined;\n\t\t} else {\n\t\t\t// Snapshot before the base read, so a write it misses is one the next `gk` config event sees as a change\n\t\t\tif (!baseKeysRead) {\n\t\t\t\tbaseKeys = await readBaseKeys(repo, branch);\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tconst aheadOfBase = await repo.branches.getAheadOfBase?.(branch, undefined, signal);\n\t\t\t\tbase = aheadOfBase?.base;\n\t\t\t\tdependsOn = aheadOfBase?.dependsOn ?? emptyRefs;\n\t\t\t} catch (ex) {\n\t\t\t\tif (isCancellationError(ex)) throw ex;\n\n\t\t\t\tbaseFailed = true;\n\t\t\t\tbaseError = ex;\n\t\t\t\t// Keep `baseDependsOn` for the same branch: a fetch moving one of its refs is what retries the base\n\t\t\t\tconst sameBranch = previous != null && branch === previous.branch && detached === previous.detached;\n\t\t\t\tbase = sameBranch ? previous.base : undefined;\n\t\t\t\tdependsOn = sameBranch ? previous.baseDependsOn : emptyRefs;\n\t\t\t\tbaseKeys = undefined;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst dirty = dirtyRead != null ? await dirtyRead : (previous?.dirty ?? false);\n\n\treturn {\n\t\tsummary: {\n\t\t\trepoPath: key,\n\t\t\tbranch: branch,\n\t\t\tdetached: detached,\n\t\t\tsha: sha,\n\t\t\tupstream: upstream,\n\t\t\tahead: ahead,\n\t\t\tbehind: behind,\n\t\t\tdirty: dirty,\n\t\t\tbase: base,\n\t\t\tbaseDependsOn: dependsOn,\n\t\t\tstale: baseFailed,\n\t\t},\n\t\tbaseKeys: baseKeys,\n\t\tbaseError: baseError,\n\t};\n}\n\n/**\n * Reads {@link BaseKeys} for `branch`, normalized the way `getAheadOfBase` reads them. `undefined` when any read\n * failed — an unknown snapshot, so the next `gk` config change reads the base rather than trusting it. A detached\n * HEAD (`branch` undefined) has no stored values to read.\n */\nasync function readBaseKeys(repo: RepositoryService, branch: string | undefined): Promise<BaseKeys | undefined> {\n\tif (branch == null) return noBaseKeys;\n\n\ttry {\n\t\tconst [user, detected, recorded] = await Promise.all([\n\t\t\trepo.branches.getStoredUserMergeTargetBranchName?.(branch),\n\t\t\trepo.branches.getStoredDetectedMergeTargetBranchName?.(branch),\n\t\t\trepo.config.getGkConfig?.(`branch.${branch}.gk-merge-base`),\n\t\t]);\n\t\treturn [user?.trim() || undefined, detected?.trim() || undefined, recorded?.trim() || undefined];\n\t} catch (ex) {\n\t\tif (isCancellationError(ex)) throw ex;\n\n\t\treturn undefined;\n\t}\n}\n\nfunction baseKeysEqual(a: BaseKeys, b: BaseKeys): boolean {\n\treturn a[0] === b[0] && a[1] === b[1] && a[2] === b[2];\n}\n\nfunction readDirty(repo: RepositoryService, signal: AbortSignal | undefined): Promise<boolean> {\n\tconst read = repo.status.hasWorkingChanges({ untracked: true }, signal);\n\t// Awaited only after the branch and base reads; if one of those throws first, this rejection has no other handler\n\tread.catch(noop);\n\treturn read;\n}\n\nfunction noop(): void {}\n\n/**\n * Uses the remote and branch git reported for the upstream; only when it reported neither does this split the name\n * at the longest remote name it starts with (no split when none matches or nothing follows it)\n */\nasync function toSummaryUpstream(\n\trepo: RepositoryService,\n\tupstream: GitTrackingUpstream,\n\tsignal: AbortSignal | undefined,\n): Promise<WorktreeSummaryUpstream> {\n\tconst { name, missing } = upstream;\n\n\tif (upstream.local) return { name: name, remote: undefined, branch: undefined, missing: missing };\n\n\tif (upstream.remote) {\n\t\tconst { remote } = upstream;\n\t\tconst branch =\n\t\t\tupstream.remoteBranch || (name.startsWith(`${remote}/`) ? name.substring(remote.length + 1) : undefined);\n\t\treturn { name: name, remote: branch ? remote : undefined, branch: branch || undefined, missing: missing };\n\t}\n\n\tlet remote: string | undefined;\n\tfor (const r of await repo.remotes.getRemotes(undefined, signal)) {\n\t\tif (r.name && name.startsWith(`${r.name}/`) && (remote == null || r.name.length > remote.length)) {\n\t\t\tremote = r.name;\n\t\t}\n\t}\n\n\tconst branch = remote != null ? name.substring(remote.length + 1) : undefined;\n\treturn {\n\t\tname: name,\n\t\tremote: branch ? remote : undefined,\n\t\tbranch: branch || undefined,\n\t\tmissing: missing,\n\t};\n}\n\nfunction summariesEqual(a: WorktreeSummary, b: WorktreeSummary): boolean {\n\treturn (\n\t\ta.repoPath === b.repoPath &&\n\t\ta.branch === b.branch &&\n\t\ta.detached === b.detached &&\n\t\ta.sha === b.sha &&\n\t\ta.ahead === b.ahead &&\n\t\ta.behind === b.behind &&\n\t\ta.dirty === b.dirty &&\n\t\ta.stale === b.stale &&\n\t\tareEqual(a.baseDependsOn, b.baseDependsOn) &&\n\t\t(a.upstream === b.upstream ||\n\t\t\t(a.upstream != null &&\n\t\t\t\tb.upstream != null &&\n\t\t\t\ta.upstream.name === b.upstream.name &&\n\t\t\t\ta.upstream.remote === b.upstream.remote &&\n\t\t\t\ta.upstream.branch === b.upstream.branch &&\n\t\t\t\ta.upstream.missing === b.upstream.missing)) &&\n\t\t(a.base === b.base ||\n\t\t\t(a.base != null &&\n\t\t\t\tb.base != null &&\n\t\t\t\ta.base.ref === b.base.ref &&\n\t\t\t\ta.base.ahead === b.base.ahead &&\n\t\t\t\ta.base.sha === b.base.sha &&\n\t\t\t\ta.base.forkPoint === b.base.forkPoint &&\n\t\t\t\ta.base.tier === b.base.tier))\n\t);\n}\n"],"mappings":";;;;;;;AAmHA,MAAM,aAAa;AACnB,MAAM,cAAc;AACpB,MAAM,YAAY;;AAElB,MAAM,gBAAgB;;AAEtB,MAAM,YAAY;AAElB,MAAM,YAA+B,OAAO,OAAO,CAAC,CAAC;AAQrD,MAAM,aAAuB;CAAC,KAAA;CAAW,KAAA;CAAW,KAAA;AAAS;AA6B7D,IAAM,sBAAN,MAAiE;CAO9C;CACA;CACA;CARlB,UAA2B,IAAI,QAAyB;;CAExD;CACA,WAAmB;CAEnB,YACC,OACA,SACA,WACC;EAHgB,KAAA,QAAA;EACA,KAAA,UAAA;EACA,KAAA,YAAA;CACf;CAEH,IAAI,UAAuC;EAC1C,OAAO,KAAK,WAAW,KAAA,IAAY,KAAK,MAAM;CAC/C;CAEA,IAAI,cAAsC;EACzC,OAAO,KAAK,QAAQ;CACrB;CAEA,CAAC,OAAO,WAAiB;EACxB,KAAK,QAAQ;CACd;CAEA,UAAgB;EACf,IAAI,KAAK,UAAU;EAEnB,KAAK,WAAW;EAChB,IAAI,KAAK,UAAU,MAAM;GACxB,KAAK,MAAM,KAAK,KAAK,QACpB,EAAE,QAAQ;GAEX,KAAK,SAAS,KAAA;EACf;EACA,KAAK,QAAQ,QAAQ;EACrB,KAAK,UAAU,IAAI;CACpB;CAEA,OAAO,SAAuC;EAC7C,IAAI,KAAK,YAAY,KAAK,UAAU,MAAM;EAE1C,KAAK,SAAS,CACb,QAAQ,UAAU;GAAE,SAAS,KAAK,SAAS;GAAS,WAAW,KAAK,SAAS;EAAU,CAAC,GACxF,QAAQ,uBAAuB;GAC9B,SAAS,KAAK,SAAS;GACvB,WAAW,KAAK,SAAS;EAC1B,CAAC,CACF;CACD;CAEA,KAAK,SAAgC;EACpC,KAAK,QAAQ,KAAK,OAAO;CAC1B;AACD;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,IAAa,yBAAb,MAAiE;CAChE,sBAAuC,IAAI,QAAyB;;CAEpE,IAAI,qBAA6C;EAChD,OAAO,KAAK,oBAAoB;CACjC;CAEA,0BAA2B,IAAI,IAA0B;CACzD;CACA;CACA;CACA;CACA,YAAoB;CAEpB,YAAY,SAAwC;EACnD,KAAK,eAAe,QAAQ;EAC5B,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,gBAAgB,QAAQ;EAC7B,KAAK,uBAAuB,QAAQ,2BAA0B,MAAK,KAAK,sBAAsB,CAAC,CAAC;CACjG;CAEA,CAAC,OAAO,WAAiB;EACxB,KAAK,QAAQ;CACd;CAEA,UAAgB;EACf,IAAI,KAAK,WAAW;EAEpB,KAAK,YAAY;EACjB,KAAK,sBAAsB,QAAQ;EACnC,KAAK,MAAM,SAAS,CAAC,GAAG,KAAK,QAAQ,OAAO,CAAC,GAC5C,KAAK,MAAM,gBAAgB,CAAC,GAAG,MAAM,WAAW,GAC/C,aAAa,QAAQ;EAGvB,KAAK,QAAQ,MAAM;EACnB,KAAK,oBAAoB,QAAQ;CAClC;;CAGA,UAAU,UAAkB,SAAwE;EACnG,MAAM,MAAM,iBAAiB,QAAQ;EAErC,IAAI,QAAQ,KAAK,YAAY,KAAA,IAAY,KAAK,QAAQ,IAAI,GAAG;EAC7D,MAAM,QAAQ,SAAS;EACvB,IAAI,SAAS,MAAM;GAClB,QAAQ,YAAY,GAAG;GACvB,IAAI,KAAK,WACR,MAAM,WAAW;QAEjB,KAAK,QAAQ,IAAI,KAAK,KAAK;EAE7B;EAEA,MAAM,SAAS;EACf,MAAM,eAAe,IAAI,oBAAoB,QAAQ,UAAS,MAAK,KAAK,QAAQ,QAAQ,CAAC,CAAC;EAC1F,OAAO,YAAY,IAAI,YAAY;EAEnC,IAAI,OACC;OAAA,CAAC,OAAO,UAAU;IACrB,OAAO,UAAU;IACjB,OAAO,UAAU,KAAK,MAAM,MAAM;GACnC;SACM,IAAI,OAAO,UAAU,MAC3B,aAAa,OAAO,OAAO,OAAO,OAAO;OACnC,IAAI,OAAO,WAAW,QAAQ,CAAC,OAAO,UAAU;GAEtD,OAAO,UAAU;GACjB,OAAO,UAAU,KAAK,MAAM,MAAM;EACnC;EAEA,OAAO;CACR;;;;;;CAOA,MAAM,WAAW,UAAkB,cAAsD;EACxF,MAAM,MAAM,iBAAiB,QAAQ;EACrC,MAAM,QAAQ,KAAK,QAAQ,IAAI,GAAG;EAClC,IAAI,SAAS,MAAM;GAClB,MAAM,OAAO,KAAK,cAAc,GAAG;GACnC,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,4BAA4B,IAAI,EAAE;GAGpE,QAAO,MADc,YAAY,MAAM,KAAK,WAAW,KAAA,GAAW,KAAA,GAAW,YAAY,EAAA,CAC3E;EACf;EAEA,IAAI,MAAM,WAAW,MACpB,OAAO,gBAAgB,OAAO,eAAe,MAAM,SAAS,YAAY,IAAI,MAAM;EAEnF,IAAI,MAAM,UAAU,MAAM,IAAI,kBAAkB;EAChD,IAAI,MAAM,SAAS,MAAM,OAAO,MAAM;EAEtC,MAAM,MAAM,yBAAS,IAAI,MAAM,6BAA6B,IAAI,EAAE;CACnE;CAEA,mBAAkC;EACjC,OAAO,EAAE,iCAAiC,KAAK,QAAQ,KAAK;CAC7D;CAEA,MAAc,MAAM,OAAoC;EACvD,IAAI;EACJ,IAAI;GACH,SAAS,MAAM,KAAK,cAAc,MAAM,GAAG,CAAC,EAAE,OAAO,YAAY;EAClE,SAAS,IAAI;GACZ,IAAI,MAAM,UAAU;GAEpB,MAAM,UAAU,KAAA;GAChB,IAAI,CAAC,oBAAoB,EAAE,GAC1B,KAAK,KAAK,OAAO,EAAE;GAEpB;EACD;EACA,IAAI,MAAM,UAAU;EAEpB,MAAM,SAAS,UAAU,OAAO,KAAK,aAAa,MAAM,MAAM,KAAK,MAAM,IAAI,KAAA;EAC7E,IAAI,UAAU,MAAM;GACnB,MAAM,UAAU,KAAA;GAChB,KAAK,KAAK,uBAAO,IAAI,MAAM,oBAAoB,MAAM,IAAI,kCAAkC,CAAC;GAC5F;EACD;EAEA,MAAM,SAAS;EACf,MAAM,UAAU,OAAO;EAEvB,MAAM,UAAU,QAAQ,UAAU;GAAE,SAAS;GAAU,WAAW;EAAS,CAAC;EAC5E,MAAM,iBAAiB,QAAQ,uBAAuB;GAAE,SAAS;GAAU,WAAW;EAAS,CAAC;EAChG,MAAM,YAAY;GACjB,QAAQ,aAAY,MAAK,KAAK,cAAc,OAAO,CAAC,CAAC;GACrD,eAAe,6BAA6B,KAAK,MAAM,OAAO,UAAU,CAAC;GACzE;GACA;EACD;EACA,KAAK,MAAM,gBAAgB,MAAM,aAChC,aAAa,OAAO,OAAO;EAG5B,MAAM,KAAK,MAAM,KAAK;CACvB;CAEA,cAAsB,OAAqB,GAAiC;EAC3E,MAAM,QAAQ,MAAM;EACpB,IAAI,SAAS,QAAQ,EAAE,QAAQ,WAAW,MAAM,GAAG;GAClD,KAAK,MAAM,OAAO,SAAS;GAC3B;EACD;EAEA,IAAI,SAAS;EACb,IAAI,EAAE,QAAQ,SAAS,SAAS,GAC/B,UAAU;EAEX,IAAI,EAAE,QAAQ,QAAQ,GACrB,UAAU;EAEX,IAAI,EAAE,QAAQ,UAAU,GACvB,UAAU;EAEX,IAAI,MAAM,UAAU,QAAQ,EAAE,kBAAkB,cAAc,MAAM,QAAQ,GAC3E,UAAU;EAGX,IACC,MAAM,YAAY,SACjB,EAAE,kBAAkB,gBAAgB,MAAM,SAAS,MAAM,KACzD,EAAE,kBAAkB,cAAc,MAAM,SAAS,MAAM,IAExD,UAAU;EAEX,KAAK,SAAS,eAAe,GACvB;QAAA,MAAM,OAAO,MAAM,eACvB,IAAI,EAAE,kBAAkB,GAAG,GAAG;IAC7B,UAAU;IACV;GACD;;EAIF,KAAK,MAAM,OAAO,MAAM;CACzB;;;;;CAMA,sBAA8B,GAAkC;EAC/D,KAAK,MAAM,SAAS,KAAK,QAAQ,OAAO,GAAG;GAE1C,IAAI,MAAM,UAAU,MAAM;GAI1B,MAAM,QAAQ,MAAM;GACpB,IAAI,SAAS,SAAS,MAAM,UAAU,QAAS,EAAE,QAAQ,QAAQ,CAAC,EAAE,KAAK,IAAI,MAAM,MAAM,IAAK;GAE9F,MAAM,aAAa,KAAK,gBAAgB,MAAM,GAAG;GACjD,IAAI,cAAc,QAAQ,eAAe,EAAE,UAAU;GAErD,KAAK,MAAM,OAAO,aAAa;EAChC;CACD;CAEA,MAAc,OAAqB,QAAsB;EACxD,IAAI,MAAM,YAAY,WAAW,GAAG;EAIpC,MAAM,WAAW,MAAM,SAAS,QAAQ,MAAM,MAAM,QAAQ,YAAY;EACxE,MAAM,YAAY,KAAK,MAAM,KAAK;CACnC;;CAGA,MAAc,MAAM,OAAoC;EACvD,OAAO,MAAM,YAAY,KAAK,CAAC,MAAM,UAAU;GAC9C,MAAM,SAAS,MAAM;GACrB,MAAM,UAAU;GAChB,MAAM,KAAK,IAAI,OAAO,MAAM;EAC7B;EAEA,MAAM,UAAU,KAAA;CACjB;CAEA,MAAc,IAAI,OAAqB,QAA+B;EACrE,MAAM,WAAW,MAAM;EAEvB,IAAI;EACJ,IAAI;GACH,MAAM,OAAO,KAAK,cAAc,MAAM,GAAG;GACzC,IAAI,QAAQ,MAAM,MAAM,IAAI,MAAM,4BAA4B,MAAM,IAAI,EAAE;GAE1E,SAAS,MAAM,YAAY,MAAM,MAAM,KAAK,QAAQ,UAAU,MAAM,UAAU,MAAM,MAAM,MAAM;EACjG,SAAS,IAAI;GACZ,IAAI,MAAM,YAAY,oBAAoB,EAAE,GAAG;GAE/C,KAAK,KAAK,OAAO,EAAE;GACnB;EACD;EACA,IAAI,MAAM,UAAU;EAEpB,IAAI,OAAO,QAAQ,UAAU,YAAY,QAAQ,CAAC,SAAS,QAC1D,QAAO,KACN,gDAAgD,MAAM,IAAI,YAAY,OAAO,OAAO,SAAS,GAC9F;EAGD,MAAM,QAAQ,KAAA;EACd,MAAM,WAAW,OAAO;EACxB,IAAI,YAAY,QAAQ,eAAe,UAAU,OAAO,OAAO,GAAG;EAElE,MAAM,QAAQ,OAAO;EACrB,KAAK,WAAW,OAAO,OAAO,OAAO;CACtC;CAEA,KAAa,OAAqB,IAAmB;EACpD,MAAM,WAAW,MAAM;EACvB,IAAI,YAAY,MAAM;GACrB,IAAI,MAAM,SAAS,MAClB,QAAO,KAAK,oCAAoC,MAAM,IAAI,YAAY,OAAO,EAAE,GAAG;GAEnF,MAAM,QAAQ,cAAc,QAAQ,KAAK,IAAI,MAAM,OAAO,EAAE,CAAC;GAC7D;EACD;EACA,IAAI,SAAS,OAAO;EAEpB,QAAO,KAAK,uCAAuC,MAAM,IAAI,YAAY,OAAO,EAAE,GAAG;EACrF,MAAM,UAA2B;GAAE,GAAG;GAAU,OAAO;EAAK;EAC5D,MAAM,QAAQ;EACd,KAAK,WAAW,OAAO,OAAO;CAC/B;CAEA,WAAmB,OAAqB,SAAgC;EACvE,KAAK,MAAM,gBAAgB,MAAM,aAChC,aAAa,KAAK,OAAO;EAE1B,KAAK,oBAAoB,KAAK,OAAO;CACtC;CAEA,QAAgB,OAAqB,cAAyC;EAC7E,MAAM,YAAY,OAAO,YAAY;EACrC,IAAI,MAAM,YAAY,SAAS,GAAG;EAElC,MAAM,WAAW;EACjB,MAAM,MAAM,MAAM;EAClB,IAAI,MAAM,aAAa,MAAM;GAC5B,KAAK,MAAM,KAAK,MAAM,WACrB,EAAE,QAAQ;GAEX,MAAM,YAAY,KAAA;EACnB;EACA,MAAM,QAAQ,QAAQ;EACtB,MAAM,SAAS,KAAA;EACf,MAAM,QAAQ,KAAA;EACd,MAAM,WAAW,KAAA;EACjB,MAAM,UAAU;EAChB,MAAM,UAAU,KAAA;EAEhB,IAAI,KAAK,QAAQ,IAAI,MAAM,GAAG,MAAM,OACnC,KAAK,QAAQ,OAAO,MAAM,GAAG;CAE/B;AACD;AAEA,SAAS,YAAY,KAA2B;CAC/C,OAAO;EACD;EACL,OAAO,IAAI,gBAAgB;EAC3B,6BAAa,IAAI,IAAI;EACrB,QAAQ,KAAA;EACR,WAAW,KAAA;EACX,OAAO,KAAA;EACP,UAAU,KAAA;EACV,OAAO,KAAA;EACP,SAAS;EACT,SAAS,KAAA;EACT,UAAU;CACX;AACD;;;;;;;;AASA,eAAe,YACd,MACA,KACA,QACA,UACA,kBACA,QACsB;CACtB,IAAI,YAAY,MACf,SAAS;CAGV,IAAI,YAAY,SAAS,aAAa,UAAU,MAAM,MAAM,IAAI,KAAA;CAEhE,IAAI,SAAS,UAAU;CACvB,IAAI,WAAW,UAAU,YAAY;CACrC,IAAI,MAAM,UAAU;CACpB,IAAI,WAAW,UAAU;CACzB,IAAI,QAAQ,UAAU,SAAS;CAC/B,IAAI,SAAS,UAAU,UAAU;CACjC,IAAI,SAAS,aAAa;EACzB,MAAM,UAAU,MAAM,KAAK,SAAS,UAAU,KAAA,GAAW,KAAA,GAAW,MAAM;EAC1E,WAAW,SAAS,YAAY;EAChC,SAAS,WAAW,KAAA,IAAY,SAAS,QAAQ,KAAA;EACjD,MAAM,SAAS;EACf,WAAW,SAAS,YAAY,OAAO,MAAM,kBAAkB,MAAM,QAAQ,UAAU,MAAM,IAAI,KAAA;EACjG,QAAQ,SAAS,UAAU,MAAM,SAAS;EAC1C,SAAS,SAAS,UAAU,MAAM,UAAU;EAE5C,IAAI,YAAY,SAAS,WAAW,SAAS,UAAU,aAAa,SAAS,WAAW;GACvF,UAAU;GACV,cAAc,UAAU,MAAM,MAAM;EACrC;CACD;CAEA,IAAI,OAAO,UAAU;CACrB,IAAI,YAAY,UAAU,iBAAiB;CAC3C,IAAI,WAAW;CACf,IAAI,eAAe;CACnB,IAAI,aAAa;CACjB,IAAI;CAEJ,MAAM,eAAe,KAAK,SAAS,kBAAkB,SAAS,YAAY,UAAU;CACpF,IAAI,SAAS,iBAAiB,EAAE,SAAS,cAAc,cAAc;EACpE,WAAW,MAAM,aAAa,MAAM,MAAM;EAC1C,eAAe;EACf,IAAI,YAAY,QAAQ,oBAAoB,QAAQ,CAAC,cAAc,UAAU,gBAAgB,GAC5F,UAAU;CAEZ;CACA,IAAI,SAAS,WAAW;EACvB,IAAI,CAAC,cAAc;GAClB,OAAO,KAAA;GACP,YAAY;GACZ,WAAW,KAAA;EACZ,OAAO;GAEN,IAAI,CAAC,cACJ,WAAW,MAAM,aAAa,MAAM,MAAM;GAE3C,IAAI;IACH,MAAM,cAAc,MAAM,KAAK,SAAS,iBAAiB,QAAQ,KAAA,GAAW,MAAM;IAClF,OAAO,aAAa;IACpB,YAAY,aAAa,aAAa;GACvC,SAAS,IAAI;IACZ,IAAI,oBAAoB,EAAE,GAAG,MAAM;IAEnC,aAAa;IACb,YAAY;IAEZ,MAAM,aAAa,YAAY,QAAQ,WAAW,SAAS,UAAU,aAAa,SAAS;IAC3F,OAAO,aAAa,SAAS,OAAO,KAAA;IACpC,YAAY,aAAa,SAAS,gBAAgB;IAClD,WAAW,KAAA;GACZ;EACD;CACD;CAEA,MAAM,QAAQ,aAAa,OAAO,MAAM,YAAa,UAAU,SAAS;CAExE,OAAO;EACN,SAAS;GACR,UAAU;GACF;GACE;GACL;GACK;GACH;GACC;GACD;GACD;GACN,eAAe;GACf,OAAO;EACR;EACU;EACC;CACZ;AACD;;;;;;AAOA,eAAe,aAAa,MAAyB,QAA2D;CAC/G,IAAI,UAAU,MAAM,OAAO;CAE3B,IAAI;EACH,MAAM,CAAC,MAAM,UAAU,YAAY,MAAM,QAAQ,IAAI;GACpD,KAAK,SAAS,qCAAqC,MAAM;GACzD,KAAK,SAAS,yCAAyC,MAAM;GAC7D,KAAK,OAAO,cAAc,UAAU,OAAO,eAAe;EAC3D,CAAC;EACD,OAAO;GAAC,MAAM,KAAK,KAAK,KAAA;GAAW,UAAU,KAAK,KAAK,KAAA;GAAW,UAAU,KAAK,KAAK,KAAA;EAAS;CAChG,SAAS,IAAI;EACZ,IAAI,oBAAoB,EAAE,GAAG,MAAM;EAEnC;CACD;AACD;AAEA,SAAS,cAAc,GAAa,GAAsB;CACzD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;AACrD;AAEA,SAAS,UAAU,MAAyB,QAAmD;CAC9F,MAAM,OAAO,KAAK,OAAO,kBAAkB,EAAE,WAAW,KAAK,GAAG,MAAM;CAEtE,KAAK,MAAM,IAAI;CACf,OAAO;AACR;AAEA,SAAS,OAAa,CAAC;;;;;AAMvB,eAAe,kBACd,MACA,UACA,QACmC;CACnC,MAAM,EAAE,MAAM,YAAY;CAE1B,IAAI,SAAS,OAAO,OAAO;EAAQ;EAAM,QAAQ,KAAA;EAAW,QAAQ,KAAA;EAAoB;CAAQ;CAEhG,IAAI,SAAS,QAAQ;EACpB,MAAM,EAAE,WAAW;EACnB,MAAM,SACL,SAAS,iBAAiB,KAAK,WAAW,GAAG,OAAO,EAAE,IAAI,KAAK,UAAU,OAAO,SAAS,CAAC,IAAI,KAAA;EAC/F,OAAO;GAAQ;GAAM,QAAQ,SAAS,SAAS,KAAA;GAAW,QAAQ,UAAU,KAAA;GAAoB;EAAQ;CACzG;CAEA,IAAI;CACJ,KAAK,MAAM,KAAK,MAAM,KAAK,QAAQ,WAAW,KAAA,GAAW,MAAM,GAC9D,IAAI,EAAE,QAAQ,KAAK,WAAW,GAAG,EAAE,KAAK,EAAE,MAAM,UAAU,QAAQ,EAAE,KAAK,SAAS,OAAO,SACxF,SAAS,EAAE;CAIb,MAAM,SAAS,UAAU,OAAO,KAAK,UAAU,OAAO,SAAS,CAAC,IAAI,KAAA;CACpE,OAAO;EACA;EACN,QAAQ,SAAS,SAAS,KAAA;EAC1B,QAAQ,UAAU,KAAA;EACT;CACV;AACD;AAEA,SAAS,eAAe,GAAoB,GAA6B;CACxE,OACC,EAAE,aAAa,EAAE,YACjB,EAAE,WAAW,EAAE,UACf,EAAE,aAAa,EAAE,YACjB,EAAE,QAAQ,EAAE,OACZ,EAAE,UAAU,EAAE,SACd,EAAE,WAAW,EAAE,UACf,EAAE,UAAU,EAAE,SACd,EAAE,UAAU,EAAE,SACd,SAAS,EAAE,eAAe,EAAE,aAAa,MACxC,EAAE,aAAa,EAAE,YAChB,EAAE,YAAY,QACd,EAAE,YAAY,QACd,EAAE,SAAS,SAAS,EAAE,SAAS,QAC/B,EAAE,SAAS,WAAW,EAAE,SAAS,UACjC,EAAE,SAAS,WAAW,EAAE,SAAS,UACjC,EAAE,SAAS,YAAY,EAAE,SAAS,aACnC,EAAE,SAAS,EAAE,QACZ,EAAE,QAAQ,QACV,EAAE,QAAQ,QACV,EAAE,KAAK,QAAQ,EAAE,KAAK,OACtB,EAAE,KAAK,UAAU,EAAE,KAAK,SACxB,EAAE,KAAK,QAAQ,EAAE,KAAK,OACtB,EAAE,KAAK,cAAc,EAAE,KAAK,aAC5B,EAAE,KAAK,SAAS,EAAE,KAAK;AAE3B"}
@@ -215,6 +215,12 @@ export declare class CliGitProvider implements GitProvider {
215
215
  paths?: readonly string[];
216
216
  priority?: GitCommandPriority;
217
217
  }, cancellation?: AbortSignal): Promise<string[]>;
218
+ /**
219
+ * Which of `scopes` are ignored. `check-ignore` dies on a path inside a submodule or beyond a symlink, where this
220
+ * repository ignores nothing, so a failed batch is bisected, one half at a time, and a scope that fails alone is
221
+ * not ignored. That costs about 2·k·log2(N) runs for k bad scopes, rather than a burst of N at once.
222
+ */
223
+ private getIgnoredScopes;
218
224
  getIgnoredPaths(repoPath: string, paths: readonly string[], options?: {
219
225
  priority?: GitCommandPriority;
220
226
  }, cancellation?: AbortSignal): Promise<Set<string>>;
@@ -1,3 +1,4 @@
1
+ import { isCancellationError } from "../utils/cancellation.js";
1
2
  import { iterateByDelimiter } from "../utils/string.js";
2
3
  import { getScopedLogger } from "../utils/logger.scoped.js";
3
4
  import { Emitter } from "../utils/event.js";
@@ -463,8 +464,22 @@ var CliGitProvider = class {
463
464
  return [...kept];
464
465
  }
465
466
  async getIgnoredEntries(repoPath, options, cancellation) {
466
- const paths = options?.paths;
467
+ let paths = options?.paths;
467
468
  if (paths != null && !paths.length) return [];
469
+ const scopedEntries = /* @__PURE__ */ new Set();
470
+ if (paths != null) {
471
+ const scopes = [...new Set(paths.map((p) => p.length > 1 ? p.replace(/\/+$/, "") : p))];
472
+ const ignoredScopes = await this.getIgnoredScopes(repoPath, scopes.filter((p) => p !== ""), options?.priority, cancellation);
473
+ const ignored = [...ignoredScopes];
474
+ const listed = await Promise.allSettled(ignored.map(async (scope) => {
475
+ const fullPath = path.join(repoPath, scope);
476
+ if (!(await promises.lstat(fullPath)).isDirectory()) return scope;
477
+ return await hasNonDirectoryEntry(fullPath) ? `${scope}/` : void 0;
478
+ }));
479
+ for (const result of listed) if (result.status === "fulfilled" && result.value != null) scopedEntries.add(result.value);
480
+ paths = scopes.filter((p) => !ignoredScopes.has(p));
481
+ if (!paths.length) return [...scopedEntries].sort();
482
+ }
468
483
  const args = [
469
484
  "ls-files",
470
485
  "-z",
@@ -491,7 +506,7 @@ var CliGitProvider = class {
491
506
  const [ignoredResult, untrackedResult] = await Promise.allSettled([readListing(["--ignored"]), readListing([])]);
492
507
  if (ignoredResult.status === "rejected") throw ignoredResult.reason;
493
508
  if (untrackedResult.status === "rejected") throw untrackedResult.reason;
494
- const entries = /* @__PURE__ */ new Set();
509
+ const entries = new Set(scopedEntries);
495
510
  for (const entry of ignoredResult.value) if (entry !== ".git/") entries.add(entry);
496
511
  const folders = [];
497
512
  for (const entry of untrackedResult.value) if (entry.endsWith("/")) folders.push(entry);
@@ -522,6 +537,23 @@ var CliGitProvider = class {
522
537
  if (matching) for (const entry of foldMatchingIgnored(records)) entries.add(entry);
523
538
  return [...entries].sort();
524
539
  }
540
+ /**
541
+ * Which of `scopes` are ignored. `check-ignore` dies on a path inside a submodule or beyond a symlink, where this
542
+ * repository ignores nothing, so a failed batch is bisected, one half at a time, and a scope that fails alone is
543
+ * not ignored. That costs about 2·k·log2(N) runs for k bad scopes, rather than a burst of N at once.
544
+ */
545
+ async getIgnoredScopes(repoPath, scopes, priority, cancellation) {
546
+ try {
547
+ return await this.getIgnoredPaths(repoPath, scopes, { priority }, cancellation);
548
+ } catch (ex) {
549
+ if (isCancellationError(ex)) throw ex;
550
+ if (scopes.length < 2) return /* @__PURE__ */ new Set();
551
+ }
552
+ const middle = scopes.length >> 1;
553
+ const ignored = await this.getIgnoredScopes(repoPath, scopes.slice(0, middle), priority, cancellation);
554
+ for (const scope of await this.getIgnoredScopes(repoPath, scopes.slice(middle), priority, cancellation)) ignored.add(scope);
555
+ return ignored;
556
+ }
525
557
  async getIgnoredPaths(repoPath, paths, options, cancellation) {
526
558
  if (!paths.length) return /* @__PURE__ */ new Set();
527
559
  let prefixed;
@@ -604,6 +636,20 @@ function getDefaultGlobalExcludesPath() {
604
636
  const home = env.HOME || homedir();
605
637
  return home ? path.join(home, ".config", "git", "ignore") : void 0;
606
638
  }
639
+ /** Whether a folder holds a file or symlink anywhere below it, as git lists no folder without one; an unreadable folder holds none */
640
+ async function hasNonDirectoryEntry(folder) {
641
+ const pending = [folder];
642
+ while (pending.length) {
643
+ const current = pending.pop();
644
+ try {
645
+ for (const entry of await promises.readdir(current, { withFileTypes: true })) {
646
+ if (!entry.isDirectory()) return true;
647
+ pending.push(path.join(current, entry.name));
648
+ }
649
+ } catch {}
650
+ }
651
+ return false;
652
+ }
607
653
  //#endregion
608
654
  export { CliGitProvider };
609
655