@gitkraken/core-gitlens 0.15.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/git/models/branch.d.ts +7 -0
  3. package/dist/git/models/branch.js +1 -0
  4. package/dist/git/models/branch.js.map +1 -1
  5. package/dist/git/models/graph.d.ts +2 -0
  6. package/dist/git/models/graph.js.map +1 -1
  7. package/dist/git/models/reference.d.ts +4 -0
  8. package/dist/git/providers/provider.d.ts +3 -1
  9. package/dist/git/providers/revision.d.ts +2 -1
  10. package/dist/git/run.types.d.ts +3 -2
  11. package/dist/git/utils/branch.utils.d.ts +15 -3
  12. package/dist/git/utils/branch.utils.js +34 -8
  13. package/dist/git/utils/branch.utils.js.map +1 -1
  14. package/dist/git/utils/reference.utils.d.ts +2 -0
  15. package/dist/git/utils/reference.utils.js.map +1 -1
  16. package/dist/git/watching/worktreeSummary.d.ts +7 -4
  17. package/dist/git/watching/worktreeSummary.js +22 -3
  18. package/dist/git/watching/worktreeSummary.js.map +1 -1
  19. package/dist/git-cli/cliGitProvider.d.ts +6 -0
  20. package/dist/git-cli/cliGitProvider.js +48 -2
  21. package/dist/git-cli/cliGitProvider.js.map +1 -1
  22. package/dist/git-cli/exec/git.d.ts +1 -0
  23. package/dist/git-cli/exec/git.js +8 -10
  24. package/dist/git-cli/exec/git.js.map +1 -1
  25. package/dist/git-cli/exec/indexLock.d.ts +9 -1
  26. package/dist/git-cli/exec/indexLock.js +19 -5
  27. package/dist/git-cli/exec/indexLock.js.map +1 -1
  28. package/dist/git-cli/parsers/logParser.d.ts +4 -0
  29. package/dist/git-cli/parsers/logParser.js +12 -2
  30. package/dist/git-cli/parsers/logParser.js.map +1 -1
  31. package/dist/git-cli/parsers/refParser.js +4 -2
  32. package/dist/git-cli/parsers/refParser.js.map +1 -1
  33. package/dist/git-cli/providers/branchRemote.d.ts +13 -1
  34. package/dist/git-cli/providers/branchRemote.js +41 -12
  35. package/dist/git-cli/providers/branchRemote.js.map +1 -1
  36. package/dist/git-cli/providers/branches.js +55 -21
  37. package/dist/git-cli/providers/branches.js.map +1 -1
  38. package/dist/git-cli/providers/graph.js +53 -32
  39. package/dist/git-cli/providers/graph.js.map +1 -1
  40. package/dist/git-cli/providers/operations.js +22 -4
  41. package/dist/git-cli/providers/operations.js.map +1 -1
  42. package/dist/git-cli/providers/revision.d.ts +8 -1
  43. package/dist/git-cli/providers/revision.js +22 -8
  44. package/dist/git-cli/providers/revision.js.map +1 -1
  45. package/dist/git-cli/providers/stash.js +3 -3
  46. package/dist/git-cli/providers/stash.js.map +1 -1
  47. package/dist/git-cli/providers/worktrees.js +3 -3
  48. package/dist/git-cli/providers/worktrees.js.map +1 -1
  49. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,29 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.15.1] - 2026-10-19
10
+
11
+ ### Added
12
+
13
+ - Adds `cancellation` to `getTreeForRevision`, as its fourth argument after `options`, so a host lazy-loading a folder tree can abort the read for a folder the user collapsed; an aborted read rejects with a `CancellationError` rather than answering empty, and a read only joins an identical concurrent one with the same signal. The GitHub provider ignores it (git, git-cli)
14
+
15
+ ### Changed
16
+
17
+ - Changes `push` of a commit `reference` (not a branch) onto the current branch, without `publish`, to push to the branch's upstream on the remote rather than to a remote branch named like the local one. The two differ when local `foo` tracks `origin/bar`: the commit now goes to `bar`, where it used to create or move `foo`. This changes GitLens's own push of a single commit too (git-cli)
18
+
19
+ ### Fixed
20
+
21
+ - Fixes a `beforeIndexLockRetry` rejection reaching the host wrapped in the typed write's own error, such as a generic `CommitError` "Unable to commit" with the host's error only as its `original`: `commit`, `reset`, `restore`, `checkout` and every other typed write that takes run options now rethrow the host's rejection unchanged. Reject with an `Error` (an object), since only an object can be told apart from git's own failure (git-cli)
22
+ - Fixes `push`, `fetch` with a branch, and a `fastForward: 'only'` `pull` of a branch checked out nowhere targeting the wrong branch on the remote, and `GitBranch.trackingWithoutRemote` naming it wrong, when a custom fetch refspec stores it under another name (remote `origin` fetching into `refs/remotes/mirror/*`): they now use the upstream's name on the remote, which branch reads report as `GitTrackingUpstream.remoteBranch` (and `remoteBranch` on a branch reference's `upstream`) from git's `%(upstream:remoteref)` on Git 2.23+; older Git keeps stripping the remote from the upstream's name. (git, git-cli)
23
+ - Fixes `GitBranch.remoteName`, `trackingWithoutRemote` and the push, fetch and pull resolution splitting an upstream such as `team/fork/feature` at its first `/` on Git older than 2.23, which can't report the upstream's remote: branch reads there now resolve it against the repository's remotes, read once and only when an upstream's name leaves the remote ambiguous (git-cli)
24
+ - Fixes `splitRemoteBranchName` dropping a leading `remotes/` from every name, so a remote named `remotes` (or `remotes/x`) never matched: the name is now matched as given first, and the `remotes/` prefix is stripped only when no remote matches (git)
25
+ - Fixes a `commit` being retried, re-running its hooks, when a commit hook's own git command lost `index.lock` to another process and printed git's lock failure as the run's first output: a run is now retried only when git itself exited on the held lock (exit code 128), since a failed hook makes `commit` exit 1 (git-cli)
26
+ - Fixes the graph confusing local branches with remote-tracking ones by their short names: a local `team/fork/work` beside remote `team/fork` showed as a remote branch, and a local and a remote branch of the same short name (local `origin/x` beside remote `origin`'s `x`) both resolved to one of them, losing the local branch's current-branch and worktree markers; a branch tracking a local branch also got an upstream id that matched no branch. The graph's log now reads full ref names (`--decorate=full`, whatever `log.decorate` says), so each tip is exactly a local branch, a remote branch or a tag, and refs from other namespaces (`refs/pull/*`) no longer show as local branches (git-cli)
27
+ - Fixes `getIgnoredEntries` with `paths` scoped inside an ignored folder answering with the enclosing folder (`node_modules/` for `node_modules/pkg`), or throwing for a scope with a trailing `/` or a file inside it, which made git fail: a scope that is itself ignored is now answered as one entry in the caller's scope (`node_modules/pkg/`, or the file's path), at the cost of one `check-ignore` run per scoped call (git-cli)
28
+ - Fixes a branch tracking a local branch with a `/` in its name (`release/2.0`) being read as on remote `release`, so `fetch` with that branch ran a failing `git fetch release 2.0`, a `fastForward: 'only'` `pull` failed the same way, `GitBranch.remoteName` named a remote that doesn't exist, and publishing such a branch pushed it onto the remote under its local upstream's name (moving the remote's `main` for a branch tracking local `main`): branch reads now keep that git reported the upstream as a local branch, as `GitTrackingUpstream.local` (and `local` on a branch reference's `upstream`), so such a branch has no remote and no remote list is read for it. A branch reference that carries neither `local` nor `remote` on its upstream (one a host built itself) is now looked up in the branch list rather than guessed from the upstream's name. A remote git reports wins over the ref's prefix, so a fetch refspec that writes into `refs/heads/` isn't mistaken for a local upstream, and the current-branch read used before the branch list is loaded reports the same fields; the fetch does nothing and the pull rejects with `PullError` reason `noUpstream` (git, git-cli)
29
+ - Fixes the worktree summary's upstream `remote` and `branch` being worked out from the upstream's name against the repository's remotes, so a local upstream named like a remote's branch (`release/2.0` beside remote `release`) got a remote, and a custom fetch refspec got none: it now uses what git reported, `local`, `remote` and `remoteBranch`, and reads the remotes only when git reported neither (git)
30
+ - Fixes a `push` with no resolvable remote running a bare `git push` that follows `push.default`, which can push somewhere the caller didn't pick, and a branch reference read before its remote was removed pushing to a folder named like that remote, which git takes as a repository path: a push with a `reference` whose upstream is a local branch, names a remote the repository no longer has (or the remote list can't be read), or has no remote at all, and a push of the current branch with no upstream (which rejected with reason `other`), now reject with a `PushError` with reason `noUpstream` before running git (git-cli)
31
+
9
32
  ## [0.15.0] - 2026-10-19
10
33
 
11
34
  ### Added
@@ -769,7 +792,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
769
792
 
770
793
  - Initial release. Bundles `@gitlens/utils`, `@gitlens/git`, `@gitlens/git-cli`, `@gitlens/ai`, and `@gitlens/git-github` into a single core npm package with subpath exports.
771
794
 
772
- [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.0...HEAD
795
+ [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.1...HEAD
796
+ [0.15.1]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.0...gitkraken:releases/core/v0.15.1
773
797
  [0.15.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.14.0...gitkraken:releases/core/v0.15.0
774
798
  [0.14.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.13.0...gitkraken:releases/core/v0.14.0
775
799
  [0.13.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.12.0...gitkraken:releases/core/v0.13.0
@@ -93,6 +93,13 @@ export interface GitTrackingUpstream {
93
93
  * `/` stays whole. `undefined` when git is too old to report it (before 2.23 here) or the upstream is a local branch
94
94
  */
95
95
  readonly remote?: string;
96
+ /**
97
+ * The branch's name on its {@link remote} (`branch.<name>.merge` without `refs/heads/`), so a custom fetch refspec
98
+ * can't misname it. `undefined` when git is too old to report it or the upstream is a local branch
99
+ */
100
+ readonly remoteBranch?: string;
101
+ /** The upstream is a branch in this repository (`branch.<name>.remote = .`), so it has no remote */
102
+ readonly local?: boolean;
96
103
  readonly missing: boolean;
97
104
  readonly state: GitTrackingState;
98
105
  }
@@ -107,6 +107,7 @@ let GitBranch = _GitBranch = class GitBranch {
107
107
  }
108
108
  get remoteName() {
109
109
  if (this.remote) return getRemoteNameFromBranchName(this.name);
110
+ if (this.upstream?.local) return void 0;
110
111
  if (this.upstream != null) return this.upstream.remote ?? getRemoteNameFromBranchName(this.upstream.name);
111
112
  }
112
113
  get starred() {
@@ -1 +1 @@
1
- {"version":3,"file":"branch.js","names":[],"sources":["../../../../git/src/models/branch.ts"],"sourcesContent":["import { formatDate, fromNow } from '@gitlens/utils/date.js';\nimport { loggable } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport { serializable } from '@gitlens/utils/decorators/serializable.js';\nimport { getBranchId, getRemoteNameFromBranchName, getRemoteNameSlashIndex } from '@gitlens/utils/gitRefs.js';\nimport type { MaybePausedResult } from '@gitlens/utils/promise.js';\nimport type { Shape } from '@gitlens/utils/types.js';\nimport {\n\tformatDetachedHeadName,\n\tgetBranchTrackingWithoutRemote,\n\tisDetachedHead,\n\tparseRefName,\n} from '../utils/branch.utils.js';\nimport { getUpstreamStatus } from '../utils/status.utils.js';\nimport type { GitBranchReference } from './reference.js';\n\nexport type BranchDisposition = 'starred' | 'archived';\n\n/** The `.git/gk/config` keys (`branch.<name>.<key>`) a branch's {@link BranchMetadata} is read from */\nexport type BranchMetadataGkConfigKey =\n\t| 'gk-last-accessed'\n\t| 'gk-last-modified'\n\t| 'gk-agent-last-activity'\n\t| 'gk-disposition';\n\n/**\n * A branch's `.git/gk/config` metadata. Unlike other cached values, the cache updates these objects in place when a\n * key changes (see `Cache.patchBranchMetadata`), so every `GitBranch` holding one, old or new, reads the new value\n */\nexport interface BranchMetadata {\n\tlastAccessedAt?: string;\n\tlastModifiedAt?: string;\n\tagentLastActivityAt?: string;\n\tdisposition?: BranchDisposition;\n}\n\nexport type GitBranchShape = Shape<GitBranch>;\n\n/**\n * Each metadata object's last parsed stamps. Not `@memoize`d on the branch: the cache updates `BranchMetadata` in\n * place, so a parse is reused only while its stamp is unchanged — and shared by every branch holding the object.\n */\nconst stampDates = new WeakMap<BranchMetadata, Partial<Record<StampField, { stamp: string; date: Date }>>>();\ntype StampField = 'lastAccessedAt' | 'lastModifiedAt';\n\nfunction getStampDate(metadata: BranchMetadata, field: StampField): Date | undefined {\n\tconst stamp = metadata[field];\n\tif (!stamp) return undefined;\n\n\tlet parsed = stampDates.get(metadata);\n\tif (parsed == null) {\n\t\tparsed = {};\n\t\tstampDates.set(metadata, parsed);\n\t}\n\n\tlet entry = parsed[field];\n\tif (entry?.stamp !== stamp) {\n\t\tentry = { stamp: stamp, date: new Date(stamp) };\n\t\tparsed[field] = entry;\n\t}\n\treturn entry.date;\n}\n\n@loggable(i => i.id)\n@serializable\nexport class GitBranch implements GitBranchReference {\n\treadonly refType = 'branch';\n\treadonly detached: boolean;\n\treadonly id: string;\n\n\tprivate readonly _name: string;\n\tget name(): string {\n\t\treturn this._name;\n\t}\n\n\tprivate readonly _remote: boolean;\n\tget remote(): boolean {\n\t\treturn this._remote;\n\t}\n\n\tconstructor(\n\t\tpublic readonly repoPath: string,\n\t\tpublic readonly refName: string,\n\t\tpublic readonly current: boolean,\n\t\tpublic readonly date: Date | undefined,\n\t\t/**\n\t\t * GK config metadata (dates, disposition), shared with the cache and updated in place: comparing an old and a\n\t\t * new branch can't detect a changed stamp (no change event fires for one either)\n\t\t */\n\t\tpublic readonly metadata: BranchMetadata | undefined,\n\t\tpublic readonly sha?: string,\n\t\tpublic readonly upstream?: GitTrackingUpstream,\n\t\tpublic readonly worktree?: { path: string; isDefault: boolean } | false,\n\t\tdetached: boolean = false,\n\t\tpublic readonly rebasing: boolean = false,\n\t) {\n\t\t({ name: this._name, remote: this._remote } = parseRefName(refName));\n\n\t\tthis.detached = detached || (this.current ? isDetachedHead(this._name) : false);\n\t\tif (this.detached) {\n\t\t\tthis.id = getBranchId(repoPath, this._remote, this.sha!);\n\t\t\tthis._name = formatDetachedHeadName(this.sha!);\n\t\t} else {\n\t\t\tthis.id = getBranchId(repoPath, this._remote, this._name);\n\t\t}\n\n\t\tthis.upstream = upstream?.name ? upstream : undefined;\n\t}\n\n\tget archived(): boolean {\n\t\treturn this.metadata?.disposition === 'archived';\n\t}\n\n\t@memoize()\n\tget basename(): string {\n\t\tconst name = this.nameWithoutRemote;\n\t\tconst index = name.lastIndexOf('/');\n\t\treturn index !== -1 ? name.substring(index + 1) : name;\n\t}\n\n\tget disposition(): BranchDisposition | undefined {\n\t\treturn this.metadata?.disposition;\n\t}\n\n\t/** @returns The most recent date among lastModifiedDate, lastAccessedDate, and branch.date */\n\tget effectiveDate(): Date | undefined {\n\t\tlet maxTime: number | undefined;\n\n\t\tconst accessed = this.lastAccessedDate?.getTime();\n\t\tif (accessed != null && (maxTime == null || accessed > maxTime)) {\n\t\t\tmaxTime = accessed;\n\t\t}\n\n\t\tconst modified = this.lastModifiedDate?.getTime();\n\t\tif (modified != null && (maxTime == null || modified > maxTime)) {\n\t\t\tmaxTime = modified;\n\t\t}\n\n\t\tconst date = this.date?.getTime();\n\t\tif (date != null && (maxTime == null || date > maxTime)) {\n\t\t\tmaxTime = date;\n\t\t}\n\n\t\treturn maxTime != null ? new Date(maxTime) : undefined;\n\t}\n\n\tget lastAccessedDate(): Date | undefined {\n\t\treturn this.metadata != null ? getStampDate(this.metadata, 'lastAccessedAt') : undefined;\n\t}\n\n\t/** When a checkout's working tree or index last changed while on this branch (`gk-last-modified`), not only commits */\n\tget lastModifiedDate(): Date | undefined {\n\t\treturn this.metadata != null ? getStampDate(this.metadata, 'lastModifiedAt') : undefined;\n\t}\n\n\t@memoize()\n\tget nameWithoutRemote(): string {\n\t\treturn this.remote ? this.name.substring(getRemoteNameSlashIndex(this.name) + 1) : this.name;\n\t}\n\n\tget ref(): string {\n\t\treturn this.detached ? this.sha! : this._name;\n\t}\n\n\t@memoize()\n\tget remoteName(): string | undefined {\n\t\tif (this.remote) return getRemoteNameFromBranchName(this.name);\n\t\tif (this.upstream != null) return this.upstream.remote ?? getRemoteNameFromBranchName(this.upstream.name);\n\n\t\treturn undefined;\n\t}\n\n\tget starred(): boolean {\n\t\treturn this.metadata?.disposition === 'starred';\n\t}\n\n\tget status(): GitBranchStatus {\n\t\tif (this.remote) return 'remote';\n\t\tif (this.upstream == null) return this.detached ? 'detached' : 'local';\n\n\t\tif (this.upstream.missing) return 'missingUpstream';\n\t\tif (this.upstream.state.ahead && this.upstream.state.behind) return 'diverged';\n\t\tif (this.upstream.state.ahead) return 'ahead';\n\t\tif (this.upstream.state.behind) return 'behind';\n\t\treturn 'upToDate';\n\t}\n\n\t@memoize()\n\tget trackingWithoutRemote(): string | undefined {\n\t\treturn getBranchTrackingWithoutRemote(this);\n\t}\n\n\t/** Creates a copy of this branch with a different repoPath and optionally a different current flag — ONLY used for worktree-aware caching */\n\twithRepoPath(repoPath: string, current?: boolean): GitBranch {\n\t\tconst newCurrent = current ?? this.current;\n\t\tif (repoPath === this.repoPath && newCurrent === this.current) return this;\n\t\treturn new GitBranch(\n\t\t\trepoPath,\n\t\t\tthis.refName,\n\t\t\tnewCurrent,\n\t\t\tthis.date,\n\t\t\tthis.metadata,\n\t\t\tthis.sha,\n\t\t\tthis.upstream,\n\t\t\tthis.worktree,\n\t\t\tthis.detached,\n\t\t\tthis.rebasing,\n\t\t);\n\t}\n\n\tstatic is(branch: unknown): branch is GitBranch {\n\t\treturn branch instanceof GitBranch;\n\t}\n\n\tstatic formatDate(branch: GitBranchShape, format?: string | null): string {\n\t\treturn branch.date != null ? formatDate(branch.date, format ?? 'MMMM Do, YYYY h:mma') : '';\n\t}\n\n\tstatic formatDateFromNow(branch: GitBranchShape): string {\n\t\treturn branch.date != null ? fromNow(branch.date) : '';\n\t}\n\n\tstatic formatDateWithStyle(\n\t\tbranch: GitBranchShape,\n\t\tformatting: { dateStyle: string; dateFormat: string | null },\n\t): string {\n\t\treturn formatting.dateStyle === 'absolute'\n\t\t\t? GitBranch.formatDate(branch, formatting.dateFormat)\n\t\t\t: GitBranch.formatDateFromNow(branch);\n\t}\n\n\tstatic getTrackingStatus(\n\t\tbranch: GitBranchShape,\n\t\toptions?: {\n\t\t\tcount?: boolean;\n\t\t\tempty?: string;\n\t\t\texpand?: boolean;\n\t\t\ticons?: boolean;\n\t\t\tprefix?: string;\n\t\t\tseparator?: string;\n\t\t\tsuffix?: string;\n\t\t},\n\t): string {\n\t\treturn getUpstreamStatus(branch.upstream, options);\n\t}\n}\n\nexport interface GitTrackingState {\n\treadonly ahead: number;\n\treadonly behind: number;\n}\n\nexport interface GitTrackingUpstream {\n\treadonly name: string;\n\t/**\n\t * The remote the upstream is on (`branch.<name>.remote`) as git reports it, so a remote whose own name contains a\n\t * `/` stays whole. `undefined` when git is too old to report it (before 2.23 here) or the upstream is a local branch\n\t */\n\treadonly remote?: string;\n\treadonly missing: boolean;\n\treadonly state: GitTrackingState;\n}\n\nexport type GitBranchStatus =\n\t| 'local'\n\t| 'detached'\n\t| 'ahead'\n\t| 'behind'\n\t| 'diverged'\n\t| 'upToDate'\n\t| 'missingUpstream'\n\t| 'remote';\n\nexport interface BranchTargetInfo {\n\tmergeTargetBranch: MaybePausedResult<string | undefined>;\n\tbaseBranch: string | undefined;\n\tdefaultBranch: string | undefined;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"branch.js","names":[],"sources":["../../../../git/src/models/branch.ts"],"sourcesContent":["import { formatDate, fromNow } from '@gitlens/utils/date.js';\nimport { loggable } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport { serializable } from '@gitlens/utils/decorators/serializable.js';\nimport { getBranchId, getRemoteNameFromBranchName, getRemoteNameSlashIndex } from '@gitlens/utils/gitRefs.js';\nimport type { MaybePausedResult } from '@gitlens/utils/promise.js';\nimport type { Shape } from '@gitlens/utils/types.js';\nimport {\n\tformatDetachedHeadName,\n\tgetBranchTrackingWithoutRemote,\n\tisDetachedHead,\n\tparseRefName,\n} from '../utils/branch.utils.js';\nimport { getUpstreamStatus } from '../utils/status.utils.js';\nimport type { GitBranchReference } from './reference.js';\n\nexport type BranchDisposition = 'starred' | 'archived';\n\n/** The `.git/gk/config` keys (`branch.<name>.<key>`) a branch's {@link BranchMetadata} is read from */\nexport type BranchMetadataGkConfigKey =\n\t| 'gk-last-accessed'\n\t| 'gk-last-modified'\n\t| 'gk-agent-last-activity'\n\t| 'gk-disposition';\n\n/**\n * A branch's `.git/gk/config` metadata. Unlike other cached values, the cache updates these objects in place when a\n * key changes (see `Cache.patchBranchMetadata`), so every `GitBranch` holding one, old or new, reads the new value\n */\nexport interface BranchMetadata {\n\tlastAccessedAt?: string;\n\tlastModifiedAt?: string;\n\tagentLastActivityAt?: string;\n\tdisposition?: BranchDisposition;\n}\n\nexport type GitBranchShape = Shape<GitBranch>;\n\n/**\n * Each metadata object's last parsed stamps. Not `@memoize`d on the branch: the cache updates `BranchMetadata` in\n * place, so a parse is reused only while its stamp is unchanged — and shared by every branch holding the object.\n */\nconst stampDates = new WeakMap<BranchMetadata, Partial<Record<StampField, { stamp: string; date: Date }>>>();\ntype StampField = 'lastAccessedAt' | 'lastModifiedAt';\n\nfunction getStampDate(metadata: BranchMetadata, field: StampField): Date | undefined {\n\tconst stamp = metadata[field];\n\tif (!stamp) return undefined;\n\n\tlet parsed = stampDates.get(metadata);\n\tif (parsed == null) {\n\t\tparsed = {};\n\t\tstampDates.set(metadata, parsed);\n\t}\n\n\tlet entry = parsed[field];\n\tif (entry?.stamp !== stamp) {\n\t\tentry = { stamp: stamp, date: new Date(stamp) };\n\t\tparsed[field] = entry;\n\t}\n\treturn entry.date;\n}\n\n@loggable(i => i.id)\n@serializable\nexport class GitBranch implements GitBranchReference {\n\treadonly refType = 'branch';\n\treadonly detached: boolean;\n\treadonly id: string;\n\n\tprivate readonly _name: string;\n\tget name(): string {\n\t\treturn this._name;\n\t}\n\n\tprivate readonly _remote: boolean;\n\tget remote(): boolean {\n\t\treturn this._remote;\n\t}\n\n\tconstructor(\n\t\tpublic readonly repoPath: string,\n\t\tpublic readonly refName: string,\n\t\tpublic readonly current: boolean,\n\t\tpublic readonly date: Date | undefined,\n\t\t/**\n\t\t * GK config metadata (dates, disposition), shared with the cache and updated in place: comparing an old and a\n\t\t * new branch can't detect a changed stamp (no change event fires for one either)\n\t\t */\n\t\tpublic readonly metadata: BranchMetadata | undefined,\n\t\tpublic readonly sha?: string,\n\t\tpublic readonly upstream?: GitTrackingUpstream,\n\t\tpublic readonly worktree?: { path: string; isDefault: boolean } | false,\n\t\tdetached: boolean = false,\n\t\tpublic readonly rebasing: boolean = false,\n\t) {\n\t\t({ name: this._name, remote: this._remote } = parseRefName(refName));\n\n\t\tthis.detached = detached || (this.current ? isDetachedHead(this._name) : false);\n\t\tif (this.detached) {\n\t\t\tthis.id = getBranchId(repoPath, this._remote, this.sha!);\n\t\t\tthis._name = formatDetachedHeadName(this.sha!);\n\t\t} else {\n\t\t\tthis.id = getBranchId(repoPath, this._remote, this._name);\n\t\t}\n\n\t\tthis.upstream = upstream?.name ? upstream : undefined;\n\t}\n\n\tget archived(): boolean {\n\t\treturn this.metadata?.disposition === 'archived';\n\t}\n\n\t@memoize()\n\tget basename(): string {\n\t\tconst name = this.nameWithoutRemote;\n\t\tconst index = name.lastIndexOf('/');\n\t\treturn index !== -1 ? name.substring(index + 1) : name;\n\t}\n\n\tget disposition(): BranchDisposition | undefined {\n\t\treturn this.metadata?.disposition;\n\t}\n\n\t/** @returns The most recent date among lastModifiedDate, lastAccessedDate, and branch.date */\n\tget effectiveDate(): Date | undefined {\n\t\tlet maxTime: number | undefined;\n\n\t\tconst accessed = this.lastAccessedDate?.getTime();\n\t\tif (accessed != null && (maxTime == null || accessed > maxTime)) {\n\t\t\tmaxTime = accessed;\n\t\t}\n\n\t\tconst modified = this.lastModifiedDate?.getTime();\n\t\tif (modified != null && (maxTime == null || modified > maxTime)) {\n\t\t\tmaxTime = modified;\n\t\t}\n\n\t\tconst date = this.date?.getTime();\n\t\tif (date != null && (maxTime == null || date > maxTime)) {\n\t\t\tmaxTime = date;\n\t\t}\n\n\t\treturn maxTime != null ? new Date(maxTime) : undefined;\n\t}\n\n\tget lastAccessedDate(): Date | undefined {\n\t\treturn this.metadata != null ? getStampDate(this.metadata, 'lastAccessedAt') : undefined;\n\t}\n\n\t/** When a checkout's working tree or index last changed while on this branch (`gk-last-modified`), not only commits */\n\tget lastModifiedDate(): Date | undefined {\n\t\treturn this.metadata != null ? getStampDate(this.metadata, 'lastModifiedAt') : undefined;\n\t}\n\n\t@memoize()\n\tget nameWithoutRemote(): string {\n\t\treturn this.remote ? this.name.substring(getRemoteNameSlashIndex(this.name) + 1) : this.name;\n\t}\n\n\tget ref(): string {\n\t\treturn this.detached ? this.sha! : this._name;\n\t}\n\n\t@memoize()\n\tget remoteName(): string | undefined {\n\t\tif (this.remote) return getRemoteNameFromBranchName(this.name);\n\t\tif (this.upstream?.local) return undefined;\n\t\tif (this.upstream != null) return this.upstream.remote ?? getRemoteNameFromBranchName(this.upstream.name);\n\n\t\treturn undefined;\n\t}\n\n\tget starred(): boolean {\n\t\treturn this.metadata?.disposition === 'starred';\n\t}\n\n\tget status(): GitBranchStatus {\n\t\tif (this.remote) return 'remote';\n\t\tif (this.upstream == null) return this.detached ? 'detached' : 'local';\n\n\t\tif (this.upstream.missing) return 'missingUpstream';\n\t\tif (this.upstream.state.ahead && this.upstream.state.behind) return 'diverged';\n\t\tif (this.upstream.state.ahead) return 'ahead';\n\t\tif (this.upstream.state.behind) return 'behind';\n\t\treturn 'upToDate';\n\t}\n\n\t@memoize()\n\tget trackingWithoutRemote(): string | undefined {\n\t\treturn getBranchTrackingWithoutRemote(this);\n\t}\n\n\t/** Creates a copy of this branch with a different repoPath and optionally a different current flag — ONLY used for worktree-aware caching */\n\twithRepoPath(repoPath: string, current?: boolean): GitBranch {\n\t\tconst newCurrent = current ?? this.current;\n\t\tif (repoPath === this.repoPath && newCurrent === this.current) return this;\n\t\treturn new GitBranch(\n\t\t\trepoPath,\n\t\t\tthis.refName,\n\t\t\tnewCurrent,\n\t\t\tthis.date,\n\t\t\tthis.metadata,\n\t\t\tthis.sha,\n\t\t\tthis.upstream,\n\t\t\tthis.worktree,\n\t\t\tthis.detached,\n\t\t\tthis.rebasing,\n\t\t);\n\t}\n\n\tstatic is(branch: unknown): branch is GitBranch {\n\t\treturn branch instanceof GitBranch;\n\t}\n\n\tstatic formatDate(branch: GitBranchShape, format?: string | null): string {\n\t\treturn branch.date != null ? formatDate(branch.date, format ?? 'MMMM Do, YYYY h:mma') : '';\n\t}\n\n\tstatic formatDateFromNow(branch: GitBranchShape): string {\n\t\treturn branch.date != null ? fromNow(branch.date) : '';\n\t}\n\n\tstatic formatDateWithStyle(\n\t\tbranch: GitBranchShape,\n\t\tformatting: { dateStyle: string; dateFormat: string | null },\n\t): string {\n\t\treturn formatting.dateStyle === 'absolute'\n\t\t\t? GitBranch.formatDate(branch, formatting.dateFormat)\n\t\t\t: GitBranch.formatDateFromNow(branch);\n\t}\n\n\tstatic getTrackingStatus(\n\t\tbranch: GitBranchShape,\n\t\toptions?: {\n\t\t\tcount?: boolean;\n\t\t\tempty?: string;\n\t\t\texpand?: boolean;\n\t\t\ticons?: boolean;\n\t\t\tprefix?: string;\n\t\t\tseparator?: string;\n\t\t\tsuffix?: string;\n\t\t},\n\t): string {\n\t\treturn getUpstreamStatus(branch.upstream, options);\n\t}\n}\n\nexport interface GitTrackingState {\n\treadonly ahead: number;\n\treadonly behind: number;\n}\n\nexport interface GitTrackingUpstream {\n\treadonly name: string;\n\t/**\n\t * The remote the upstream is on (`branch.<name>.remote`) as git reports it, so a remote whose own name contains a\n\t * `/` stays whole. `undefined` when git is too old to report it (before 2.23 here) or the upstream is a local branch\n\t */\n\treadonly remote?: string;\n\t/**\n\t * The branch's name on its {@link remote} (`branch.<name>.merge` without `refs/heads/`), so a custom fetch refspec\n\t * can't misname it. `undefined` when git is too old to report it or the upstream is a local branch\n\t */\n\treadonly remoteBranch?: string;\n\t/** The upstream is a branch in this repository (`branch.<name>.remote = .`), so it has no remote */\n\treadonly local?: boolean;\n\treadonly missing: boolean;\n\treadonly state: GitTrackingState;\n}\n\nexport type GitBranchStatus =\n\t| 'local'\n\t| 'detached'\n\t| 'ahead'\n\t| 'behind'\n\t| 'diverged'\n\t| 'upToDate'\n\t| 'missingUpstream'\n\t| 'remote';\n\nexport interface BranchTargetInfo {\n\tmergeTargetBranch: MaybePausedResult<string | undefined>;\n\tbaseBranch: string | undefined;\n\tdefaultBranch: string | undefined;\n}\n"],"mappings":"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"}
@@ -357,6 +357,8 @@ export interface GraphIncrementalSeed {
357
357
  * compares unequal ⇒ safe full fallback.
358
358
  */
359
359
  readonly headRefUpstreamName?: string;
360
+ /** Whether {@link headRefUpstreamName} is a local branch rather than a remote-tracking one. */
361
+ readonly headRefUpstreamLocal?: boolean;
360
362
  }
361
363
  /**
362
364
  * Which path a seeded {@link GitGraphSubProvider.getGraph} call took: the R6b incremental head-walk fast
@@ -1 +1 @@
1
- {"version":3,"file":"graph.js","names":[],"sources":["../../../../git/src/models/graph.ts"],"sourcesContent":["import type { GitCommitReachability } from '../providers/commits.js';\nimport type { GitBranch } from './branch.js';\nimport type { GitStashCommit } from './commit.js';\nimport type { GitRemote } from './remote.js';\nimport type { GkProviderId } from './repositoryIdentities.js';\nimport type { GitWorktree } from './worktree.js';\n\nexport type GitGraphRowKind = 'commit' | 'merge' | 'stash' | 'workdir';\n\nexport interface GitGraphRowHead {\n\t/**\n\t * Stable ref id. **Its presence is the provider-tier marker** — the one signal separating a ref that\n\t * can be acted on from a lean, name-only one, now that neither carries a serialized context.\n\t *\n\t * The CLI provider stamps an id on every ref it emits (`git-cli/providers/graph.ts:781`, `:805`,\n\t * `:836`); the GitHub provider never does, because it has no branch metadata to key\n\t * (`git-github/providers/github/graph.ts:231`, `:239`). So `id == null` means \"this ref came from a\n\t * provider that computes nothing beyond a name\", and every id-keyed feature — ref metadata, exclusions,\n\t * pinning, PR/issue lookup — must degrade rather than guess. Consumers already do this; it is written\n\t * down here because until now it was an accident of two providers rather than a stated contract.\n\t *\n\t * ⚠ An id means the ref is **identifiable and actionable** — it does NOT mean its enrichment is\n\t * complete. The CLI resolves branches, remotes and worktrees with `Promise.allSettled`\n\t * (`git-cli/providers/graph.ts:546`) and still synthesizes ids when any of those settle rejected, so an\n\t * id-bearing ref can carry a missing `starred`/`upstream` that means \"not computed\" rather than \"no\".\n\t * Treat every optional field as enrichment that may be absent, and never report a capability from its\n\t * absence. If a consumer ever needs to tell \"unavailable\" from \"false\", that wants one graph-level\n\t * completeness flag, not a per-ref field on every row.\n\t */\n\tid?: string;\n\tname: string;\n\tisCurrentHead: boolean;\n\t/**\n\t * The tracked upstream. `missing`/`state` are optional here while {@link GitTrackingUpstream} makes\n\t * them required, and the difference is meaningful: absent means the producer did NOT compute them\n\t * (the GitHub provider has no branch metadata at all), NOT \"present and false/zero\". Consumers must\n\t * treat `undefined` as unknown rather than folding it into the negative case.\n\t */\n\tupstream?: { name: string; id: string; missing?: boolean; state?: { ahead: number; behind: number } };\n\t/** Starred (a GitKraken branch disposition). Structured so `+starred` stops being baked into the\n\t * serialized context at row-build time, where starring a branch left the label stale. */\n\tstarred?: boolean;\n\t/** Set when this branch is checked out in ANY worktree, including the default one. Grouped so\n\t * producers can't half-populate id-without-path or vice versa. Consumers that mean \"checked out\n\t * somewhere other than here\" — the ref-ordering tier and the worktree glyph — must test\n\t * `isDefault` too; the default worktree's checkout is the ordinary case, not a worktree badge.\n\t * Note `isDefault` here describes the WORKTREE; the sibling `isDefault` below describes the BRANCH. */\n\tworktree?: { id: string; path: string; isDefault: boolean };\n\t/** True when this head is the repository's default branch. */\n\tisDefault?: boolean;\n}\n\nexport interface GitGraphRowRemoteHead {\n\t/** Stable ref id; see {@link GitGraphRowHead.id} — its presence is the provider-tier marker. */\n\tid?: string;\n\tname: string;\n\turl?: string;\n\towner: string;\n\tavatarUrl?: string;\n\tcurrent?: boolean;\n\t/** True when this remote ref IS the repository's default branch and no local branch tracks it (no\n\t * local checkout) — so the default-branch tier still applies for remote-only defaults. */\n\tisDefault?: boolean;\n\thostingServiceType?: GkProviderId;\n\t/** See {@link GitGraphRowHead.starred}. */\n\tstarred?: boolean;\n}\n\nexport interface GitGraphRowTag {\n\t/** Stable ref id; see {@link GitGraphRowHead.id} — its presence is the provider-tier marker. */\n\tid?: string;\n\tname: string;\n\tannotated: boolean;\n}\n\n/**\n * Compact, host-computed flags for a commit/stash row, shipped instead of the bulky serialized\n * `contexts.row`/`contexts.avatar` blobs (which duplicated sha/message/repoPath already on the row).\n * The webview reconstructs the full webview-item contexts from these flags + row fields + repoPath.\n * Bit 0: reachable-from-HEAD (`+current`); Bit 1: unique-to-one-local-branch (`+unique`);\n * Bit 2: has-children — set ONLY on undo-eligible tip rows (active HEAD + worktree HEADs) that have a\n * child, to gate Undo Commit to leaf tips (undoing a commit other work is stacked on is unsafe). It is\n * NOT a general has-children signal: non-tip rows never carry it (the host only computes it for tips);\n * Bit 3: unpushed/ahead-of-upstream (`+unpublished`).\n * Bit 4: history-rewriteable (`+rewriteable`) — on the first-parent chain from HEAD up to (excluding)\n * the first merge commit, so a plain (non-`--rebase-merges`) interactive rebase can safely rewrite it;\n * gates squash/drop/reword/modify. Strictly narrower than `+current` (reachable-from-HEAD includes a\n * merge's other-parent ancestry, which is NOT safely rewriteable).\n * Bit 5: unpulled/behind-the-upstream — the exact mirror of bit 3: on HEAD's upstream tip but NOT on HEAD\n * (i.e. `HEAD..@{u}`). Drives the graph's read-only unpulled indicator; it has no `webviewItem` segment\n * (no command gates on it). Mutually exclusive with `Unpublished` by construction.\n * (`+HEAD` derives from `row.heads[].isCurrentHead`; contributor `+current` from `row.isCurrentUser`.)\n */\nexport const enum GitGraphRowContextFlags {\n\tNone = 0,\n\tReachableFromHead = 1 << 0,\n\tUniqueToBranch = 1 << 1,\n\tHasChildren = 1 << 2,\n\tUnpublished = 1 << 3,\n\tRewriteableFromHead = 1 << 4,\n\tUnpulled = 1 << 5,\n}\n\n/** Context data attached to graph rows for command/menu handling. */\nexport interface GitGraphRowContexts {\n\t/** Compact replacement for the serialized commit `row`/`avatar` contexts; see {@link GitGraphRowContextFlags}. */\n\tflags?: GitGraphRowContextFlags;\n\t/**\n\t * Index into the per-graph `GraphReachabilityTable.sets` for this row's reachable-refs set,\n\t * shipped instead of the per-row `reachability` object (which dominated the graph payload). The\n\t * webview rebuilds `row.reachability` from the shared table. Absent ⇒ no reachability for this row.\n\t */\n\treachabilityIndex?: number;\n\t/** Serialized right-click context. Built here only for stash rows; commit rows ship {@link flags}\n\t * instead and have theirs rebuilt on demand (see `rowContext.utils`). */\n\trow?: string | object;\n\t/** Serialized context per grouped-ref name, for the group's own menu. */\n\trefGroups?: Record<string, string | object>;\n}\n\nexport interface GitGraphRowStats {\n\tfiles: number;\n\tadditions: number;\n\tdeletions: number;\n}\n\n/**\n * Wire encoding for per-row reachability, shipped once per graph payload instead of inline on every\n * row. Git reachability is monotone (adjacent commits share ref-sets), so the rows collapse to a\n * relatively small number of distinct sets over the repo's ref universe — `dictionary` (the shared\n * ref list) + `sets` (distinct membership bitmaps, base64; bit i ⇒ `dictionary[i]` reachable) + a\n * per-row index (`GitGraphRowContexts.reachabilityIndex`) reconstructs every row's `reachability`\n * client-side for a fraction of the bytes the inline objects cost. `current` is a per-ref property\n * (the checked-out HEAD branch) and lives in the dictionary entry.\n *\n * The table is append-only within a graph generation, so the host ships only the entries appended\n * since its last push (a delta) on same-generation pagination and the full table on a new generation;\n * `id` lets the webview tell the two apart (same `id` ⇒ append the delta, new `id` ⇒ replace + reset\n * its decode cache). A fresh `getGraph` walk mints a new `id`; `more()` keeps the same one.\n */\nexport interface GraphReachabilityTable {\n\t/** Generation id — stable across `more()` pagination, new per fresh graph walk. */\n\treadonly id: number;\n\treadonly dictionary: GitCommitReachability['refs'];\n\t/** Distinct ref-membership bitmaps, base64-encoded `Uint8Array` of `ceil(dictionary.length/8)` bytes. */\n\treadonly sets: string[];\n}\n\n/** Library-owned graph row type. */\nexport interface GitGraphRow {\n\tsha: string;\n\tparents: string[];\n\tauthor: string;\n\temail: string;\n\tdate: number;\n\tcommitDate?: number;\n\tmessage: string;\n\tkind: GitGraphRowKind;\n\theads?: GitGraphRowHead[];\n\tremotes?: GitGraphRowRemoteHead[];\n\ttags?: GitGraphRowTag[];\n\tcontexts?: GitGraphRowContexts;\n\tstats?: GitGraphRowStats;\n\t/** Stash rows only: the `{n}` of `stash@{n}`, so consumers stop parsing it back out of the message. */\n\tstashNumber?: string;\n\t/**\n\t * Transient, set only for the duration of {@link GraphRowProcessor.processRow} (the row processor's\n\t * `+unique` decision reads it). The provider strips it before pushing the row and records the\n\t * reachable-ref set into the shared {@link GitGraph.reachability} table instead (see\n\t * {@link GitGraphRowContexts.reachabilityIndex}), so emitted rows do NOT retain the per-row arrays.\n\t */\n\treachability?: GitCommitReachability;\n\tisCurrentUser?: boolean;\n}\n\nexport interface GitGraph {\n\treadonly repoPath: string;\n\t/** A map of all avatar urls */\n\treadonly avatars: Map<string, string>;\n\t/** A set of all \"seen\" commit ids */\n\treadonly ids: Set<string>;\n\treadonly includes: { stats?: boolean } | undefined;\n\treadonly branches: ReadonlyMap<string, GitBranch>;\n\treadonly remotes: ReadonlyMap<string, GitRemote>;\n\treadonly downstreams: ReadonlyMap<string, string[]>;\n\treadonly stashes: ReadonlyMap<string, GitStashCommit> | undefined;\n\treadonly worktrees: ReadonlyArray<GitWorktree> | undefined;\n\treadonly worktreesByBranch: ReadonlyMap<string, GitWorktree> | undefined;\n\n\t/** SHAs reachable from HEAD (for enrichment: marking commits on the current branch) */\n\treadonly reachableFromHEAD?: ReadonlySet<string>;\n\n\t/**\n\t * Ref tips as of this walk: canonical refname (`refs/heads/…`, `refs/remotes/…`, `refs/tags/…`) →\n\t * PEELED tip sha (annotated tags map to the commit their badge sits on). Captured so the host can seed\n\t * the NEXT rebuild's {@link GraphIncrementalSeed.tips} — the map the R6b fast path diffs against to find\n\t * the structural changes that force a full fallback. The CLI provider populates it on both paths (full\n\t * walk + fast path); the GitHub provider leaves it undefined.\n\t */\n\treadonly refTips?: ReadonlyMap<string, string>;\n\n\t/**\n\t * Fingerprint of every SIDE INPUT row construction embeds into row decorations as of this walk —\n\t * default branch, per-branch upstreams, HEAD's upstream, remote urls/providers, worktree assignments,\n\t * and the current user. These can all change WITHOUT moving any ref tip (`git remote set-head`,\n\t * `branch --set-upstream-to`, `worktree add`, remote/user config edits), and the R6b fast path reuses\n\t * prior rows wholesale (only flags/reachability are re-derived) — so it must compare this against the\n\t * seed's and fall back to a full walk on ANY change. The CLI provider populates it on both paths; the\n\t * GitHub provider leaves it undefined.\n\t */\n\treadonly decorationFingerprint?: string;\n\n\t/** Canonical shallow boundary OIDs for this window. Empty means complete history; undefined means\n\t * unknown (or unsupported by the provider), so a CLI refresh cannot reuse ancestry-dependent data. */\n\treadonly shallowBoundary?: string;\n\n\t/**\n\t * SHAs on the first-parent chain from HEAD up to (excluding) the first merge commit — i.e. the\n\t * commits a plain interactive rebase can safely rewrite. Empty when HEAD itself is a merge. Used by\n\t * the graph's history-rewriting commands (squash/drop/reword/modify) to validate selections.\n\t */\n\treadonly rewriteableFromHEAD?: ReadonlySet<string>;\n\n\t/**\n\t * Shared, append-only reachability table for the loaded rows (the primary representation — rows\n\t * carry only a {@link GitGraphRowContexts.reachabilityIndex} into it, not per-row ref arrays).\n\t * Grows monotonically across {@link more} pagination within a graph session: indices already\n\t * assigned never change, so consumers can decode any row on demand and cache by index. Absent ⇒\n\t * no row in the graph has reachability. See {@link GraphReachabilityTable}.\n\t */\n\treadonly reachability?: GraphReachabilityTable;\n\n\t/** The rows for the set of commits requested */\n\treadonly rows: GitGraphRow[];\n\treadonly id?: string;\n\n\treadonly rowsStats?: GitGraphRowsStats;\n\treadonly rowsStatsDeferred?: { isLoaded: () => boolean; promise: Promise<void> };\n\n\treadonly paging?: {\n\t\treadonly limit: number | undefined;\n\t\treadonly startingCursor: string | undefined;\n\t\treadonly hasMore: boolean;\n\t};\n\n\t/**\n\t * Loads more commits for the graph.\n\t *\n\t * @param limit - The number of commits to load (page size)\n\t * @param id - Optional SHA to find\n\t * @param cancellation - Cancellation token\n\t *\n\t * **Behavior based on parameters:**\n\t *\n\t * - **`id` provided + `limit > 0`**: Find the commit with the given SHA, then ensure at least `limit` commits are loaded.\n\t * - If SHA is found early (e.g., at position 100), continues loading to reach `limit` total commits.\n\t * - If SHA is found late (e.g., at position 2000 when limit is 1000), loads all commits up to and including the SHA.\n\t * - This ensures the target commit is included AND provides a full page of context.\n\t *\n\t * - **`id` provided + `limit === 0`**: Find the commit with the given SHA and stop immediately.\n\t * - Loads only as many commits as needed to reach the target SHA.\n\t * - Used when you want to find a specific commit without loading extra commits.\n\t *\n\t * - **No `id` + `limit > 0`**: Load exactly `limit` commits (normal pagination).\n\t * - Standard page-based loading without a specific target.\n\t *\n\t * - **No `id` + `limit === 0`**: Load all remaining commits.\n\t * - Loads everything from the current position to the end of history.\n\t * - Use with caution as this can load a large number of commits.\n\t *\n\t * @example\n\t * // Load next page of 1000 commits\n\t * await graph.more(1000);\n\t *\n\t * @example\n\t * // Find a specific commit and ensure a full page is loaded\n\t * await graph.more(1000, 'abc123');\n\t *\n\t * @example\n\t * // Find a specific commit and stop (minimal loading)\n\t * await graph.more(0, 'abc123');\n\t */\n\tmore?(limit: number, id?: string, cancellation?: AbortSignal): Promise<GitGraph | undefined>;\n}\n\nexport type GitGraphRowsStats = Map<string, GitGraphRowStats>;\n\n/**\n * Seed for the R6b incremental head-walk fast path. Carries the prior generation's walk artifacts so a\n * repo-change rebuild can walk ONLY the changed head region, stitch the cached tail, and re-derive\n * flags/reachability in memory — instead of re-walking every loaded row.\n *\n * The CLI provider consumes this on its incremental fast path, degrading to the full ordered walk\n * whenever any gate below rejects the seed. The GitHub provider ignores it structurally (it never\n * lists the option).\n */\nexport interface GraphIncrementalSeed {\n\t/**\n\t * Prior generation's emitted rows, in walk order. R6b stitches the unchanged tail from these once the\n\t * streamed head region CONVERGES with them (K consecutive shas aligned at a stable offset), and reads\n\t * their parent lists to re-derive reachability/flags in memory (no git) over the stitched window.\n\t */\n\treadonly rows: readonly GitGraphRow[];\n\t/**\n\t * Ref tips as of the prior walk: canonical refname (e.g. `refs/heads/main`, `refs/remotes/origin/main`,\n\t * `refs/tags/v1`) → tip sha. R6b enumerates the new commits via `git log --all --not <these shas>`\n\t * (cheap, exact) and diffs this map against the current tips to detect the structural changes that force\n\t * a full fallback: any ref DELETION (key gone) or a NON-fast-forward move (old tip not an ancestor of the\n\t * new tip).\n\t */\n\treadonly tips: ReadonlyMap<string, string>;\n\t/**\n\t * Ordering the prior rows were walked in. The convergence + date-boundary reasoning R6b relies on is only\n\t * sound for `date` order in v1; a seed whose ordering is `author-date`/`topo`, or that disagrees with the\n\t * current walk's ordering, is discarded (full fallback).\n\t */\n\treadonly ordering: 'date' | 'author-date' | 'topo';\n\t/**\n\t * Prior generation's reachability table, to CONTINUE (same role as the sub-provider's `reachabilitySeed`)\n\t * so rows retained across the rebuild keep stable {@link GitGraphRowContexts.reachabilityIndex} values and\n\t * only appended entries ship. R6b decides whether the stitched window extends this builder or needs a\n\t * fresh generation; see `createReachabilityTableBuilder`.\n\t */\n\treadonly reachability?: GraphReachabilityTable;\n\t/**\n\t * Prior generation's per-sha stats. With unchanged ancestry the deferred query recomputes only new\n\t * shas; shallow-boundary or replacement changes invalidate the seed — same role as `rowsStatsSeed`.\n\t */\n\treadonly rowsStats?: GitGraphRowsStats;\n\t/**\n\t * Whether the prior generation had more rows below its loaded window (`paging.hasMore`). The fast path\n\t * can only reconstruct rows the seed carries; when the seed was a partial (paged) load and no new commit\n\t * pushes the window past its limit, `hasMore` must still be reported so the caller keeps paging. Absent ⇒\n\t * treated as `false` (the seed loaded the full history).\n\t */\n\treadonly hasMore?: boolean;\n\t/**\n\t * The `graph.onlyFollowFirstParent` setting the prior rows were walked under. When first-parent is on the\n\t * emitted rows carry sliced (first-parent-only) parents, which the in-memory re-derivation can't expand\n\t * back to the full parent set the walk propagates reachability through — so a seed built under (or a\n\t * current config of) first-parent forces a full fallback. Absent ⇒ treated as `false`.\n\t */\n\treadonly onlyFollowFirstParent?: boolean;\n\t/** The prior window's shallow boundary. Compare contents, since partial deepening stays shallow. */\n\treadonly shallowBoundary?: string;\n\t/**\n\t * {@link GitGraph.decorationFingerprint} of the prior walk. R6b falls back to a full walk on ANY change:\n\t * reused rows keep their embedded decorations (upstream/worktree/default/remote/user metadata), so a\n\t * metadata-only change — one that moves no ref tip — would otherwise ship stale pills indefinitely.\n\t * Optional because providers without the incremental machinery (e.g. GitHub) never populate it; absent ⇒\n\t * never matches ⇒ safe full fallback.\n\t */\n\treadonly decorationFingerprint?: string;\n\t/**\n\t * HEAD's tracking upstream (e.g. `origin/main`) as of the prior walk — carried separately from\n\t * {@link decorationFingerprint} so a session REBIND can neutralize it: moving HEAD to another\n\t * worktree's branch changes the fingerprint even though nothing in the repo did, so recomputing the\n\t * seed-comparable fingerprint from this value (old repoPath, everything else current) cancels exactly\n\t * that perspective move. Sound because it feeds only `remotes[].current` on a reused row, which the\n\t * rebind re-derives in memory. Absent compares as \"no upstream\", so an older seed with a real upstream\n\t * compares unequal ⇒ safe full fallback.\n\t */\n\treadonly headRefUpstreamName?: string;\n}\n\n/**\n * Which path a seeded {@link GitGraphSubProvider.getGraph} call took: the R6b incremental head-walk fast\n * path, or a full ordered walk (with the gate/boundary `reason` that forced it). Reported via the\n * `onIncrementalResult` option so the host can log it and the equivalence harness can assert it. Purely\n * observational — it never changes the returned graph.\n */\nexport interface IncrementalGraphOutcome {\n\treadonly path: 'fast' | 'fallback';\n\treadonly reason?: IncrementalGraphFallbackReason;\n\t/** Fast path only: how many NEW commit rows the incremental enumeration added at the head. */\n\treadonly added?: number;\n}\n\nexport type IncrementalGraphFallbackReason =\n\t| 'no-row-processor'\n\t| 'ordering-not-date'\n\t| 'rev-outside-seed'\n\t| 'first-parent'\n\t| 'limit-exceeds-seed'\n\t| 'ref-deleted'\n\t| 'ref-non-fast-forward'\n\t| 'replace-refs-changed'\n\t| 'shallow-changed'\n\t| 'stash-changed'\n\t| 'stash-window-conflict'\n\t| 'date-boundary'\n\t// A decoration side input changed without moving any ref tip (default branch, an upstream, a worktree\n\t// assignment, a remote's url/provider, the current user) — reused rows would keep stale embedded\n\t// decorations, so the walk must rebuild them. See `GitGraph.decorationFingerprint`.\n\t| 'metadata-changed'\n\t// The fast path threw (spawn/arg limits on huge ref sets, queue overflow, transient git errors) —\n\t// never an eligibility gate. The fast path must never fail where the full walk it accelerates\n\t// would succeed, so any unexpected error degrades to the full walk (cancellation still propagates).\n\t| 'error';\n\n/**\n * Processes a single graph row — mutates the row in place.\n *\n * Sets `row.contexts` and `row.message` (e.g., emojified), and resolves `remoteHead.avatarUrl`.\n * Also populates `context.avatars` with email → URL mappings.\n *\n * Per-ref contexts are NOT serialized here. The webview builds them from the structured ref fields\n * (`webviews/apps/plus/graph/utils/refContext.utils.ts`), where live state the host would have baked in\n * stale — whether a branch is starred, which ref is pinned — is actually visible. What remains here needs\n * something the webview cannot reach: the extension's asset URIs, or the whole-row grouping.\n *\n * Called inline during graph row iteration in the library's row-building\n * loop, once per row. Because it runs inside the loop, `more()` pagination\n * automatically gets processing for free — no wrapping needed.\n */\nexport interface GraphRowProcessor {\n\tprocessRow(row: GitGraphRow, context: GraphContext): void;\n\t/**\n\t * Rebuild a reused row's HOST-SERIALIZED contexts (`contexts.refGroups`, a stash row's `contexts.row`)\n\t * for a session rebind. The row's ref ids are already re-stamped by the walk (`restampGraphRowIds`)\n\t * before this runs — since this processor is optional, implementations must read ids as already\n\t * correct and must NOT touch them again, nor message/author (not idempotent).\n\t */\n\trestampRow?(row: GitGraphRow, context: GraphContext): void;\n}\n\nexport interface GraphContext {\n\treadonly repoPath: string;\n\treadonly useAvatars: boolean;\n\treadonly branches: ReadonlyMap<string, GitBranch>;\n\treadonly remotes: ReadonlyMap<string, GitRemote>;\n\treadonly worktreesByBranch: ReadonlyMap<string, GitWorktree> | undefined;\n\treadonly branchIdOfMainWorktree: string | undefined;\n\treadonly stashes: ReadonlyMap<string, GitStashCommit> | undefined;\n\treadonly reachableFromHEAD: ReadonlySet<string>;\n\t/** SHAs on the first-parent chain from HEAD up to (excluding) the first merge commit — the commits\n\t * a plain interactive rebase can safely rewrite. Empty when HEAD is a merge. Sets the\n\t * {@link GitGraphRowContextFlags.RewriteableFromHead} flag, gating squash/drop/reword/modify. */\n\treadonly rewriteableFromHEAD: ReadonlySet<string>;\n\t/** The subset of undo-eligible tip shas (active HEAD + worktree HEADs) that have at least one\n\t * child — i.e. are NOT leaves. Scoped to tips (not all commits) for performance; do not treat as\n\t * a general has-children signal. Sets the {@link GitGraphRowContextFlags.HasChildren} flag, which\n\t * gates Undo Commit to leaf tips. */\n\treadonly tipShasWithChildren: ReadonlySet<string>;\n\t/** SHAs reachable from HEAD's tracking-upstream tip, or `undefined` when HEAD has no upstream.\n\t * A commit reachable from HEAD but NOT from this set is unpushed (`+unpublished`); `undefined`\n\t * means there's no upstream to be ahead of, so nothing is flagged. */\n\treadonly reachableFromHeadUpstream: ReadonlySet<string> | undefined;\n\treadonly avatars: Map<string, string>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6FA,IAAkB,0BAAX,yBAAA,yBAAA;CACN,wBAAA,wBAAA,UAAA,KAAA;CACA,wBAAA,wBAAA,uBAAA,KAAA;CACA,wBAAA,wBAAA,oBAAA,KAAA;CACA,wBAAA,wBAAA,iBAAA,KAAA;CACA,wBAAA,wBAAA,iBAAA,KAAA;CACA,wBAAA,wBAAA,yBAAA,MAAA;CACA,wBAAA,wBAAA,cAAA,MAAA;;AACD,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"graph.js","names":[],"sources":["../../../../git/src/models/graph.ts"],"sourcesContent":["import type { GitCommitReachability } from '../providers/commits.js';\nimport type { GitBranch } from './branch.js';\nimport type { GitStashCommit } from './commit.js';\nimport type { GitRemote } from './remote.js';\nimport type { GkProviderId } from './repositoryIdentities.js';\nimport type { GitWorktree } from './worktree.js';\n\nexport type GitGraphRowKind = 'commit' | 'merge' | 'stash' | 'workdir';\n\nexport interface GitGraphRowHead {\n\t/**\n\t * Stable ref id. **Its presence is the provider-tier marker** — the one signal separating a ref that\n\t * can be acted on from a lean, name-only one, now that neither carries a serialized context.\n\t *\n\t * The CLI provider stamps an id on every ref it emits (`git-cli/providers/graph.ts:781`, `:805`,\n\t * `:836`); the GitHub provider never does, because it has no branch metadata to key\n\t * (`git-github/providers/github/graph.ts:231`, `:239`). So `id == null` means \"this ref came from a\n\t * provider that computes nothing beyond a name\", and every id-keyed feature — ref metadata, exclusions,\n\t * pinning, PR/issue lookup — must degrade rather than guess. Consumers already do this; it is written\n\t * down here because until now it was an accident of two providers rather than a stated contract.\n\t *\n\t * ⚠ An id means the ref is **identifiable and actionable** — it does NOT mean its enrichment is\n\t * complete. The CLI resolves branches, remotes and worktrees with `Promise.allSettled`\n\t * (`git-cli/providers/graph.ts:546`) and still synthesizes ids when any of those settle rejected, so an\n\t * id-bearing ref can carry a missing `starred`/`upstream` that means \"not computed\" rather than \"no\".\n\t * Treat every optional field as enrichment that may be absent, and never report a capability from its\n\t * absence. If a consumer ever needs to tell \"unavailable\" from \"false\", that wants one graph-level\n\t * completeness flag, not a per-ref field on every row.\n\t */\n\tid?: string;\n\tname: string;\n\tisCurrentHead: boolean;\n\t/**\n\t * The tracked upstream. `missing`/`state` are optional here while {@link GitTrackingUpstream} makes\n\t * them required, and the difference is meaningful: absent means the producer did NOT compute them\n\t * (the GitHub provider has no branch metadata at all), NOT \"present and false/zero\". Consumers must\n\t * treat `undefined` as unknown rather than folding it into the negative case.\n\t */\n\tupstream?: { name: string; id: string; missing?: boolean; state?: { ahead: number; behind: number } };\n\t/** Starred (a GitKraken branch disposition). Structured so `+starred` stops being baked into the\n\t * serialized context at row-build time, where starring a branch left the label stale. */\n\tstarred?: boolean;\n\t/** Set when this branch is checked out in ANY worktree, including the default one. Grouped so\n\t * producers can't half-populate id-without-path or vice versa. Consumers that mean \"checked out\n\t * somewhere other than here\" — the ref-ordering tier and the worktree glyph — must test\n\t * `isDefault` too; the default worktree's checkout is the ordinary case, not a worktree badge.\n\t * Note `isDefault` here describes the WORKTREE; the sibling `isDefault` below describes the BRANCH. */\n\tworktree?: { id: string; path: string; isDefault: boolean };\n\t/** True when this head is the repository's default branch. */\n\tisDefault?: boolean;\n}\n\nexport interface GitGraphRowRemoteHead {\n\t/** Stable ref id; see {@link GitGraphRowHead.id} — its presence is the provider-tier marker. */\n\tid?: string;\n\tname: string;\n\turl?: string;\n\towner: string;\n\tavatarUrl?: string;\n\tcurrent?: boolean;\n\t/** True when this remote ref IS the repository's default branch and no local branch tracks it (no\n\t * local checkout) — so the default-branch tier still applies for remote-only defaults. */\n\tisDefault?: boolean;\n\thostingServiceType?: GkProviderId;\n\t/** See {@link GitGraphRowHead.starred}. */\n\tstarred?: boolean;\n}\n\nexport interface GitGraphRowTag {\n\t/** Stable ref id; see {@link GitGraphRowHead.id} — its presence is the provider-tier marker. */\n\tid?: string;\n\tname: string;\n\tannotated: boolean;\n}\n\n/**\n * Compact, host-computed flags for a commit/stash row, shipped instead of the bulky serialized\n * `contexts.row`/`contexts.avatar` blobs (which duplicated sha/message/repoPath already on the row).\n * The webview reconstructs the full webview-item contexts from these flags + row fields + repoPath.\n * Bit 0: reachable-from-HEAD (`+current`); Bit 1: unique-to-one-local-branch (`+unique`);\n * Bit 2: has-children — set ONLY on undo-eligible tip rows (active HEAD + worktree HEADs) that have a\n * child, to gate Undo Commit to leaf tips (undoing a commit other work is stacked on is unsafe). It is\n * NOT a general has-children signal: non-tip rows never carry it (the host only computes it for tips);\n * Bit 3: unpushed/ahead-of-upstream (`+unpublished`).\n * Bit 4: history-rewriteable (`+rewriteable`) — on the first-parent chain from HEAD up to (excluding)\n * the first merge commit, so a plain (non-`--rebase-merges`) interactive rebase can safely rewrite it;\n * gates squash/drop/reword/modify. Strictly narrower than `+current` (reachable-from-HEAD includes a\n * merge's other-parent ancestry, which is NOT safely rewriteable).\n * Bit 5: unpulled/behind-the-upstream — the exact mirror of bit 3: on HEAD's upstream tip but NOT on HEAD\n * (i.e. `HEAD..@{u}`). Drives the graph's read-only unpulled indicator; it has no `webviewItem` segment\n * (no command gates on it). Mutually exclusive with `Unpublished` by construction.\n * (`+HEAD` derives from `row.heads[].isCurrentHead`; contributor `+current` from `row.isCurrentUser`.)\n */\nexport const enum GitGraphRowContextFlags {\n\tNone = 0,\n\tReachableFromHead = 1 << 0,\n\tUniqueToBranch = 1 << 1,\n\tHasChildren = 1 << 2,\n\tUnpublished = 1 << 3,\n\tRewriteableFromHead = 1 << 4,\n\tUnpulled = 1 << 5,\n}\n\n/** Context data attached to graph rows for command/menu handling. */\nexport interface GitGraphRowContexts {\n\t/** Compact replacement for the serialized commit `row`/`avatar` contexts; see {@link GitGraphRowContextFlags}. */\n\tflags?: GitGraphRowContextFlags;\n\t/**\n\t * Index into the per-graph `GraphReachabilityTable.sets` for this row's reachable-refs set,\n\t * shipped instead of the per-row `reachability` object (which dominated the graph payload). The\n\t * webview rebuilds `row.reachability` from the shared table. Absent ⇒ no reachability for this row.\n\t */\n\treachabilityIndex?: number;\n\t/** Serialized right-click context. Built here only for stash rows; commit rows ship {@link flags}\n\t * instead and have theirs rebuilt on demand (see `rowContext.utils`). */\n\trow?: string | object;\n\t/** Serialized context per grouped-ref name, for the group's own menu. */\n\trefGroups?: Record<string, string | object>;\n}\n\nexport interface GitGraphRowStats {\n\tfiles: number;\n\tadditions: number;\n\tdeletions: number;\n}\n\n/**\n * Wire encoding for per-row reachability, shipped once per graph payload instead of inline on every\n * row. Git reachability is monotone (adjacent commits share ref-sets), so the rows collapse to a\n * relatively small number of distinct sets over the repo's ref universe — `dictionary` (the shared\n * ref list) + `sets` (distinct membership bitmaps, base64; bit i ⇒ `dictionary[i]` reachable) + a\n * per-row index (`GitGraphRowContexts.reachabilityIndex`) reconstructs every row's `reachability`\n * client-side for a fraction of the bytes the inline objects cost. `current` is a per-ref property\n * (the checked-out HEAD branch) and lives in the dictionary entry.\n *\n * The table is append-only within a graph generation, so the host ships only the entries appended\n * since its last push (a delta) on same-generation pagination and the full table on a new generation;\n * `id` lets the webview tell the two apart (same `id` ⇒ append the delta, new `id` ⇒ replace + reset\n * its decode cache). A fresh `getGraph` walk mints a new `id`; `more()` keeps the same one.\n */\nexport interface GraphReachabilityTable {\n\t/** Generation id — stable across `more()` pagination, new per fresh graph walk. */\n\treadonly id: number;\n\treadonly dictionary: GitCommitReachability['refs'];\n\t/** Distinct ref-membership bitmaps, base64-encoded `Uint8Array` of `ceil(dictionary.length/8)` bytes. */\n\treadonly sets: string[];\n}\n\n/** Library-owned graph row type. */\nexport interface GitGraphRow {\n\tsha: string;\n\tparents: string[];\n\tauthor: string;\n\temail: string;\n\tdate: number;\n\tcommitDate?: number;\n\tmessage: string;\n\tkind: GitGraphRowKind;\n\theads?: GitGraphRowHead[];\n\tremotes?: GitGraphRowRemoteHead[];\n\ttags?: GitGraphRowTag[];\n\tcontexts?: GitGraphRowContexts;\n\tstats?: GitGraphRowStats;\n\t/** Stash rows only: the `{n}` of `stash@{n}`, so consumers stop parsing it back out of the message. */\n\tstashNumber?: string;\n\t/**\n\t * Transient, set only for the duration of {@link GraphRowProcessor.processRow} (the row processor's\n\t * `+unique` decision reads it). The provider strips it before pushing the row and records the\n\t * reachable-ref set into the shared {@link GitGraph.reachability} table instead (see\n\t * {@link GitGraphRowContexts.reachabilityIndex}), so emitted rows do NOT retain the per-row arrays.\n\t */\n\treachability?: GitCommitReachability;\n\tisCurrentUser?: boolean;\n}\n\nexport interface GitGraph {\n\treadonly repoPath: string;\n\t/** A map of all avatar urls */\n\treadonly avatars: Map<string, string>;\n\t/** A set of all \"seen\" commit ids */\n\treadonly ids: Set<string>;\n\treadonly includes: { stats?: boolean } | undefined;\n\treadonly branches: ReadonlyMap<string, GitBranch>;\n\treadonly remotes: ReadonlyMap<string, GitRemote>;\n\treadonly downstreams: ReadonlyMap<string, string[]>;\n\treadonly stashes: ReadonlyMap<string, GitStashCommit> | undefined;\n\treadonly worktrees: ReadonlyArray<GitWorktree> | undefined;\n\treadonly worktreesByBranch: ReadonlyMap<string, GitWorktree> | undefined;\n\n\t/** SHAs reachable from HEAD (for enrichment: marking commits on the current branch) */\n\treadonly reachableFromHEAD?: ReadonlySet<string>;\n\n\t/**\n\t * Ref tips as of this walk: canonical refname (`refs/heads/…`, `refs/remotes/…`, `refs/tags/…`) →\n\t * PEELED tip sha (annotated tags map to the commit their badge sits on). Captured so the host can seed\n\t * the NEXT rebuild's {@link GraphIncrementalSeed.tips} — the map the R6b fast path diffs against to find\n\t * the structural changes that force a full fallback. The CLI provider populates it on both paths (full\n\t * walk + fast path); the GitHub provider leaves it undefined.\n\t */\n\treadonly refTips?: ReadonlyMap<string, string>;\n\n\t/**\n\t * Fingerprint of every SIDE INPUT row construction embeds into row decorations as of this walk —\n\t * default branch, per-branch upstreams, HEAD's upstream, remote urls/providers, worktree assignments,\n\t * and the current user. These can all change WITHOUT moving any ref tip (`git remote set-head`,\n\t * `branch --set-upstream-to`, `worktree add`, remote/user config edits), and the R6b fast path reuses\n\t * prior rows wholesale (only flags/reachability are re-derived) — so it must compare this against the\n\t * seed's and fall back to a full walk on ANY change. The CLI provider populates it on both paths; the\n\t * GitHub provider leaves it undefined.\n\t */\n\treadonly decorationFingerprint?: string;\n\n\t/** Canonical shallow boundary OIDs for this window. Empty means complete history; undefined means\n\t * unknown (or unsupported by the provider), so a CLI refresh cannot reuse ancestry-dependent data. */\n\treadonly shallowBoundary?: string;\n\n\t/**\n\t * SHAs on the first-parent chain from HEAD up to (excluding) the first merge commit — i.e. the\n\t * commits a plain interactive rebase can safely rewrite. Empty when HEAD itself is a merge. Used by\n\t * the graph's history-rewriting commands (squash/drop/reword/modify) to validate selections.\n\t */\n\treadonly rewriteableFromHEAD?: ReadonlySet<string>;\n\n\t/**\n\t * Shared, append-only reachability table for the loaded rows (the primary representation — rows\n\t * carry only a {@link GitGraphRowContexts.reachabilityIndex} into it, not per-row ref arrays).\n\t * Grows monotonically across {@link more} pagination within a graph session: indices already\n\t * assigned never change, so consumers can decode any row on demand and cache by index. Absent ⇒\n\t * no row in the graph has reachability. See {@link GraphReachabilityTable}.\n\t */\n\treadonly reachability?: GraphReachabilityTable;\n\n\t/** The rows for the set of commits requested */\n\treadonly rows: GitGraphRow[];\n\treadonly id?: string;\n\n\treadonly rowsStats?: GitGraphRowsStats;\n\treadonly rowsStatsDeferred?: { isLoaded: () => boolean; promise: Promise<void> };\n\n\treadonly paging?: {\n\t\treadonly limit: number | undefined;\n\t\treadonly startingCursor: string | undefined;\n\t\treadonly hasMore: boolean;\n\t};\n\n\t/**\n\t * Loads more commits for the graph.\n\t *\n\t * @param limit - The number of commits to load (page size)\n\t * @param id - Optional SHA to find\n\t * @param cancellation - Cancellation token\n\t *\n\t * **Behavior based on parameters:**\n\t *\n\t * - **`id` provided + `limit > 0`**: Find the commit with the given SHA, then ensure at least `limit` commits are loaded.\n\t * - If SHA is found early (e.g., at position 100), continues loading to reach `limit` total commits.\n\t * - If SHA is found late (e.g., at position 2000 when limit is 1000), loads all commits up to and including the SHA.\n\t * - This ensures the target commit is included AND provides a full page of context.\n\t *\n\t * - **`id` provided + `limit === 0`**: Find the commit with the given SHA and stop immediately.\n\t * - Loads only as many commits as needed to reach the target SHA.\n\t * - Used when you want to find a specific commit without loading extra commits.\n\t *\n\t * - **No `id` + `limit > 0`**: Load exactly `limit` commits (normal pagination).\n\t * - Standard page-based loading without a specific target.\n\t *\n\t * - **No `id` + `limit === 0`**: Load all remaining commits.\n\t * - Loads everything from the current position to the end of history.\n\t * - Use with caution as this can load a large number of commits.\n\t *\n\t * @example\n\t * // Load next page of 1000 commits\n\t * await graph.more(1000);\n\t *\n\t * @example\n\t * // Find a specific commit and ensure a full page is loaded\n\t * await graph.more(1000, 'abc123');\n\t *\n\t * @example\n\t * // Find a specific commit and stop (minimal loading)\n\t * await graph.more(0, 'abc123');\n\t */\n\tmore?(limit: number, id?: string, cancellation?: AbortSignal): Promise<GitGraph | undefined>;\n}\n\nexport type GitGraphRowsStats = Map<string, GitGraphRowStats>;\n\n/**\n * Seed for the R6b incremental head-walk fast path. Carries the prior generation's walk artifacts so a\n * repo-change rebuild can walk ONLY the changed head region, stitch the cached tail, and re-derive\n * flags/reachability in memory — instead of re-walking every loaded row.\n *\n * The CLI provider consumes this on its incremental fast path, degrading to the full ordered walk\n * whenever any gate below rejects the seed. The GitHub provider ignores it structurally (it never\n * lists the option).\n */\nexport interface GraphIncrementalSeed {\n\t/**\n\t * Prior generation's emitted rows, in walk order. R6b stitches the unchanged tail from these once the\n\t * streamed head region CONVERGES with them (K consecutive shas aligned at a stable offset), and reads\n\t * their parent lists to re-derive reachability/flags in memory (no git) over the stitched window.\n\t */\n\treadonly rows: readonly GitGraphRow[];\n\t/**\n\t * Ref tips as of the prior walk: canonical refname (e.g. `refs/heads/main`, `refs/remotes/origin/main`,\n\t * `refs/tags/v1`) → tip sha. R6b enumerates the new commits via `git log --all --not <these shas>`\n\t * (cheap, exact) and diffs this map against the current tips to detect the structural changes that force\n\t * a full fallback: any ref DELETION (key gone) or a NON-fast-forward move (old tip not an ancestor of the\n\t * new tip).\n\t */\n\treadonly tips: ReadonlyMap<string, string>;\n\t/**\n\t * Ordering the prior rows were walked in. The convergence + date-boundary reasoning R6b relies on is only\n\t * sound for `date` order in v1; a seed whose ordering is `author-date`/`topo`, or that disagrees with the\n\t * current walk's ordering, is discarded (full fallback).\n\t */\n\treadonly ordering: 'date' | 'author-date' | 'topo';\n\t/**\n\t * Prior generation's reachability table, to CONTINUE (same role as the sub-provider's `reachabilitySeed`)\n\t * so rows retained across the rebuild keep stable {@link GitGraphRowContexts.reachabilityIndex} values and\n\t * only appended entries ship. R6b decides whether the stitched window extends this builder or needs a\n\t * fresh generation; see `createReachabilityTableBuilder`.\n\t */\n\treadonly reachability?: GraphReachabilityTable;\n\t/**\n\t * Prior generation's per-sha stats. With unchanged ancestry the deferred query recomputes only new\n\t * shas; shallow-boundary or replacement changes invalidate the seed — same role as `rowsStatsSeed`.\n\t */\n\treadonly rowsStats?: GitGraphRowsStats;\n\t/**\n\t * Whether the prior generation had more rows below its loaded window (`paging.hasMore`). The fast path\n\t * can only reconstruct rows the seed carries; when the seed was a partial (paged) load and no new commit\n\t * pushes the window past its limit, `hasMore` must still be reported so the caller keeps paging. Absent ⇒\n\t * treated as `false` (the seed loaded the full history).\n\t */\n\treadonly hasMore?: boolean;\n\t/**\n\t * The `graph.onlyFollowFirstParent` setting the prior rows were walked under. When first-parent is on the\n\t * emitted rows carry sliced (first-parent-only) parents, which the in-memory re-derivation can't expand\n\t * back to the full parent set the walk propagates reachability through — so a seed built under (or a\n\t * current config of) first-parent forces a full fallback. Absent ⇒ treated as `false`.\n\t */\n\treadonly onlyFollowFirstParent?: boolean;\n\t/** The prior window's shallow boundary. Compare contents, since partial deepening stays shallow. */\n\treadonly shallowBoundary?: string;\n\t/**\n\t * {@link GitGraph.decorationFingerprint} of the prior walk. R6b falls back to a full walk on ANY change:\n\t * reused rows keep their embedded decorations (upstream/worktree/default/remote/user metadata), so a\n\t * metadata-only change — one that moves no ref tip — would otherwise ship stale pills indefinitely.\n\t * Optional because providers without the incremental machinery (e.g. GitHub) never populate it; absent ⇒\n\t * never matches ⇒ safe full fallback.\n\t */\n\treadonly decorationFingerprint?: string;\n\t/**\n\t * HEAD's tracking upstream (e.g. `origin/main`) as of the prior walk — carried separately from\n\t * {@link decorationFingerprint} so a session REBIND can neutralize it: moving HEAD to another\n\t * worktree's branch changes the fingerprint even though nothing in the repo did, so recomputing the\n\t * seed-comparable fingerprint from this value (old repoPath, everything else current) cancels exactly\n\t * that perspective move. Sound because it feeds only `remotes[].current` on a reused row, which the\n\t * rebind re-derives in memory. Absent compares as \"no upstream\", so an older seed with a real upstream\n\t * compares unequal ⇒ safe full fallback.\n\t */\n\treadonly headRefUpstreamName?: string;\n\t/** Whether {@link headRefUpstreamName} is a local branch rather than a remote-tracking one. */\n\treadonly headRefUpstreamLocal?: boolean;\n}\n\n/**\n * Which path a seeded {@link GitGraphSubProvider.getGraph} call took: the R6b incremental head-walk fast\n * path, or a full ordered walk (with the gate/boundary `reason` that forced it). Reported via the\n * `onIncrementalResult` option so the host can log it and the equivalence harness can assert it. Purely\n * observational — it never changes the returned graph.\n */\nexport interface IncrementalGraphOutcome {\n\treadonly path: 'fast' | 'fallback';\n\treadonly reason?: IncrementalGraphFallbackReason;\n\t/** Fast path only: how many NEW commit rows the incremental enumeration added at the head. */\n\treadonly added?: number;\n}\n\nexport type IncrementalGraphFallbackReason =\n\t| 'no-row-processor'\n\t| 'ordering-not-date'\n\t| 'rev-outside-seed'\n\t| 'first-parent'\n\t| 'limit-exceeds-seed'\n\t| 'ref-deleted'\n\t| 'ref-non-fast-forward'\n\t| 'replace-refs-changed'\n\t| 'shallow-changed'\n\t| 'stash-changed'\n\t| 'stash-window-conflict'\n\t| 'date-boundary'\n\t// A decoration side input changed without moving any ref tip (default branch, an upstream, a worktree\n\t// assignment, a remote's url/provider, the current user) — reused rows would keep stale embedded\n\t// decorations, so the walk must rebuild them. See `GitGraph.decorationFingerprint`.\n\t| 'metadata-changed'\n\t// The fast path threw (spawn/arg limits on huge ref sets, queue overflow, transient git errors) —\n\t// never an eligibility gate. The fast path must never fail where the full walk it accelerates\n\t// would succeed, so any unexpected error degrades to the full walk (cancellation still propagates).\n\t| 'error';\n\n/**\n * Processes a single graph row — mutates the row in place.\n *\n * Sets `row.contexts` and `row.message` (e.g., emojified), and resolves `remoteHead.avatarUrl`.\n * Also populates `context.avatars` with email → URL mappings.\n *\n * Per-ref contexts are NOT serialized here. The webview builds them from the structured ref fields\n * (`webviews/apps/plus/graph/utils/refContext.utils.ts`), where live state the host would have baked in\n * stale — whether a branch is starred, which ref is pinned — is actually visible. What remains here needs\n * something the webview cannot reach: the extension's asset URIs, or the whole-row grouping.\n *\n * Called inline during graph row iteration in the library's row-building\n * loop, once per row. Because it runs inside the loop, `more()` pagination\n * automatically gets processing for free — no wrapping needed.\n */\nexport interface GraphRowProcessor {\n\tprocessRow(row: GitGraphRow, context: GraphContext): void;\n\t/**\n\t * Rebuild a reused row's HOST-SERIALIZED contexts (`contexts.refGroups`, a stash row's `contexts.row`)\n\t * for a session rebind. The row's ref ids are already re-stamped by the walk (`restampGraphRowIds`)\n\t * before this runs — since this processor is optional, implementations must read ids as already\n\t * correct and must NOT touch them again, nor message/author (not idempotent).\n\t */\n\trestampRow?(row: GitGraphRow, context: GraphContext): void;\n}\n\nexport interface GraphContext {\n\treadonly repoPath: string;\n\treadonly useAvatars: boolean;\n\treadonly branches: ReadonlyMap<string, GitBranch>;\n\treadonly remotes: ReadonlyMap<string, GitRemote>;\n\treadonly worktreesByBranch: ReadonlyMap<string, GitWorktree> | undefined;\n\treadonly branchIdOfMainWorktree: string | undefined;\n\treadonly stashes: ReadonlyMap<string, GitStashCommit> | undefined;\n\treadonly reachableFromHEAD: ReadonlySet<string>;\n\t/** SHAs on the first-parent chain from HEAD up to (excluding) the first merge commit — the commits\n\t * a plain interactive rebase can safely rewrite. Empty when HEAD is a merge. Sets the\n\t * {@link GitGraphRowContextFlags.RewriteableFromHead} flag, gating squash/drop/reword/modify. */\n\treadonly rewriteableFromHEAD: ReadonlySet<string>;\n\t/** The subset of undo-eligible tip shas (active HEAD + worktree HEADs) that have at least one\n\t * child — i.e. are NOT leaves. Scoped to tips (not all commits) for performance; do not treat as\n\t * a general has-children signal. Sets the {@link GitGraphRowContextFlags.HasChildren} flag, which\n\t * gates Undo Commit to leaf tips. */\n\treadonly tipShasWithChildren: ReadonlySet<string>;\n\t/** SHAs reachable from HEAD's tracking-upstream tip, or `undefined` when HEAD has no upstream.\n\t * A commit reachable from HEAD but NOT from this set is unpushed (`+unpublished`); `undefined`\n\t * means there's no upstream to be ahead of, so nothing is flagged. */\n\treadonly reachableFromHeadUpstream: ReadonlySet<string> | undefined;\n\treadonly avatars: Map<string, string>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6FA,IAAkB,0BAAX,yBAAA,yBAAA;CACN,wBAAA,wBAAA,UAAA,KAAA;CACA,wBAAA,wBAAA,uBAAA,KAAA;CACA,wBAAA,wBAAA,oBAAA,KAAA;CACA,wBAAA,wBAAA,iBAAA,KAAA;CACA,wBAAA,wBAAA,iBAAA,KAAA;CACA,wBAAA,wBAAA,yBAAA,MAAA;CACA,wBAAA,wBAAA,cAAA,MAAA;;AACD,EAAA,CAAA,CAAA"}
@@ -10,6 +10,8 @@ export interface GitBranchReference {
10
10
  name: string;
11
11
  missing: boolean;
12
12
  remote?: string;
13
+ remoteBranch?: string;
14
+ local?: boolean;
13
15
  };
14
16
  readonly worktree?: {
15
17
  path: string;
@@ -79,6 +81,8 @@ export interface RefRecord {
79
81
  worktreePath?: string;
80
82
  /** `%(upstream:remotename)` — the remote a local branch tracks on (`.` for a local upstream); only read where git supports it */
81
83
  upstreamRemote?: string;
84
+ /** `%(upstream:remoteref)` — the ref a local branch tracks on its remote (`branch.<name>.merge`); only read where git supports it */
85
+ upstreamRemoteRef?: string;
82
86
  }
83
87
  //#endregion
84
88
  //# sourceMappingURL=reference.d.ts.map
@@ -61,7 +61,9 @@ export interface GitProvider {
61
61
  * the caller limits what it shows. Unlike `getIgnoredDirectories` this is a listing, not a watcher-pruning set, so
62
62
  * nothing is dropped or re-confirmed against the rules. `options.paths` limits the listing to those repo-relative
63
63
  * folders (or files), matched literally rather than as globs, so only they are walked — an empty array answers
64
- * empty without running git, and omitting it lists the whole working tree. `options.priority` (e.g.
64
+ * empty without running git, and omitting it lists the whole working tree. A scope that is itself ignored, or lies
65
+ * inside an ignored folder, is answered as one entry in the caller's scope (`dir/` for a folder, the path for a
66
+ * file), never an ancestor outside it. `options.priority` (e.g.
65
67
  * `'interactive'`) is for a caller waiting on the answer.
66
68
  */
67
69
  getIgnoredEntries?(repoPath: string, options?: {
@@ -36,10 +36,11 @@ export interface GitRevisionSubProvider {
36
36
  * `paths` limits the listing to those repo-relative folders (or files), matched literally rather than as globs;
37
37
  * each scope's ancestor folders are listed too, an empty array answers empty without running git, and omitting
38
38
  * it lists the whole tree.
39
+ * An aborted `cancellation` rejects with a `CancellationError` rather than answering empty.
39
40
  */
40
41
  getTreeForRevision(repoPath: string, rev: string, options?: {
41
42
  paths?: readonly string[];
42
- }): Promise<GitTreeEntry[]>;
43
+ }, cancellation?: AbortSignal): Promise<GitTreeEntry[]>;
43
44
  resolveRevision(repoPath: string, ref: string, pathOrUri?: string | Uri,
44
45
  /** `force`: skips the cached answer and any in-flight read that started before this call, and stores the fresh answer. */
45
46
  options?: {
@@ -95,8 +95,9 @@ export interface GitRunOptions {
95
95
  * Called before core retries a write that found `index.lock` held by another process — after the wait, right
96
96
  * before the next attempt. The index may have changed while the other process held the lock, so this lets the
97
97
  * caller re-check what the retry will act on (e.g. the staged set a `commit` records). Rejecting gives up: the
98
- * rejection becomes the run's failure, in every `errors` mode. Throwing `lockError` back opts out of the retry,
99
- * leaving the lock failure as the result.
98
+ * rejection reaches the caller as thrown — from `run` and from every typed write that takes run options — in every
99
+ * `errors` mode. Reject with an `Error` (an object), as only an object can be told apart from git's own failure.
100
+ * Throwing `lockError` back opts out of the retry, leaving the lock failure as the result.
100
101
  *
101
102
  * It runs while this checkout's index writes are held in order, so it must not write the index through core
102
103
  * (it would wait on itself). A run given this hook is never shared with an identical concurrent run. Only the
@@ -4,8 +4,15 @@ import { BranchMetadata, BranchMetadataGkConfigKey, GitBranch, GitTrackingUpstre
4
4
  //#region ../git/src/utils/branch.utils.d.ts
5
5
  export declare function formatDetachedHeadName(sha: string): string;
6
6
  export declare function getBranchNameAndRemote(ref: GitBranchReference): [name: string, remote: string | undefined];
7
+ /**
8
+ * The upstream's branch name on its remote. The name git reports (`branch.<name>.merge`) wins over stripping the local
9
+ * remote-tracking name, which a custom fetch refspec makes differ (`origin` fetching into `refs/remotes/mirror/*`)
10
+ */
7
11
  export declare function getBranchTrackingWithoutRemote(ref: GitBranchReference): string | undefined;
8
- /** Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves whole */
12
+ /**
13
+ * Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves
14
+ * whole. A leading `remotes/` is stripped only when no remote matches the name as given
15
+ */
9
16
  export declare function splitRemoteBranchName(name: string, remoteNames: Iterable<string>): [remote: string, branch: string] | undefined;
10
17
  export declare function isDetachedHead(name: string): boolean;
11
18
  export declare function isOfBranchRefType(branch: GitReference | undefined): branch is GitBranchReference;
@@ -17,8 +24,13 @@ export declare function parseRefName(refName: string): {
17
24
  };
18
25
  export declare function getLocalBranchByUpstream(remoteBranchName: string, branches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>): Promise<GitBranch | undefined>;
19
26
  export declare function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>>;
20
- /** @param remote - `%(upstream:remotename)`; empty or `.` (a local upstream) is not a remote */
21
- export declare function parseUpstream(upstream: string, tracking: string, remote?: string): GitTrackingUpstream | undefined;
27
+ /**
28
+ * @param remote - `%(upstream:remotename)`; `.` is a local upstream, and empty means git reported none. A reported
29
+ * remote always wins, even when a custom fetch refspec puts its branches under `refs/heads/`; only when none was
30
+ * reported (git < 2.23) is an upstream under `refs/heads/` taken as local
31
+ * @param remoteRef - `%(upstream:remoteref)`; the upstream's ref on `remote`, read as its branch name
32
+ */
33
+ export declare function parseUpstream(upstream: string, tracking: string, remote?: string, remoteRef?: string): GitTrackingUpstream | undefined;
22
34
  /** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */
23
35
  export declare function parseBranchMetadataGkConfigKey(key: string): {
24
36
  branch: string;
@@ -22,6 +22,7 @@ function getBranchNameAndRemote(ref) {
22
22
  if (index === -1) return [ref.name, void 0];
23
23
  return [ref.name.substring(index + 1), ref.name.substring(0, index)];
24
24
  }
25
+ if (ref.upstream?.local) return [ref.name, void 0];
25
26
  if (ref.upstream?.remote) return [ref.name, ref.upstream.remote];
26
27
  if (ref.upstream?.name != null) {
27
28
  const index = getRemoteNameSlashIndex(ref.upstream.name);
@@ -30,15 +31,29 @@ function getBranchNameAndRemote(ref) {
30
31
  }
31
32
  return [ref.name, void 0];
32
33
  }
34
+ /**
35
+ * The upstream's branch name on its remote. The name git reports (`branch.<name>.merge`) wins over stripping the local
36
+ * remote-tracking name, which a custom fetch refspec makes differ (`origin` fetching into `refs/remotes/mirror/*`)
37
+ */
33
38
  function getBranchTrackingWithoutRemote(ref) {
34
39
  if (ref.upstream == null) return void 0;
35
- const { name, remote } = ref.upstream;
40
+ const { name, remote, remoteBranch, local } = ref.upstream;
41
+ if (local) return name;
42
+ if (remoteBranch) return remoteBranch;
36
43
  if (remote && name.startsWith(`${remote}/`)) return name.substring(remote.length + 1);
37
44
  return name.substring(getRemoteNameSlashIndex(name) + 1);
38
45
  }
39
- /** Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves whole */
46
+ /**
47
+ * Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves
48
+ * whole. A leading `remotes/` is stripped only when no remote matches the name as given
49
+ */
40
50
  function splitRemoteBranchName(name, remoteNames) {
41
- if (name.startsWith("remotes/")) name = name.substring(8);
51
+ const names = Array.isArray(remoteNames) ? remoteNames : [...remoteNames];
52
+ const split = splitAtLongestRemote(name, names);
53
+ if (split != null || !name.startsWith("remotes/")) return split;
54
+ return splitAtLongestRemote(name.substring(8), names);
55
+ }
56
+ function splitAtLongestRemote(name, remoteNames) {
42
57
  let match;
43
58
  for (const remote of remoteNames) if (name.length > remote.length + 1 && name.charCodeAt(remote.length) === 47 && name.startsWith(remote) && (match == null || remote.length > match.length)) match = remote;
44
59
  return match != null ? [match, name.substring(match.length + 1)] : void 0;
@@ -89,11 +104,20 @@ async function getLocalBranchUpstreamNames(branches) {
89
104
  return remoteBranches;
90
105
  }
91
106
  const trackingStateRegex = /(?:\[(?:ahead ([0-9]+))?[,\s]*(?:behind ([0-9]+))?]|\[(gone)])/;
92
- /** @param remote - `%(upstream:remotename)`; empty or `.` (a local upstream) is not a remote */
93
- function parseUpstream(upstream, tracking, remote) {
107
+ /**
108
+ * @param remote - `%(upstream:remotename)`; `.` is a local upstream, and empty means git reported none. A reported
109
+ * remote always wins, even when a custom fetch refspec puts its branches under `refs/heads/`; only when none was
110
+ * reported (git < 2.23) is an upstream under `refs/heads/` taken as local
111
+ * @param remoteRef - `%(upstream:remoteref)`; the upstream's ref on `remote`, read as its branch name
112
+ */
113
+ function parseUpstream(upstream, tracking, remote, remoteRef) {
94
114
  if (!upstream) return void 0;
95
115
  const { name } = parseRefName(upstream);
96
- const remoteProp = remote && remote !== "." ? { remote } : void 0;
116
+ const localProp = (remote ? remote === "." : upstream.startsWith("refs/heads/")) ? { local: true } : void 0;
117
+ const remoteProp = remote && remote !== "." ? {
118
+ remote,
119
+ ...remoteRef ? { remoteBranch: remoteRef.replace(/^refs\/heads\//, "") } : void 0
120
+ } : void 0;
97
121
  const match = trackingStateRegex.exec(tracking);
98
122
  if (match == null) return {
99
123
  name,
@@ -102,7 +126,8 @@ function parseUpstream(upstream, tracking, remote) {
102
126
  ahead: 0,
103
127
  behind: 0
104
128
  },
105
- ...remoteProp
129
+ ...remoteProp,
130
+ ...localProp
106
131
  };
107
132
  const ahead = match[1] == null ? 0 : Number(match[1]);
108
133
  const behind = match[2] == null ? 0 : Number(match[2]);
@@ -113,7 +138,8 @@ function parseUpstream(upstream, tracking, remote) {
113
138
  ahead: isNaN(ahead) ? 0 : ahead,
114
139
  behind: isNaN(behind) ? 0 : behind
115
140
  },
116
- ...remoteProp
141
+ ...remoteProp,
142
+ ...localProp
117
143
  };
118
144
  }
119
145
  const branchMetadataGkConfigKeyRegex = /^branch\.(.+)\.(gk-(?:last-accessed|last-modified|agent-last-activity|disposition))$/;
@@ -1 +1 @@
1
- {"version":3,"file":"branch.utils.js","names":[],"sources":["../../../../git/src/utils/branch.utils.ts"],"sourcesContent":["import { getRemoteNameSlashIndex } from '@gitlens/utils/gitRefs.js';\nimport type { PageableResult } from '@gitlens/utils/paging.js';\nimport type { BranchMetadata, BranchMetadataGkConfigKey, GitBranch, GitTrackingUpstream } from '../models/branch.js';\nimport type { GitBranchReference, GitReference } from '../models/reference.js';\nimport { shortenRevision } from './revision.utils.js';\n\n/**\n * The names git uses where a branch name would go when HEAD isn't on a branch — `(HEAD detached\n * at|from <rev>)`, `(no branch)`, `(no branch, rebasing <name>)`, `(no branch, bisect started on\n * <name>)`, and `git status --porcelain=v2`'s `branch.head` token `(detached)` — plus bare `HEAD`\n * and our own {@link formatDetachedHeadName} output (`(<rev>...)`).\n *\n * Deliberately NOT \"anything parenthesized\": parentheses are legal in ref names, so `(release)`,\n * `v1.0(rc)` and `feat/(wip)` are all real branches a user can be on. Matching those made\n * `GitBranch` rewrite a genuine branch's `name` to the synthesized `(sha…)` label and re-key its\n * `id` by SHA, which corrupts its identity for every downstream consumer.\n */\nconst detachedHEADRegex = /^(?:HEAD|\\(detached\\)|\\(HEAD detached (?:at|from) .+\\)|\\(no branch.*\\)|\\(.+\\.\\.\\.\\))$/;\n\nexport function formatDetachedHeadName(sha: string): string {\n\treturn `(${shortenRevision(sha)}...)`;\n}\n\nexport function getBranchNameAndRemote(ref: GitBranchReference): [name: string, remote: string | undefined] {\n\tif (ref.remote) {\n\t\tconst index = getRemoteNameSlashIndex(ref.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name.substring(index + 1), ref.name.substring(0, index)];\n\t}\n\n\tif (ref.upstream?.remote) return [ref.name, ref.upstream.remote];\n\n\tif (ref.upstream?.name != null) {\n\t\tconst index = getRemoteNameSlashIndex(ref.upstream.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name, ref.upstream.name.substring(0, index)];\n\t}\n\n\treturn [ref.name, undefined];\n}\n\nexport function getBranchTrackingWithoutRemote(ref: GitBranchReference): string | undefined {\n\tif (ref.upstream == null) return undefined;\n\n\tconst { name, remote } = ref.upstream;\n\tif (remote && name.startsWith(`${remote}/`)) return name.substring(remote.length + 1);\n\n\treturn name.substring(getRemoteNameSlashIndex(name) + 1);\n}\n\n/** Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves whole */\nexport function splitRemoteBranchName(\n\tname: string,\n\tremoteNames: Iterable<string>,\n): [remote: string, branch: string] | undefined {\n\tif (name.startsWith('remotes/')) {\n\t\tname = name.substring(8);\n\t}\n\n\tlet match: string | undefined;\n\tfor (const remote of remoteNames) {\n\t\tif (\n\t\t\tname.length > remote.length + 1 &&\n\t\t\tname.charCodeAt(remote.length) === 47 /* '/' */ &&\n\t\t\tname.startsWith(remote) &&\n\t\t\t(match == null || remote.length > match.length)\n\t\t) {\n\t\t\tmatch = remote;\n\t\t}\n\t}\n\n\treturn match != null ? [match, name.substring(match.length + 1)] : undefined;\n}\n\nexport function isDetachedHead(name: string): boolean {\n\t// If there is whitespace in the name assume this is not a valid branch name\n\t// Deals with detached HEAD states\n\tname = name.trim();\n\treturn name.length ? detachedHEADRegex.test(name) : true;\n}\n\nexport function isOfBranchRefType(branch: GitReference | undefined): branch is GitBranchReference {\n\treturn branch?.refType === 'branch';\n}\n\nconst localBranchesPrefixRegex = /^(refs\\/)?heads\\//i;\nconst remoteBranchesPrefixRegex = /^(refs\\/)?remotes\\//i;\nconst remoteHEADRegex = /^(refs\\/)?remotes\\/(.*\\/)HEAD$/i;\n\nexport function isRemoteBranch(refName: string): boolean {\n\treturn remoteBranchesPrefixRegex.test(refName);\n}\n\nexport function isRemoteHEAD(refName: string): boolean {\n\treturn remoteHEADRegex.test(refName);\n}\n\nexport function parseRefName(refName: string): { name: string; remote: boolean } {\n\tconst remote = isRemoteBranch(refName);\n\n\tlet name;\n\tif (remote) {\n\t\t// Strip off refs/remotes/\n\t\tname = refName.replace(remoteBranchesPrefixRegex, '');\n\t} else {\n\t\t// Strip off refs/heads/\n\t\tname = refName.replace(localBranchesPrefixRegex, '');\n\t}\n\n\treturn { name: name, remote: remote };\n}\n\nexport async function getLocalBranchByUpstream(\n\tremoteBranchName: string,\n\tbranches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>,\n): Promise<GitBranch | undefined> {\n\tlet qualifiedRemoteBranchName;\n\tif (remoteBranchName.startsWith('remotes/')) {\n\t\tqualifiedRemoteBranchName = remoteBranchName;\n\t\tremoteBranchName = remoteBranchName.substring(8);\n\t} else {\n\t\tqualifiedRemoteBranchName = `remotes/${remoteBranchName}`;\n\t}\n\n\tfunction matches(branch: GitBranch): boolean {\n\t\treturn (\n\t\t\t!branch.remote &&\n\t\t\tbranch.upstream?.name != null &&\n\t\t\t(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName!)\n\t\t);\n\t}\n\n\tconst values = branches.values();\n\tif (Symbol.asyncIterator in values) {\n\t\tfor await (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t} else {\n\t\tfor (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport async function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>> {\n\tconst remoteBranches = new Set<string>();\n\n\tfor await (const branch of branches.values()) {\n\t\tif (!branch.remote && branch.upstream?.name != null) {\n\t\t\tremoteBranches.add(branch.upstream.name);\n\t\t}\n\t}\n\n\treturn remoteBranches;\n}\n\nconst trackingStateRegex = /(?:\\[(?:ahead ([0-9]+))?[,\\s]*(?:behind ([0-9]+))?]|\\[(gone)])/;\n\n/** @param remote - `%(upstream:remotename)`; empty or `.` (a local upstream) is not a remote */\nexport function parseUpstream(upstream: string, tracking: string, remote?: string): GitTrackingUpstream | undefined {\n\tif (!upstream) return undefined;\n\n\tconst { name } = parseRefName(upstream);\n\tconst remoteProp = remote && remote !== '.' ? { remote: remote } : undefined;\n\n\tconst match = trackingStateRegex.exec(tracking);\n\tif (match == null) {\n\t\treturn { name: name, missing: false, state: { ahead: 0, behind: 0 }, ...remoteProp };\n\t}\n\n\tconst ahead = match[1] == null ? 0 : Number(match[1]);\n\tconst behind = match[2] == null ? 0 : Number(match[2]);\n\n\treturn {\n\t\tname: name,\n\t\tmissing: match[3] != null,\n\t\tstate: { ahead: isNaN(ahead) ? 0 : ahead, behind: isNaN(behind) ? 0 : behind },\n\t\t...remoteProp,\n\t};\n}\n\nconst branchMetadataGkConfigKeyRegex =\n\t/^branch\\.(.+)\\.(gk-(?:last-accessed|last-modified|agent-last-activity|disposition))$/;\n\n/** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */\nexport function parseBranchMetadataGkConfigKey(\n\tkey: string,\n): { branch: string; key: BranchMetadataGkConfigKey } | undefined {\n\tconst match = branchMetadataGkConfigKeyRegex.exec(key);\n\tif (match == null) return undefined;\n\n\treturn { branch: match[1], key: match[2] as BranchMetadataGkConfigKey };\n}\n\n/** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */\nexport function setBranchMetadataValue(\n\tmetadata: BranchMetadata,\n\tkey: BranchMetadataGkConfigKey,\n\tvalue: string | undefined,\n): void {\n\tswitch (key) {\n\t\tcase 'gk-last-accessed':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.lastAccessedAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.lastAccessedAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-last-modified':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.lastModifiedAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.lastModifiedAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-agent-last-activity':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.agentLastActivityAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.agentLastActivityAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-disposition':\n\t\t\tif (value === 'starred' || value === 'archived') {\n\t\t\t\tmetadata.disposition = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.disposition;\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"branch.utils.js","names":[],"sources":["../../../../git/src/utils/branch.utils.ts"],"sourcesContent":["import { getRemoteNameSlashIndex } from '@gitlens/utils/gitRefs.js';\nimport type { PageableResult } from '@gitlens/utils/paging.js';\nimport type { BranchMetadata, BranchMetadataGkConfigKey, GitBranch, GitTrackingUpstream } from '../models/branch.js';\nimport type { GitBranchReference, GitReference } from '../models/reference.js';\nimport { shortenRevision } from './revision.utils.js';\n\n/**\n * The names git uses where a branch name would go when HEAD isn't on a branch — `(HEAD detached\n * at|from <rev>)`, `(no branch)`, `(no branch, rebasing <name>)`, `(no branch, bisect started on\n * <name>)`, and `git status --porcelain=v2`'s `branch.head` token `(detached)` — plus bare `HEAD`\n * and our own {@link formatDetachedHeadName} output (`(<rev>...)`).\n *\n * Deliberately NOT \"anything parenthesized\": parentheses are legal in ref names, so `(release)`,\n * `v1.0(rc)` and `feat/(wip)` are all real branches a user can be on. Matching those made\n * `GitBranch` rewrite a genuine branch's `name` to the synthesized `(sha…)` label and re-key its\n * `id` by SHA, which corrupts its identity for every downstream consumer.\n */\nconst detachedHEADRegex = /^(?:HEAD|\\(detached\\)|\\(HEAD detached (?:at|from) .+\\)|\\(no branch.*\\)|\\(.+\\.\\.\\.\\))$/;\n\nexport function formatDetachedHeadName(sha: string): string {\n\treturn `(${shortenRevision(sha)}...)`;\n}\n\nexport function getBranchNameAndRemote(ref: GitBranchReference): [name: string, remote: string | undefined] {\n\tif (ref.remote) {\n\t\tconst index = getRemoteNameSlashIndex(ref.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name.substring(index + 1), ref.name.substring(0, index)];\n\t}\n\n\tif (ref.upstream?.local) return [ref.name, undefined];\n\tif (ref.upstream?.remote) return [ref.name, ref.upstream.remote];\n\n\tif (ref.upstream?.name != null) {\n\t\tconst index = getRemoteNameSlashIndex(ref.upstream.name);\n\t\tif (index === -1) return [ref.name, undefined];\n\n\t\treturn [ref.name, ref.upstream.name.substring(0, index)];\n\t}\n\n\treturn [ref.name, undefined];\n}\n\n/**\n * The upstream's branch name on its remote. The name git reports (`branch.<name>.merge`) wins over stripping the local\n * remote-tracking name, which a custom fetch refspec makes differ (`origin` fetching into `refs/remotes/mirror/*`)\n */\nexport function getBranchTrackingWithoutRemote(ref: GitBranchReference): string | undefined {\n\tif (ref.upstream == null) return undefined;\n\n\tconst { name, remote, remoteBranch, local } = ref.upstream;\n\tif (local) return name;\n\tif (remoteBranch) return remoteBranch;\n\tif (remote && name.startsWith(`${remote}/`)) return name.substring(remote.length + 1);\n\n\treturn name.substring(getRemoteNameSlashIndex(name) + 1);\n}\n\n/**\n * Splits `name` at the longest of `remoteNames` that prefixes it, so a remote whose own name contains a `/` resolves\n * whole. A leading `remotes/` is stripped only when no remote matches the name as given\n */\nexport function splitRemoteBranchName(\n\tname: string,\n\tremoteNames: Iterable<string>,\n): [remote: string, branch: string] | undefined {\n\tconst names = Array.isArray(remoteNames) ? remoteNames : [...remoteNames];\n\n\tconst split = splitAtLongestRemote(name, names);\n\tif (split != null || !name.startsWith('remotes/')) return split;\n\n\treturn splitAtLongestRemote(name.substring(8), names);\n}\n\nfunction splitAtLongestRemote(\n\tname: string,\n\tremoteNames: Iterable<string>,\n): [remote: string, branch: string] | undefined {\n\tlet match: string | undefined;\n\tfor (const remote of remoteNames) {\n\t\tif (\n\t\t\tname.length > remote.length + 1 &&\n\t\t\tname.charCodeAt(remote.length) === 47 /* '/' */ &&\n\t\t\tname.startsWith(remote) &&\n\t\t\t(match == null || remote.length > match.length)\n\t\t) {\n\t\t\tmatch = remote;\n\t\t}\n\t}\n\n\treturn match != null ? [match, name.substring(match.length + 1)] : undefined;\n}\n\nexport function isDetachedHead(name: string): boolean {\n\t// If there is whitespace in the name assume this is not a valid branch name\n\t// Deals with detached HEAD states\n\tname = name.trim();\n\treturn name.length ? detachedHEADRegex.test(name) : true;\n}\n\nexport function isOfBranchRefType(branch: GitReference | undefined): branch is GitBranchReference {\n\treturn branch?.refType === 'branch';\n}\n\nconst localBranchesPrefixRegex = /^(refs\\/)?heads\\//i;\nconst remoteBranchesPrefixRegex = /^(refs\\/)?remotes\\//i;\nconst remoteHEADRegex = /^(refs\\/)?remotes\\/(.*\\/)HEAD$/i;\n\nexport function isRemoteBranch(refName: string): boolean {\n\treturn remoteBranchesPrefixRegex.test(refName);\n}\n\nexport function isRemoteHEAD(refName: string): boolean {\n\treturn remoteHEADRegex.test(refName);\n}\n\nexport function parseRefName(refName: string): { name: string; remote: boolean } {\n\tconst remote = isRemoteBranch(refName);\n\n\tlet name;\n\tif (remote) {\n\t\t// Strip off refs/remotes/\n\t\tname = refName.replace(remoteBranchesPrefixRegex, '');\n\t} else {\n\t\t// Strip off refs/heads/\n\t\tname = refName.replace(localBranchesPrefixRegex, '');\n\t}\n\n\treturn { name: name, remote: remote };\n}\n\nexport async function getLocalBranchByUpstream(\n\tremoteBranchName: string,\n\tbranches: PageableResult<GitBranch> | ReadonlyMap<unknown, GitBranch>,\n): Promise<GitBranch | undefined> {\n\tlet qualifiedRemoteBranchName;\n\tif (remoteBranchName.startsWith('remotes/')) {\n\t\tqualifiedRemoteBranchName = remoteBranchName;\n\t\tremoteBranchName = remoteBranchName.substring(8);\n\t} else {\n\t\tqualifiedRemoteBranchName = `remotes/${remoteBranchName}`;\n\t}\n\n\tfunction matches(branch: GitBranch): boolean {\n\t\treturn (\n\t\t\t!branch.remote &&\n\t\t\tbranch.upstream?.name != null &&\n\t\t\t(branch.upstream.name === remoteBranchName || branch.upstream.name === qualifiedRemoteBranchName!)\n\t\t);\n\t}\n\n\tconst values = branches.values();\n\tif (Symbol.asyncIterator in values) {\n\t\tfor await (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t} else {\n\t\tfor (const branch of values) {\n\t\t\tif (matches(branch)) return branch;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\nexport async function getLocalBranchUpstreamNames(branches: PageableResult<GitBranch>): Promise<Set<string>> {\n\tconst remoteBranches = new Set<string>();\n\n\tfor await (const branch of branches.values()) {\n\t\tif (!branch.remote && branch.upstream?.name != null) {\n\t\t\tremoteBranches.add(branch.upstream.name);\n\t\t}\n\t}\n\n\treturn remoteBranches;\n}\n\nconst trackingStateRegex = /(?:\\[(?:ahead ([0-9]+))?[,\\s]*(?:behind ([0-9]+))?]|\\[(gone)])/;\n\n/**\n * @param remote - `%(upstream:remotename)`; `.` is a local upstream, and empty means git reported none. A reported\n * remote always wins, even when a custom fetch refspec puts its branches under `refs/heads/`; only when none was\n * reported (git < 2.23) is an upstream under `refs/heads/` taken as local\n * @param remoteRef - `%(upstream:remoteref)`; the upstream's ref on `remote`, read as its branch name\n */\nexport function parseUpstream(\n\tupstream: string,\n\ttracking: string,\n\tremote?: string,\n\tremoteRef?: string,\n): GitTrackingUpstream | undefined {\n\tif (!upstream) return undefined;\n\n\tconst { name } = parseRefName(upstream);\n\tconst local = remote ? remote === '.' : upstream.startsWith('refs/heads/');\n\tconst localProp = local ? { local: true } : undefined;\n\tconst remoteProp =\n\t\tremote && remote !== '.'\n\t\t\t? { remote: remote, ...(remoteRef ? { remoteBranch: remoteRef.replace(/^refs\\/heads\\//, '') } : undefined) }\n\t\t\t: undefined;\n\n\tconst match = trackingStateRegex.exec(tracking);\n\tif (match == null) {\n\t\treturn { name: name, missing: false, state: { ahead: 0, behind: 0 }, ...remoteProp, ...localProp };\n\t}\n\n\tconst ahead = match[1] == null ? 0 : Number(match[1]);\n\tconst behind = match[2] == null ? 0 : Number(match[2]);\n\n\treturn {\n\t\tname: name,\n\t\tmissing: match[3] != null,\n\t\tstate: { ahead: isNaN(ahead) ? 0 : ahead, behind: isNaN(behind) ? 0 : behind },\n\t\t...remoteProp,\n\t\t...localProp,\n\t};\n}\n\nconst branchMetadataGkConfigKeyRegex =\n\t/^branch\\.(.+)\\.(gk-(?:last-accessed|last-modified|agent-last-activity|disposition))$/;\n\n/** Splits a `branch.<name>.<key>` gk config key that a branch's {@link BranchMetadata} is read from, or `undefined` for any other key */\nexport function parseBranchMetadataGkConfigKey(\n\tkey: string,\n): { branch: string; key: BranchMetadataGkConfigKey } | undefined {\n\tconst match = branchMetadataGkConfigKeyRegex.exec(key);\n\tif (match == null) return undefined;\n\n\treturn { branch: match[1], key: match[2] as BranchMetadataGkConfigKey };\n}\n\n/** Sets the field `key` is read into to `value`, or removes it for `undefined`, exactly as a fresh read of the key would */\nexport function setBranchMetadataValue(\n\tmetadata: BranchMetadata,\n\tkey: BranchMetadataGkConfigKey,\n\tvalue: string | undefined,\n): void {\n\tswitch (key) {\n\t\tcase 'gk-last-accessed':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.lastAccessedAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.lastAccessedAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-last-modified':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.lastModifiedAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.lastModifiedAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-agent-last-activity':\n\t\t\tif (value != null) {\n\t\t\t\tmetadata.agentLastActivityAt = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.agentLastActivityAt;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'gk-disposition':\n\t\t\tif (value === 'starred' || value === 'archived') {\n\t\t\t\tmetadata.disposition = value;\n\t\t\t} else {\n\t\t\t\tdelete metadata.disposition;\n\t\t\t}\n\t\t\tbreak;\n\t}\n}\n"],"mappings":"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"}
@@ -10,6 +10,8 @@ interface GitBranchReferenceOptions {
10
10
  name: string;
11
11
  missing: boolean;
12
12
  remote?: string;
13
+ remoteBranch?: string;
14
+ local?: boolean;
13
15
  };
14
16
  worktree?: {
15
17
  path: string;