@gitkraken/core-gitlens 0.16.2 → 0.16.4
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.
- package/CHANGELOG.md +33 -1
- package/dist/git/cache.d.ts +64 -5
- package/dist/git/cache.js +75 -5
- package/dist/git/cache.js.map +1 -1
- package/dist/git/models/branch.d.ts +0 -6
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/providers/branches.d.ts +65 -1
- package/dist/git/providers/refs.d.ts +3 -2
- package/dist/git/providers/shared/remotes.js +5 -5
- package/dist/git/providers/shared/remotes.js.map +1 -1
- package/dist/git/watching/classifyChange.js +5 -2
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/watchGroup.js +2 -1
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +151 -31
- package/dist/git-cli/providers/branches.js +918 -253
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.js +37 -20
- package/dist/git-cli/providers/commits.js.map +1 -1
- package/dist/git-cli/providers/config.js +16 -10
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.js +3 -3
- package/dist/git-cli/providers/contributors.js.map +1 -1
- package/dist/git-cli/providers/diff.js +15 -12
- package/dist/git-cli/providers/diff.js.map +1 -1
- package/dist/git-cli/providers/graph.js +17 -14
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/maintenance.js +3 -3
- package/dist/git-cli/providers/maintenance.js.map +1 -1
- package/dist/git-cli/providers/patch.js +4 -1
- package/dist/git-cli/providers/patch.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +2 -2
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.js +16 -13
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/remotes.js +4 -1
- package/dist/git-cli/providers/remotes.js.map +1 -1
- package/dist/git-cli/providers/revision.js +14 -11
- package/dist/git-cli/providers/revision.js.map +1 -1
- package/dist/git-cli/providers/stash.js +2 -2
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/status.js +7 -7
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/tags.js +6 -3
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/git-cli/providers/worktrees.js +3 -3
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/plus/git-github/providers/github/branches.d.ts +9 -1
- package/dist/plus/git-github/providers/github/branches.js +54 -8
- package/dist/plus/git-github/providers/github/branches.js.map +1 -1
- package/dist/plus/integrations/providers/issueSorts.d.ts +1 -0
- package/dist/plus/integrations/reads/drains.d.ts +1 -0
- package/dist/utils/decorators/log.d.ts +3 -1
- package/dist/utils/decorators/log.js +61 -45
- package/dist/utils/decorators/log.js.map +1 -1
- package/dist/utils/logger.d.ts +15 -1
- package/dist/utils/logger.js +30 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/logger.scoped.d.ts +12 -1
- package/dist/utils/logger.scoped.js +38 -4
- package/dist/utils/logger.scoped.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MaybePausedResult } from "../../utils/promise.js";
|
|
2
1
|
import { Shape } from "../../utils/types.js";
|
|
3
2
|
import { GitBranchReference } from "./reference.js";
|
|
4
3
|
//#region ../git/src/models/branch.d.ts
|
|
@@ -104,10 +103,5 @@ export interface GitTrackingUpstream {
|
|
|
104
103
|
readonly state: GitTrackingState;
|
|
105
104
|
}
|
|
106
105
|
export type GitBranchStatus = 'local' | 'detached' | 'ahead' | 'behind' | 'diverged' | 'upToDate' | 'missingUpstream' | 'remote';
|
|
107
|
-
export interface BranchTargetInfo {
|
|
108
|
-
mergeTargetBranch: MaybePausedResult<string | undefined>;
|
|
109
|
-
baseBranch: string | undefined;
|
|
110
|
-
defaultBranch: string | undefined;
|
|
111
|
-
}
|
|
112
106
|
//#endregion
|
|
113
107
|
//# sourceMappingURL=branch.d.ts.map
|
|
@@ -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?.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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"}
|
|
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 { 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"],"mappings":"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"}
|
|
@@ -44,6 +44,47 @@ export interface AheadOfBase {
|
|
|
44
44
|
/** The default branch the read resolved from local refs and the remotes' `HEAD` symrefs, `null` when none did; absent when it didn't consult the default branch (a recorded candidate decided the base first) */
|
|
45
45
|
readonly defaultBranch?: string | null;
|
|
46
46
|
}
|
|
47
|
+
/** Which source decided a merge target (see `getMergeTarget`): an {@link AheadOfBaseTier}, the pull request's base, or the branch HEAD was on when the branch was created */
|
|
48
|
+
export type MergeTargetTier = AheadOfBaseTier | 'pr' | 'inferred';
|
|
49
|
+
export interface MergeTarget {
|
|
50
|
+
/** The ref the branch is measured against: the chosen target or its tracking ref, whichever the branch forked from nearer */
|
|
51
|
+
readonly ref: string;
|
|
52
|
+
/** Where the branch forked from `ref`: git's `--fork-point` reading of `ref`'s reflog, else the merge base; `undefined` when they share no history */
|
|
53
|
+
readonly forkPoint: string | undefined;
|
|
54
|
+
readonly tier: MergeTargetTier;
|
|
55
|
+
}
|
|
56
|
+
export interface MergeTargetOptions {
|
|
57
|
+
/** Leaves the user's merge target out: the order a merge-target picker shows as "automatic" */
|
|
58
|
+
skipUserTarget?: boolean;
|
|
59
|
+
/** A target to try before every other source, as `getAheadOfBase`'s */
|
|
60
|
+
preferredTarget?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Read only when no stored merge target is usable. Its base, on the branch's remote, is tried after the stored
|
|
63
|
+
* targets and before the recorded base, and is stored as the detected target (never the user's) unless the pull
|
|
64
|
+
* request is part of a stack or the branch is a remote-tracking one
|
|
65
|
+
*/
|
|
66
|
+
getAssociatedPullRequest?: () => Promise<{
|
|
67
|
+
refs?: {
|
|
68
|
+
base?: {
|
|
69
|
+
branch: string;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
stack?: unknown;
|
|
73
|
+
} | undefined>;
|
|
74
|
+
/**
|
|
75
|
+
* Also reads VS Code's `vscode-merge-base` as the recorded base, and, for a branch its reflog says was created from
|
|
76
|
+
* `HEAD`, takes the branch HEAD was on when it was first checked out (tier `'inferred'`). The inferred branch is
|
|
77
|
+
* never stored
|
|
78
|
+
*/
|
|
79
|
+
inferBase?: boolean;
|
|
80
|
+
/** `'remote'` lets the default-branch read contact the remote when local refs name none; `'local'` (the default) never does */
|
|
81
|
+
defaultBranch?: 'local' | 'remote';
|
|
82
|
+
/** Asked for the default branch when git names none */
|
|
83
|
+
getFallbackDefaultBranch?: () => Promise<string | undefined>;
|
|
84
|
+
/** Answers a target that is the branch itself at the branch's own commit (including its upstream there), instead of `undefined` */
|
|
85
|
+
keepSelfAtTip?: boolean;
|
|
86
|
+
priority?: GitCommandPriority;
|
|
87
|
+
}
|
|
47
88
|
/** A ref {@link GitPushTarget} names, with its commit from the cached ref list */
|
|
48
89
|
export interface GitPushTargetRef {
|
|
49
90
|
/** The full ref name: `refs/remotes/origin/main`, or `refs/heads/main` for an upstream that is a local branch */
|
|
@@ -90,8 +131,11 @@ export interface GitBranchesSubProvider {
|
|
|
90
131
|
paging?: PagingOptions | undefined;
|
|
91
132
|
sort?: boolean | BranchSortOptions | undefined;
|
|
92
133
|
}, cancellation?: AbortSignal): Promise<PagedResult<GitBranch>>;
|
|
134
|
+
/** Measured against the branch's {@link getMergeTarget}, which reads the pull request and the reflog guess, may ask the remote for its default branch, and keeps a target that is the branch itself */
|
|
93
135
|
getBranchContributionsOverview(repoPath: string, ref: string, options?: {
|
|
94
136
|
associatedPullRequest?: Promise<PullRequest | undefined>;
|
|
137
|
+
/** Asked for the default branch when git names none (see {@link MergeTargetOptions.getFallbackDefaultBranch}) */
|
|
138
|
+
getFallbackDefaultBranch?: () => Promise<string | undefined>;
|
|
95
139
|
priority?: GitCommandPriority;
|
|
96
140
|
}, cancellation?: AbortSignal): Promise<BranchContributionsOverview | undefined>;
|
|
97
141
|
getBranchesWithCommits(repoPath: string, shas: string[], branch?: string, options?: {
|
|
@@ -103,6 +147,25 @@ export interface GitBranchesSubProvider {
|
|
|
103
147
|
mode?: 'contains' | 'pointsAt';
|
|
104
148
|
remotes?: boolean;
|
|
105
149
|
}, cancellation?: AbortSignal): Promise<string[]>;
|
|
150
|
+
/**
|
|
151
|
+
* The ref `branch` (a local branch, or a remote-tracking one such as `origin/feature`) is measured against, by the
|
|
152
|
+
* rules of {@link getAheadOfBase} but measured from the branch's own tip rather than HEAD: `preferredTarget`, the
|
|
153
|
+
* user's merge target (unless `skipUserTarget`), the detected one, the pull request's base (`getAssociatedPullRequest`),
|
|
154
|
+
* the recorded base, then — on the default branch — its upstream (tier `'upstream'`), else the branch the reflog
|
|
155
|
+
* says it was created from (or, with `inferBase`, the one HEAD was on when it was first checked out: tier
|
|
156
|
+
* `'inferred'`) unless the default branch forked strictly nearer, else the default branch. The default branch is
|
|
157
|
+
* read from local refs; with `defaultBranch: 'remote'` the remote is asked when they name none, and
|
|
158
|
+
* `getFallbackDefaultBranch` last. A candidate that is empty, flag-like or the branch itself falls through, as does
|
|
159
|
+
* one git confirms is gone; one that couldn't be checked is kept. A remote-tracking branch has no stored targets,
|
|
160
|
+
* reflog or upstream of its own (its pull request's base is still tried), and is itself as its local branch or its
|
|
161
|
+
* copy on any remote. `undefined` for a detached HEAD or an empty name, when nothing resolves, or — unless
|
|
162
|
+
* `keepSelfAtTip` — when the target is the branch itself at the branch's own commit.
|
|
163
|
+
*
|
|
164
|
+
* Answers are cached with the ref tips, stored keys and config they were read from, and a later call re-reads
|
|
165
|
+
* only those (all cached) to confirm them, so a warm call runs no git, and a ref moving that the answer didn't
|
|
166
|
+
* read re-measures nothing. A later call asks the pull request again only when the answer reached it and stored nothing.
|
|
167
|
+
*/
|
|
168
|
+
getMergeTarget(repoPath: string, branch: string, options?: MergeTargetOptions, cancellation?: AbortSignal): Promise<MergeTarget | undefined>;
|
|
106
169
|
getDefaultBranchName(repoPath: string | undefined, remote?: string,
|
|
107
170
|
/**
|
|
108
171
|
* `local`: resolve only from the local `refs/remotes/<remote>/HEAD` symref and never contact the
|
|
@@ -150,7 +213,8 @@ export interface GitBranchesSubProvider {
|
|
|
150
213
|
* detached) was cut from. Candidates in trust
|
|
151
214
|
* order: the user's merge target, the detected one, then the base recorded against the branch — only recorded bases
|
|
152
215
|
* count, never a reflog guess. Each is measured as itself or its tracking ref, whichever forked nearer HEAD; a stale
|
|
153
|
-
* target (one
|
|
216
|
+
* target (one git confirms names no ref, a flag-like one, or the branch itself) falls through to the next — one that
|
|
217
|
+
* couldn't be checked is kept, so its count rejects — and the default branch
|
|
154
218
|
* is used when none survives: the `HEAD` symref of the `upstream` remote, else `origin`, else the first remote, as its
|
|
155
219
|
* local branch or else the remote ref; then, in the same remote order, `<remote>/main` or `<remote>/master`, again as
|
|
156
220
|
* the local branch or else the remote ref; then `init.defaultBranch`, `main`, `master` — from local refs only, never
|
|
@@ -18,8 +18,9 @@ export interface GitRefsSubProvider {
|
|
|
18
18
|
}, cancellation?: AbortSignal): Promise<GitReference | undefined>;
|
|
19
19
|
/**
|
|
20
20
|
* Reads `ref`'s reflog, newest first, optionally keeping only entries whose message matches `grep` (git's
|
|
21
|
-
* `--grep-reflog` pattern). Resolves `[]` when nothing matches or `ref` has no
|
|
22
|
-
* read fails (e.g. `ref` doesn't exist), so "no answer" is distinguishable from
|
|
21
|
+
* `--grep-reflog` pattern, as a basic regular expression). Resolves `[]` when nothing matches or `ref` has no
|
|
22
|
+
* reflog, and rejects when the read fails (e.g. `ref` doesn't exist), so "no answer" is distinguishable from
|
|
23
|
+
* "never read".
|
|
23
24
|
*/
|
|
24
25
|
getReflogEntries?(repoPath: string, ref: string, options?: {
|
|
25
26
|
grep?: string;
|
|
@@ -42,11 +42,11 @@ var RemotesGitProviderBase = class {
|
|
|
42
42
|
this.context.hooks?.repository?.onChanged?.(repoPath, ["remotes", "remoteProviders"]);
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
|
-
__decorate([debug()], RemotesGitProviderBase.prototype, "getRemote", null);
|
|
46
|
-
__decorate([debug()], RemotesGitProviderBase.prototype, "getDefaultRemote", null);
|
|
47
|
-
__decorate([debug()], RemotesGitProviderBase.prototype, "getRemotesWithProviders", null);
|
|
48
|
-
__decorate([debug()], RemotesGitProviderBase.prototype, "getBestRemoteWithProvider", null);
|
|
49
|
-
__decorate([debug()], RemotesGitProviderBase.prototype, "getBestRemotesWithProviders", null);
|
|
45
|
+
__decorate([debug({ quiet: true })], RemotesGitProviderBase.prototype, "getRemote", null);
|
|
46
|
+
__decorate([debug({ quiet: true })], RemotesGitProviderBase.prototype, "getDefaultRemote", null);
|
|
47
|
+
__decorate([debug({ quiet: true })], RemotesGitProviderBase.prototype, "getRemotesWithProviders", null);
|
|
48
|
+
__decorate([debug({ quiet: true })], RemotesGitProviderBase.prototype, "getBestRemoteWithProvider", null);
|
|
49
|
+
__decorate([debug({ quiet: true })], RemotesGitProviderBase.prototype, "getBestRemotesWithProviders", null);
|
|
50
50
|
__decorate([debug()], RemotesGitProviderBase.prototype, "setRemoteAsDefault", null);
|
|
51
51
|
//#endregion
|
|
52
52
|
export { RemotesGitProviderBase };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remotes.js","names":[],"sources":["../../../../../git/src/providers/shared/remotes.ts"],"sourcesContent":["import { debug } from '@gitlens/utils/decorators/log.js';\nimport { sortCompare } from '@gitlens/utils/string.js';\nimport type { Cache } from '../../cache.js';\nimport type { GitServiceContext } from '../../context.js';\nimport type { GitRemote } from '../../models/remote.js';\nimport type { RemoteProvider } from '../../models/remoteProvider.js';\nimport { getDefaultRemoteOrHighlander } from '../../utils/remote.utils.js';\nimport type { GitProvider } from '../provider.js';\nimport type { GitRemotesSubProvider } from '../remotes.js';\n\nexport abstract class RemotesGitProviderBase implements GitRemotesSubProvider {\n\tconstructor(\n\t\tprotected readonly context: GitServiceContext,\n\t\tprotected readonly cache: Cache,\n\t\tprotected readonly provider: GitProvider,\n\t) {}\n\n\tabstract getRemotes(\n\t\trepoPath: string | undefined,\n\t\toptions?: { filter?: (remote: GitRemote) => boolean; sort?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote[]>;\n\n\t@debug()\n\tasync getRemote(\n\t\trepoPath: string | undefined,\n\t\tname: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote | undefined> {\n\t\tif (repoPath == null) return undefined;\n\n\t\tconst remotes = await this.getRemotes(repoPath, undefined, cancellation);\n\t\treturn remotes.find(r => r.name === name);\n\t}\n\n\t@debug()\n\tasync getDefaultRemote(repoPath: string, cancellation?: AbortSignal): Promise<GitRemote | undefined> {\n\t\tconst remotes = await this.getRemotes(repoPath, undefined, cancellation);\n\t\treturn getDefaultRemoteOrHighlander(remotes);\n\t}\n\n\t@debug()\n\tasync getRemotesWithProviders(\n\t\trepoPath: string,\n\t\toptions?: { sort?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote<RemoteProvider>[]> {\n\t\tconst remotes = await this.getRemotes(repoPath, options, cancellation);\n\t\treturn remotes.filter((r): r is GitRemote<RemoteProvider> => r.provider != null);\n\t}\n\n\t@debug()\n\tasync getBestRemoteWithProvider(\n\t\trepoPath: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote<RemoteProvider> | undefined> {\n\t\tconst remotes = await this.getBestRemotesWithProviders(repoPath, cancellation);\n\t\treturn remotes[0];\n\t}\n\n\t@debug()\n\tasync getBestRemotesWithProviders(\n\t\trepoPath: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote<RemoteProvider>[]> {\n\t\tif (!repoPath) return [];\n\n\t\tconst remotes = this.cache.bestRemotes.getOrCreate(\n\t\t\trepoPath,\n\t\t\tasync (_cacheable, aggregateSignal) => {\n\t\t\t\tconst remotes = await this.getRemotesWithProviders(repoPath, { sort: true }, aggregateSignal);\n\t\t\t\tif (remotes.length === 0) return [];\n\t\t\t\tif (remotes.length === 1) return [...remotes];\n\n\t\t\t\tif (this.context.remotes?.sort != null) {\n\t\t\t\t\treturn this.context.remotes.sort(remotes, aggregateSignal);\n\t\t\t\t}\n\n\t\t\t\t// Fallback: sort by name when no host sort is provided\n\t\t\t\treturn remotes.toSorted((a, b) => sortCompare(a.name, b.name));\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\n\t\treturn [...(await remotes)];\n\t}\n\n\t@debug()\n\tasync setRemoteAsDefault(repoPath: string, name: string, value: boolean = true): Promise<void> {\n\t\t// setGkConfig is optional — providers that don't support git config (e.g. GitHub) silently skip persistence\n\t\tawait this.provider.config.setGkConfig?.(repoPath, 'gk.defaultRemote', value ? name : undefined);\n\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes');\n\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['remotes', 'remoteProviders']);\n\t}\n}\n"],"mappings":";;;;;AAUA,IAAsB,yBAAtB,MAA8E;CAEzD;CACA;CACA;CAHpB,YACC,SACA,OACA,UACC;EAHkB,KAAA,UAAA;EACA,KAAA,QAAA;EACA,KAAA,WAAA;CACjB;CAQH,MACM,UACL,UACA,MACA,cACiC;EACjC,IAAI,YAAY,MAAM,OAAO,KAAA;EAG7B,QAAO,MADe,KAAK,WAAW,UAAU,KAAA,GAAW,YAAY,EAAA,CACxD,MAAK,MAAK,EAAE,SAAS,IAAI;CACzC;CAEA,MACM,iBAAiB,UAAkB,cAA4D;EACpG,MAAM,UAAU,MAAM,KAAK,WAAW,UAAU,KAAA,GAAW,YAAY;EACvE,OAAO,6BAA6B,OAAO;CAC5C;CAEA,MACM,wBACL,UACA,SACA,cACuC;EAEvC,QAAO,MADe,KAAK,WAAW,UAAU,SAAS,YAAY,EAAA,CACtD,QAAQ,MAAsC,EAAE,YAAY,IAAI;CAChF;CAEA,MACM,0BACL,UACA,cACiD;EAEjD,QAAO,MADe,KAAK,4BAA4B,UAAU,YAAY,EAAA,CAC9D;CAChB;CAEA,MACM,4BACL,UACA,cACuC;EACvC,IAAI,CAAC,UAAU,OAAO,CAAC;EAmBvB,OAAO,CAAC,GAAI,MAjBI,KAAK,MAAM,YAAY,YACtC,UACA,OAAO,YAAY,oBAAoB;GACtC,MAAM,UAAU,MAAM,KAAK,wBAAwB,UAAU,EAAE,MAAM,KAAK,GAAG,eAAe;GAC5F,IAAI,QAAQ,WAAW,GAAG,OAAO,CAAC;GAClC,IAAI,QAAQ,WAAW,GAAG,OAAO,CAAC,GAAG,OAAO;GAE5C,IAAI,KAAK,QAAQ,SAAS,QAAQ,MACjC,OAAO,KAAK,QAAQ,QAAQ,KAAK,SAAS,eAAe;GAI1D,OAAO,QAAQ,UAAU,GAAG,MAAM,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC;EAC9D,GACA,YAGuB,CAAE;CAC3B;CAEA,MACM,mBAAmB,UAAkB,MAAc,QAAiB,MAAqB;EAE9F,MAAM,KAAK,SAAS,OAAO,cAAc,UAAU,oBAAoB,QAAQ,OAAO,KAAA,CAAS;EAC/F,KAAK,QAAQ,OAAO,OAAO,UAAU,UAAU,SAAS;EACxD,KAAK,QAAQ,OAAO,YAAY,YAAY,UAAU,CAAC,WAAW,iBAAiB,CAAC;CACrF;AACD;AAvEE,WAAA,CAAA,MAAM,CAAA,GAAA,uBAAA,WAAA,aAAA,IAAA;
|
|
1
|
+
{"version":3,"file":"remotes.js","names":[],"sources":["../../../../../git/src/providers/shared/remotes.ts"],"sourcesContent":["import { debug } from '@gitlens/utils/decorators/log.js';\nimport { sortCompare } from '@gitlens/utils/string.js';\nimport type { Cache } from '../../cache.js';\nimport type { GitServiceContext } from '../../context.js';\nimport type { GitRemote } from '../../models/remote.js';\nimport type { RemoteProvider } from '../../models/remoteProvider.js';\nimport { getDefaultRemoteOrHighlander } from '../../utils/remote.utils.js';\nimport type { GitProvider } from '../provider.js';\nimport type { GitRemotesSubProvider } from '../remotes.js';\n\nexport abstract class RemotesGitProviderBase implements GitRemotesSubProvider {\n\tconstructor(\n\t\tprotected readonly context: GitServiceContext,\n\t\tprotected readonly cache: Cache,\n\t\tprotected readonly provider: GitProvider,\n\t) {}\n\n\tabstract getRemotes(\n\t\trepoPath: string | undefined,\n\t\toptions?: { filter?: (remote: GitRemote) => boolean; sort?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote[]>;\n\n\t@debug({ quiet: true })\n\tasync getRemote(\n\t\trepoPath: string | undefined,\n\t\tname: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote | undefined> {\n\t\tif (repoPath == null) return undefined;\n\n\t\tconst remotes = await this.getRemotes(repoPath, undefined, cancellation);\n\t\treturn remotes.find(r => r.name === name);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getDefaultRemote(repoPath: string, cancellation?: AbortSignal): Promise<GitRemote | undefined> {\n\t\tconst remotes = await this.getRemotes(repoPath, undefined, cancellation);\n\t\treturn getDefaultRemoteOrHighlander(remotes);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getRemotesWithProviders(\n\t\trepoPath: string,\n\t\toptions?: { sort?: boolean },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote<RemoteProvider>[]> {\n\t\tconst remotes = await this.getRemotes(repoPath, options, cancellation);\n\t\treturn remotes.filter((r): r is GitRemote<RemoteProvider> => r.provider != null);\n\t}\n\n\t@debug({ quiet: true })\n\tasync getBestRemoteWithProvider(\n\t\trepoPath: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote<RemoteProvider> | undefined> {\n\t\tconst remotes = await this.getBestRemotesWithProviders(repoPath, cancellation);\n\t\treturn remotes[0];\n\t}\n\n\t@debug({ quiet: true })\n\tasync getBestRemotesWithProviders(\n\t\trepoPath: string,\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitRemote<RemoteProvider>[]> {\n\t\tif (!repoPath) return [];\n\n\t\tconst remotes = this.cache.bestRemotes.getOrCreate(\n\t\t\trepoPath,\n\t\t\tasync (_cacheable, aggregateSignal) => {\n\t\t\t\tconst remotes = await this.getRemotesWithProviders(repoPath, { sort: true }, aggregateSignal);\n\t\t\t\tif (remotes.length === 0) return [];\n\t\t\t\tif (remotes.length === 1) return [...remotes];\n\n\t\t\t\tif (this.context.remotes?.sort != null) {\n\t\t\t\t\treturn this.context.remotes.sort(remotes, aggregateSignal);\n\t\t\t\t}\n\n\t\t\t\t// Fallback: sort by name when no host sort is provided\n\t\t\t\treturn remotes.toSorted((a, b) => sortCompare(a.name, b.name));\n\t\t\t},\n\t\t\tcancellation,\n\t\t);\n\n\t\treturn [...(await remotes)];\n\t}\n\n\t@debug()\n\tasync setRemoteAsDefault(repoPath: string, name: string, value: boolean = true): Promise<void> {\n\t\t// setGkConfig is optional — providers that don't support git config (e.g. GitHub) silently skip persistence\n\t\tawait this.provider.config.setGkConfig?.(repoPath, 'gk.defaultRemote', value ? name : undefined);\n\t\tthis.context.hooks?.cache?.onReset?.(repoPath, 'remotes');\n\t\tthis.context.hooks?.repository?.onChanged?.(repoPath, ['remotes', 'remoteProviders']);\n\t}\n}\n"],"mappings":";;;;;AAUA,IAAsB,yBAAtB,MAA8E;CAEzD;CACA;CACA;CAHpB,YACC,SACA,OACA,UACC;EAHkB,KAAA,UAAA;EACA,KAAA,QAAA;EACA,KAAA,WAAA;CACjB;CAQH,MACM,UACL,UACA,MACA,cACiC;EACjC,IAAI,YAAY,MAAM,OAAO,KAAA;EAG7B,QAAO,MADe,KAAK,WAAW,UAAU,KAAA,GAAW,YAAY,EAAA,CACxD,MAAK,MAAK,EAAE,SAAS,IAAI;CACzC;CAEA,MACM,iBAAiB,UAAkB,cAA4D;EACpG,MAAM,UAAU,MAAM,KAAK,WAAW,UAAU,KAAA,GAAW,YAAY;EACvE,OAAO,6BAA6B,OAAO;CAC5C;CAEA,MACM,wBACL,UACA,SACA,cACuC;EAEvC,QAAO,MADe,KAAK,WAAW,UAAU,SAAS,YAAY,EAAA,CACtD,QAAQ,MAAsC,EAAE,YAAY,IAAI;CAChF;CAEA,MACM,0BACL,UACA,cACiD;EAEjD,QAAO,MADe,KAAK,4BAA4B,UAAU,YAAY,EAAA,CAC9D;CAChB;CAEA,MACM,4BACL,UACA,cACuC;EACvC,IAAI,CAAC,UAAU,OAAO,CAAC;EAmBvB,OAAO,CAAC,GAAI,MAjBI,KAAK,MAAM,YAAY,YACtC,UACA,OAAO,YAAY,oBAAoB;GACtC,MAAM,UAAU,MAAM,KAAK,wBAAwB,UAAU,EAAE,MAAM,KAAK,GAAG,eAAe;GAC5F,IAAI,QAAQ,WAAW,GAAG,OAAO,CAAC;GAClC,IAAI,QAAQ,WAAW,GAAG,OAAO,CAAC,GAAG,OAAO;GAE5C,IAAI,KAAK,QAAQ,SAAS,QAAQ,MACjC,OAAO,KAAK,QAAQ,QAAQ,KAAK,SAAS,eAAe;GAI1D,OAAO,QAAQ,UAAU,GAAG,MAAM,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC;EAC9D,GACA,YAGuB,CAAE;CAC3B;CAEA,MACM,mBAAmB,UAAkB,MAAc,QAAiB,MAAqB;EAE9F,MAAM,KAAK,SAAS,OAAO,cAAc,UAAU,oBAAoB,QAAQ,OAAO,KAAA,CAAS;EAC/F,KAAK,QAAQ,OAAO,OAAO,UAAU,UAAU,SAAS;EACxD,KAAK,QAAQ,OAAO,YAAY,YAAY,UAAU,CAAC,WAAW,iBAAiB,CAAC;CACrF;AACD;AAvEE,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,uBAAA,WAAA,aAAA,IAAA;AAYrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,uBAAA,WAAA,oBAAA,IAAA;AAMrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,uBAAA,WAAA,2BAAA,IAAA;AAUrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,uBAAA,WAAA,6BAAA,IAAA;AASrB,WAAA,CAAA,MAAM,EAAE,OAAO,KAAK,CAAC,CAAA,GAAA,uBAAA,WAAA,+BAAA,IAAA;AA2BrB,WAAA,CAAA,MAAM,CAAA,GAAA,uBAAA,WAAA,sBAAA,IAAA"}
|
|
@@ -22,7 +22,7 @@ const allWatchedRepositoryChanges = [
|
|
|
22
22
|
"gkConfig"
|
|
23
23
|
];
|
|
24
24
|
const classifyRegex = /(index|HEAD|FETCH_HEAD|ORIG_HEAD|CHERRY_PICK_HEAD|MERGE_HEAD|REBASE_HEAD|rebase-merge|rebase-apply|sequencer|REVERT_HEAD|config|gk\/config|info\/exclude|refs\/(?:heads|remotes|stash|tags|replace)|packed-refs|shallow)/;
|
|
25
|
-
/** A ref, ref directory or ref lock in a namespace {@link classifyRegex} maps
|
|
25
|
+
/** A ref, ref directory or ref lock in a namespace {@link classifyRegex} maps; of the locks, only the stash's counts */
|
|
26
26
|
const watchedRefRegex = /^refs\/(?:heads|remotes|stash|tags|replace)(?:\/|\.lock$|$)/;
|
|
27
27
|
/**
|
|
28
28
|
* Maps a path relative to a `.git` directory to the corresponding
|
|
@@ -53,7 +53,10 @@ function classifyGitDirChange(relativePath) {
|
|
|
53
53
|
return file === "HEAD" || file === "locked" || file === "gitdir" ? ["worktrees"] : void 0;
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
if (relativePath.startsWith("refs/")
|
|
56
|
+
if (relativePath.startsWith("refs/")) {
|
|
57
|
+
if (!watchedRefRegex.test(relativePath)) return void 0;
|
|
58
|
+
if (relativePath.endsWith(".lock") && relativePath !== "refs/stash.lock") return void 0;
|
|
59
|
+
}
|
|
57
60
|
const match = classifyRegex.exec(relativePath);
|
|
58
61
|
if (match == null) return void 0;
|
|
59
62
|
switch (match[1]) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"classifyChange.js","names":[],"sources":["../../../../git/src/watching/classifyChange.ts"],"sourcesContent":["import type { RepositoryChange } from '../models/repository.js';\n\n/**\n * Everything a `.git` watcher can report. Used when events were lost (`overflow`), so every cache type those\n * kinds clear is refreshed. Deliberately not `'unknown'`: `Cache.onRepositoryChanged` treats that as a close and\n * unregisters the path, after which shared evictions stop reaching it.\n */\nexport const allWatchedRepositoryChanges: readonly RepositoryChange[] = [\n\t'index',\n\t'head',\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'config',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'pausedOp',\n\t'ignores',\n\t'gkConfig',\n];\n\nconst classifyRegex =\n\t/(index|HEAD|FETCH_HEAD|ORIG_HEAD|CHERRY_PICK_HEAD|MERGE_HEAD|REBASE_HEAD|rebase-merge|rebase-apply|sequencer|REVERT_HEAD|config|gk\\/config|info\\/exclude|refs\\/(?:heads|remotes|stash|tags|replace)|packed-refs|shallow)/;\n/** A ref, ref directory or ref lock in a namespace {@link classifyRegex} maps
|
|
1
|
+
{"version":3,"file":"classifyChange.js","names":[],"sources":["../../../../git/src/watching/classifyChange.ts"],"sourcesContent":["import type { RepositoryChange } from '../models/repository.js';\n\n/**\n * Everything a `.git` watcher can report. Used when events were lost (`overflow`), so every cache type those\n * kinds clear is refreshed. Deliberately not `'unknown'`: `Cache.onRepositoryChanged` treats that as a close and\n * unregisters the path, after which shared evictions stop reaching it.\n */\nexport const allWatchedRepositoryChanges: readonly RepositoryChange[] = [\n\t'index',\n\t'head',\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'config',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'pausedOp',\n\t'ignores',\n\t'gkConfig',\n];\n\nconst classifyRegex =\n\t/(index|HEAD|FETCH_HEAD|ORIG_HEAD|CHERRY_PICK_HEAD|MERGE_HEAD|REBASE_HEAD|rebase-merge|rebase-apply|sequencer|REVERT_HEAD|config|gk\\/config|info\\/exclude|refs\\/(?:heads|remotes|stash|tags|replace)|packed-refs|shallow)/;\n/** A ref, ref directory or ref lock in a namespace {@link classifyRegex} maps; of the locks, only the stash's counts */\nconst watchedRefRegex = /^refs\\/(?:heads|remotes|stash|tags|replace)(?:\\/|\\.lock$|$)/;\n\n/**\n * Maps a path relative to a `.git` directory to the corresponding\n * {@link RepositoryChange} types. This is pure Git knowledge — the\n * regex and switch table encode which files in `.git/` correspond\n * to which logical repository changes.\n *\n * `worktrees` means the worktree list, or what `git worktree list` reports per worktree, changed:\n * `worktrees`, `worktrees/<n>` (add/remove/prune) and `worktrees/<n>/{HEAD,locked,gitdir}`. Any other\n * path under `worktrees/<n>/` (index, `*_HEAD`, `MERGE_*`, rebase state, logs, …) is that worktree's\n * own state, which its own gitdir watcher classifies, so it yields `undefined` here.\n *\n * @param relativePath - Path relative to the common `.git` directory or a worktree's gitdir\n * (e.g., `refs/heads/main`, `HEAD`, `config`, `worktrees/foo/HEAD`)\n * @returns An array of change types, or `undefined` if the path\n * is noise or unrecognized. `undefined` for `FETCH_HEAD` which\n * is intentionally not mapped to a change type (the extension\n * handles it as a last-fetched timestamp).\n */\nexport function classifyGitDirChange(relativePath: string): RepositoryChange[] | undefined {\n\t// User-edited todo file (and its backup) — handled by VS Code's TextDocument\n\t// events in the rebase webview (see `rebaseWebviewProvider.ts`). Suppress here\n\t// to avoid thrashing the repo update path on every save during an interactive\n\t// rebase; rebase lifecycle (start/stop/step) still fires from sibling files\n\t// in `rebase-merge/` and the directory itself.\n\tif (relativePath.endsWith('/git-rebase-todo') || relativePath.endsWith('/git-rebase-todo.backup')) {\n\t\treturn undefined;\n\t}\n\n\tif (relativePath.startsWith('worktrees')) {\n\t\tif (relativePath.length === 9) return ['worktrees'];\n\n\t\tif (relativePath.charCodeAt(9) === 47 /* / */) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd === -1) return relativePath.length > 10 ? ['worktrees'] : undefined;\n\n\t\t\tconst file = relativePath.substring(idEnd + 1);\n\t\t\treturn file === 'HEAD' || file === 'locked' || file === 'gitdir' ? ['worktrees'] : undefined;\n\t\t}\n\t}\n\n\t// A ref outside the namespaces core reads (a tool's own, such as `refs/undo/*`) is no change, whatever words its\n\t// name holds: `refs/kepler/x/fix-index` must not read as the index\n\tif (relativePath.startsWith('refs/')) {\n\t\tif (!watchedRefRegex.test(relativePath)) return undefined;\n\n\t\t// A ref's lock file is no change of the ref (no ref name ends in `.lock`): git renames it over the ref, which\n\t\t// reports the ref itself, or removes it with the ref unchanged, as every fetch does with\n\t\t// `refs/remotes/<remote>/HEAD` to check the remote's default branch. Except the stash's: its entries are its\n\t\t// reflog (`logs/`, unwatched), which `git reflog expire` (as `gc` runs it) and `git reflog delete` rewrite under\n\t\t// that lock alone\n\t\tif (relativePath.endsWith('.lock') && relativePath !== 'refs/stash.lock') return undefined;\n\t}\n\n\tconst match = classifyRegex.exec(relativePath);\n\tif (match == null) return undefined;\n\n\tswitch (match[1]) {\n\t\tcase 'config':\n\t\t\treturn ['config', 'remotes'];\n\n\t\tcase 'gk/config':\n\t\t\treturn ['gkConfig'];\n\n\t\tcase 'info/exclude':\n\t\t\treturn ['ignores'];\n\n\t\tcase 'index':\n\t\t\treturn ['index'];\n\n\t\tcase 'FETCH_HEAD':\n\t\t\t// No RepositoryChange — extension handles this for last-fetched timestamps\n\t\t\treturn undefined;\n\n\t\tcase 'HEAD':\n\t\t\treturn ['head', 'heads'];\n\n\t\tcase 'ORIG_HEAD':\n\t\t\treturn ['heads'];\n\n\t\tcase 'shallow':\n\t\tcase 'refs/replace':\n\t\t\t// Ancestry changes affect graph, blame/history, health, and ahead/behind consumers even\n\t\t\t// when tips stay put. Reuse their shared history invalidation rather than only refreshing Graph.\n\t\t\treturn ['heads'];\n\n\t\tcase 'CHERRY_PICK_HEAD':\n\t\t\treturn ['cherryPick', 'pausedOp'];\n\n\t\tcase 'MERGE_HEAD':\n\t\t\treturn ['merge', 'pausedOp'];\n\n\t\tcase 'REBASE_HEAD':\n\t\tcase 'rebase-merge':\n\t\tcase 'rebase-apply':\n\t\t\treturn ['rebase', 'pausedOp'];\n\n\t\tcase 'REVERT_HEAD':\n\t\t\treturn ['revert', 'pausedOp'];\n\n\t\tcase 'sequencer':\n\t\t\treturn ['pausedOp'];\n\n\t\t// `'heads'` is deliberately the ONLY thing a `refs/heads/*` event yields, including a create. It is\n\t\t// tempting to read a create as \"a new branch exists under this name\" and use it to drop anything\n\t\t// derived from the old branch's identity (its base, its merge target). That signal does not mean\n\t\t// what it looks like: git rewrites an EXISTING loose ref by writing `<name>.lock` and renaming it\n\t\t// over the file, which watchers report as a create of the destination — so every ordinary commit\n\t\t// would look like a new branch and discard a live branch's metadata. Nothing here can distinguish\n\t\t// the two; identity changes are handled where GitLens performs the operation itself.\n\t\tcase 'refs/heads':\n\t\t\treturn ['heads'];\n\n\t\tcase 'refs/remotes':\n\t\t\treturn ['remotes'];\n\n\t\tcase 'refs/stash':\n\t\t\treturn ['stash'];\n\n\t\tcase 'refs/tags':\n\t\t\treturn ['tags'];\n\n\t\t// A `packed-refs` rewrite (after `git pack-refs` or `git gc`) changes ref state\n\t\t// WITHOUT any `refs/**` file event — deleting a packed branch, for example, only\n\t\t// rewrites this file. The file content is not inspected here, so classification\n\t\t// is deliberately broad: it can add, remove, or move refs of any of these types.\n\t\tcase 'packed-refs':\n\t\t\treturn ['heads', 'remotes', 'tags'];\n\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n"],"mappings":";;;;;;AAOA,MAAa,8BAA2D;CACvE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD;AAEA,MAAM,gBACL;;AAED,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;AAoBxB,SAAgB,qBAAqB,cAAsD;CAM1F,IAAI,aAAa,SAAS,kBAAkB,KAAK,aAAa,SAAS,yBAAyB,GAC/F;CAGD,IAAI,aAAa,WAAW,WAAW,GAAG;EACzC,IAAI,aAAa,WAAW,GAAG,OAAO,CAAC,WAAW;EAElD,IAAI,aAAa,WAAW,CAAC,MAAM,IAAY;GAC9C,MAAM,QAAQ,aAAa,QAAQ,KAAK,EAAE;GAC1C,IAAI,UAAU,IAAI,OAAO,aAAa,SAAS,KAAK,CAAC,WAAW,IAAI,KAAA;GAEpE,MAAM,OAAO,aAAa,UAAU,QAAQ,CAAC;GAC7C,OAAO,SAAS,UAAU,SAAS,YAAY,SAAS,WAAW,CAAC,WAAW,IAAI,KAAA;EACpF;CACD;CAIA,IAAI,aAAa,WAAW,OAAO,GAAG;EACrC,IAAI,CAAC,gBAAgB,KAAK,YAAY,GAAG,OAAO,KAAA;EAOhD,IAAI,aAAa,SAAS,OAAO,KAAK,iBAAiB,mBAAmB,OAAO,KAAA;CAClF;CAEA,MAAM,QAAQ,cAAc,KAAK,YAAY;CAC7C,IAAI,SAAS,MAAM,OAAO,KAAA;CAE1B,QAAQ,MAAM,IAAd;EACC,KAAK,UACJ,OAAO,CAAC,UAAU,SAAS;EAE5B,KAAK,aACJ,OAAO,CAAC,UAAU;EAEnB,KAAK,gBACJ,OAAO,CAAC,SAAS;EAElB,KAAK,SACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,cAEJ;EAED,KAAK,QACJ,OAAO,CAAC,QAAQ,OAAO;EAExB,KAAK,aACJ,OAAO,CAAC,OAAO;EAEhB,KAAK;EACL,KAAK,gBAGJ,OAAO,CAAC,OAAO;EAEhB,KAAK,oBACJ,OAAO,CAAC,cAAc,UAAU;EAEjC,KAAK,cACJ,OAAO,CAAC,SAAS,UAAU;EAE5B,KAAK;EACL,KAAK;EACL,KAAK,gBACJ,OAAO,CAAC,UAAU,UAAU;EAE7B,KAAK,eACJ,OAAO,CAAC,UAAU,UAAU;EAE7B,KAAK,aACJ,OAAO,CAAC,UAAU;EASnB,KAAK,cACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,gBACJ,OAAO,CAAC,SAAS;EAElB,KAAK,cACJ,OAAO,CAAC,OAAO;EAEhB,KAAK,aACJ,OAAO,CAAC,MAAM;EAMf,KAAK,eACJ,OAAO;GAAC;GAAS;GAAW;EAAM;EAEnC,SACC;CACF;AACD"}
|
|
@@ -372,7 +372,8 @@ function isConfigFile(relativePath) {
|
|
|
372
372
|
}
|
|
373
373
|
/**
|
|
374
374
|
* The ref a `.git`-relative path names: a loose ref (or ref directory) under `refs/`, or the `HEAD`/`ORIG_HEAD`
|
|
375
|
-
* pseudo-refs.
|
|
375
|
+
* pseudo-refs. A lock file names the ref it locks; the only ones classification lets through are the pseudo-refs' and
|
|
376
|
+
* `refs/stash.lock`.
|
|
376
377
|
*/
|
|
377
378
|
function getChangedRef(relativePath) {
|
|
378
379
|
const ref = relativePath.endsWith(".lock") ? relativePath.substring(0, relativePath.length - 5) : relativePath;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watchGroup.js","names":[],"sources":["../../../../git/src/watching/watchGroup.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { isDescendant, joinPaths, relative } from '@gitlens/utils/path.js';\nimport type { GitDir, RepositoryChange } from '../models/repository.js';\nimport { allWatchedRepositoryChanges, classifyGitDirChange } from './classifyChange.js';\nimport type { GitConfigSections, GitConfigSectionsChange } from './configSections.js';\nimport { diffGitConfigSections, parseGitConfigBytes } from './configSections.js';\nimport type { GitDirPoller, GitDirProbe } from './gitDirPoller.js';\nimport { GitDirProbeTarget } from './gitDirPoller.js';\nimport type {\n\tFileWatcher,\n\tFileWatcherOptions,\n\tFileWatcherReliability,\n\tFileWatchEvent,\n\tFileWatchingProvider,\n} from './provider.js';\nimport {\n\tdotGitGlobCombined,\n\tdotGitGlobCommon,\n\tdotGitGlobRoot,\n\tgetGitDirRefProbes,\n\tgitDirCommonProbes,\n\tgitDirExcludes,\n\tgitDirProbesByEmittedPath,\n\tgitDirRootProbes,\n\tshouldIgnoreRepoPath,\n} from './watcherPatterns.js';\n\n/**\n * How long a read of the shared `.git/config` may take before it counts as failed (unconfined). Reads run one at a\n * time, so one that never settles, as on a hung network or WSL mount, would otherwise hold back every later report.\n */\nconst configReadTimeoutMs = 5000;\n\n/**\n * Hooks that the WatchGroup fires when events are interpreted.\n * These are raw, un-debounced events — the session handles debouncing.\n */\nexport interface WatchGroupHooks {\n\t/**\n\t * `ref` is the full ref name (or ref directory) or pseudo-ref (`HEAD`, `ORIG_HEAD`) whose file changed, with a\n\t * trailing `.lock` stripped; `false` when config changed without moving refs, and `undefined` when ref movement\n\t * is unknown. `overflow` marks the every-kind rescan of a watcher that dropped events, which no single write can\n\t * account for. `configBranches`, with a `config` change, are the local branches (full ref names) whose\n\t * `branch.<name>` sections are all that changed in the shared `.git/config`; without it, the config change may be\n\t * anywhere.\n\t */\n\treadonly onRepoChanged: (\n\t\trepoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\tref?: string | false,\n\t\toverflow?: boolean,\n\t\tconfigBranches?: ReadonlySet<string>,\n\t) => void;\n\treadonly onFetchHeadChanged?: (repoPath: string) => void;\n\treadonly onIgnoresChanged?: (repoPath: string) => void;\n\t/** The refs whose loose files a poll stats for this session, read at each poll */\n\treadonly getProbeRefs?: () => Iterable<string>;\n\t/** A watcher this session hears through went `unwatchable`, or stopped being it (see {@link WatchGroup.isUnwatchable}) */\n\treadonly onUnwatchableChanged?: (repoPath: string) => void;\n}\n\n/**\n * Tracks one session's connection to a WatchGroup.\n * Each session has its own root watcher but shares the common watcher.\n */\ninterface SessionEntry {\n\treadonly repoPath: string;\n\treadonly gitDirPath: string;\n\treadonly rootWatcher: FileWatcher;\n\t/** True when the root watcher is on the common dir itself, so it already sees every common file */\n\treadonly coversCommon: boolean;\n\t/** For a linked session, its `<n>` in `<common>/worktrees/<n>`; otherwise `undefined` */\n\treadonly worktreeId: string | undefined;\n\treadonly hooks: WatchGroupHooks;\n\treliability: FileWatcherReliability;\n\t/** Polls in place of (or beside) the root watcher while it isn't `reliable` */\n\ttarget?: GitDirProbeTarget;\n}\n\n/**\n * Manages filesystem watchers for a single physical `.git` directory,\n * shared across all worktrees that reference it.\n *\n * Structure:\n * - 1 common watcher: config, refs/**, info/exclude, etc. Delivers to linked sessions only; a\n * session rooted at the common dir hears the same files from its own combined root watcher.\n * - N root watchers: one per worktree (index, HEAD, *_HEAD, etc.)\n *\n * For a standard (non-worktree) repo, the common and root directories\n * are the same, but we still create separate watchers for clean separation.\n *\n * Given a `readFile`, the group keeps the shared `.git/config` as it last read it, section by section. A report of\n * that file (or its lock), from whichever watcher, is read and compared with it once, and every session hears what\n * changed: only some branches' `branch.<name>` sections (`config`, confined to them), anything else (`config` and\n * `remotes`, unconfined), or nothing at all, when an earlier report's read already delivered the change. Reads run\n * one at a time, so each compares with the one before it and none is skipped. The first read, taken as the group\n * starts, is the baseline and delivers nothing; a report arriving before it completes may describe a change it\n * already holds, so it is delivered unconfined. A read that fails or doesn't settle within a few seconds, or a file\n * git couldn't parse, is unconfined too.\n */\nexport class WatchGroup implements UnifiedDisposable {\n\tprivate readonly _sessions = new Map<string, SessionEntry>();\n\tprivate _commonWatcher: FileWatcher | undefined;\n\tprivate _commonReliability: FileWatcherReliability = 'reliable';\n\tprivate _commonTarget: GitDirProbeTarget | undefined;\n\tprivate _disposed = false;\n\n\t/** The shared `.git/config`, by section, as the last read that could parse it found it */\n\tprivate _config: GitConfigSections | undefined;\n\t/** The baseline read is in flight */\n\tprivate _configBaselining = false;\n\tprivate _configReading = false;\n\tprivate _configRereadQueued = false;\n\n\tconstructor(\n\t\treadonly commonGitDir: string,\n\t\tprivate readonly provider: FileWatchingProvider,\n\t\t/** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */\n\t\tprivate readonly poller?: GitDirPoller,\n\t\t/** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */\n\t\tprivate readonly readFile?: (path: string) => Promise<Uint8Array>,\n\t) {\n\t\tif (readFile != null) {\n\t\t\tvoid this.readConfigBaseline();\n\t\t}\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\t/** Dispose the entire group (common watcher + all root watchers). */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tentry.rootWatcher.dispose();\n\t\t\tthis.stopPolling(entry);\n\t\t}\n\t\tthis._sessions.clear();\n\n\t\tthis.disposeCommonWatcher();\n\t}\n\n\tget sessions(): ReadonlyMap<string, SessionEntry> {\n\t\treturn this._sessions;\n\t}\n\n\t/** True when a watcher the session hears through — its root watcher, or a linked session's common one — can't watch */\n\tisUnwatchable(repoPath: string): boolean {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return false;\n\n\t\treturn (\n\t\t\tentry.reliability === 'unwatchable' || (!entry.coversCommon && this._commonReliability === 'unwatchable')\n\t\t);\n\t}\n\n\t/** Add a session to this group. Creates the root watcher for the session's gitDir. */\n\taddSession(repoPath: string, gitDir: GitDir, hooks: WatchGroupHooks): void {\n\t\tif (this._disposed) return;\n\t\tif (this._sessions.has(repoPath)) return; // Already tracked\n\n\t\t// Determine the glob pattern for the root watcher\n\t\tconst isStandard = gitDir.commonUri == null;\n\t\tconst rootPattern = isStandard ? dotGitGlobCombined : dotGitGlobRoot;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns, so before the entry exists\n\t\tlet entry: SessionEntry | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\n\t\tconst rootWatcher = this.provider.createWatcher(\n\t\t\tgitDir.uri.fsPath,\n\t\t\trootPattern,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tconst e = this._sessions.get(repoPath);\n\t\t\t\tif (e != null) {\n\t\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\t\te.target?.forget(event.path);\n\t\t\t\t\t}\n\t\t\t\t\tthis.onRootEvent(event, e);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.getWatcherOptions(state => {\n\t\t\t\tif (entry == null) {\n\t\t\t\t\tinitialReliability = state;\n\t\t\t\t} else if (this._sessions.get(repoPath) === entry) {\n\t\t\t\t\tthis.setRootReliability(entry, state);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tconst entryGitDirPath = gitDir.uri.fsPath;\n\t\tentry = {\n\t\t\trepoPath: repoPath,\n\t\t\tgitDirPath: entryGitDirPath,\n\t\t\trootWatcher: rootWatcher,\n\t\t\tcoversCommon: isStandard,\n\t\t\tworktreeId: isStandard ? undefined : this.getWorktreeId(entryGitDirPath),\n\t\t\thooks: hooks,\n\t\t\treliability: 'reliable',\n\t\t};\n\n\t\tthis._sessions.set(repoPath, entry);\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setRootReliability(entry, initialReliability, true);\n\t\t}\n\n\t\t// Start the common watcher if this is a worktree repo and we haven't yet.\n\t\t// Standard repos don't need a separate common watcher because their root\n\t\t// watcher uses dotGitGlobCombined which already covers all patterns.\n\t\tif (!isStandard) {\n\t\t\tthis.ensureCommonWatcher();\n\t\t}\n\t}\n\n\t/** Remove a session from this group. Disposes its root watcher. */\n\tremoveSession(repoPath: string): void {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return;\n\n\t\tentry.rootWatcher.dispose();\n\t\tthis.stopPolling(entry);\n\t\tthis._sessions.delete(repoPath);\n\n\t\t// Once no linked session is left, dispose the common watcher — it delivers to linked sessions only\n\t\tif (this._commonWatcher != null && !some(this._sessions.values(), e => !e.coversCommon)) {\n\t\t\tthis.disposeCommonWatcher();\n\t\t}\n\t}\n\n\tprivate getWorktreeId(gitDirPath: string): string | undefined {\n\t\tif (!isDescendant(gitDirPath, this.commonGitDir)) return undefined;\n\n\t\tconst rel = relative(this.commonGitDir, gitDirPath);\n\t\tif (!rel.startsWith('worktrees/')) return undefined;\n\n\t\tconst id = rel.substring(10);\n\t\treturn id.length > 0 && !id.includes('/') ? id : undefined;\n\t}\n\n\tprivate ensureCommonWatcher(): void {\n\t\tif (this._commonWatcher != null || this._disposed) return;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns\n\t\tlet watcher: FileWatcher | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\t\tconst options = this.getWatcherOptions(state => {\n\t\t\tif (watcher == null) {\n\t\t\t\tinitialReliability = state;\n\t\t\t} else if (this._commonWatcher === watcher) {\n\t\t\t\tthis.setCommonReliability(state);\n\t\t\t}\n\t\t});\n\n\t\twatcher = this.provider.createWatcher(\n\t\t\tthis.commonGitDir,\n\t\t\tdotGitGlobCommon,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\tthis._commonTarget?.forget(event.path);\n\t\t\t\t}\n\t\t\t\tthis.onCommonEvent(event);\n\t\t\t},\n\t\t\toptions,\n\t\t);\n\t\tthis._commonWatcher = watcher;\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setCommonReliability(initialReliability, true);\n\t\t}\n\t}\n\n\tprivate disposeCommonWatcher(): void {\n\t\tthis._commonWatcher?.dispose();\n\t\tthis._commonWatcher = undefined;\n\t\tthis._commonReliability = 'reliable';\n\t\tif (this._commonTarget != null) {\n\t\t\tthis.poller?.delete(this._commonTarget);\n\t\t\tthis._commonTarget = undefined;\n\t\t}\n\t}\n\n\t/** Hosts that didn't opt into polling get exactly the options they always had */\n\tprivate getWatcherOptions(onDidChangeReliability: (state: FileWatcherReliability) => void): FileWatcherOptions {\n\t\treturn this.poller != null\n\t\t\t? { excludes: gitDirExcludes, onDidChangeReliability: onDidChangeReliability }\n\t\t\t: { excludes: gitDirExcludes };\n\t}\n\n\tprivate setRootReliability(entry: SessionEntry, state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = entry.reliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tentry.reliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tthis.stopPolling(entry);\n\t\t} else if (entry.target == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tentry.gitDirPath,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getRootProbes(entry),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\t(event, probe) => {\n\t\t\t\t\tif (entry.target !== target) return;\n\n\t\t\t\t\tthis.onRootEvent(event, entry);\n\t\t\t\t\tif (probe.path === 'logs/HEAD' && !entry.coversCommon) {\n\t\t\t\t\t\tthis.onLinkedHeadMoved();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tentry.target = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\t// The watch went down or came back: whatever changed meanwhile went unseen\n\t\tif (!initial) {\n\t\t\tthis.deliverOverflow(entry);\n\t\t}\n\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t}\n\n\tprivate setCommonReliability(state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = this._commonReliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tthis._commonReliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tif (this._commonTarget != null) {\n\t\t\t\tthis.poller.delete(this._commonTarget);\n\t\t\t\tthis._commonTarget = undefined;\n\t\t\t}\n\t\t} else if (this._commonTarget == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tthis.commonGitDir,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getCommonProbes(),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\tevent => {\n\t\t\t\t\tif (this._commonTarget === target) {\n\t\t\t\t\t\tthis.onCommonEvent(event);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._commonTarget = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (!initial) {\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\tprivate stopPolling(entry: SessionEntry): void {\n\t\tif (entry.target == null) return;\n\n\t\tthis.poller?.delete(entry.target);\n\t\tentry.target = undefined;\n\t}\n\n\t/**\n\t * A session rooted at the common dir polls the common files too, with the refs of every session in the group: its\n\t * combined watcher would hear them all. While the common dir is polled too, the poller stats each shared file once\n\t * for both, and each keeps its own readings, so what one watcher reported is held back from its own sessions only\n\t */\n\tprivate getRootProbes(entry: SessionEntry): readonly GitDirProbe[] {\n\t\tif (!entry.coversCommon) return gitDirRootProbes;\n\n\t\tconst refs = new Set<string>();\n\t\tfor (const e of this._sessions.values()) {\n\t\t\tfor (const ref of e.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirRootProbes, ...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\t/**\n\t * A linked worktree's `logs/HEAD` moved — a commit or reset there, on a branch a polled session rooted at the common\n\t * dir may not probe — so each such session hears a `heads` change its combined watcher would have heard\n\t */\n\tprivate onLinkedHeadMoved(): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (!entry.coversCommon || entry.target == null) continue;\n\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['heads']);\n\t\t}\n\t}\n\n\t/** The common files, with the refs of every linked session (the only ones the common watcher delivers to) */\n\tprivate getCommonProbes(): GitDirProbe[] {\n\t\tconst refs = new Set<string>();\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tfor (const ref of entry.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\tprivate onCommonEvent(event: FileWatchEvent): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tfor (const entry of this._sessions.values()) {\n\t\t\t\tif (entry.coversCommon) continue;\n\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Compute relative path from common git dir\n\t\tif (!isDescendant(event.path, this.commonGitDir)) return;\n\n\t\tconst relativePath = relative(this.commonGitDir, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\tif (this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst isFetchHead = relativePath === 'FETCH_HEAD';\n\t\tconst isExclude = relativePath === 'info/exclude';\n\t\tconst changes = isFetchHead ? undefined : classifyGitDirChange(relativePath);\n\t\tif (changes == null && !isFetchHead && !isExclude) return;\n\n\t\tconst ref = changes != null ? getChangedRef(relativePath) : undefined;\n\n\t\t// `worktrees/<n>/HEAD`: the owner hears its own HEAD from its root watcher. Its `locked` and `gitdir` aren't in\n\t\t// the root glob, so the owner hears those only from here\n\t\tlet ownerId: string | undefined;\n\t\tif (changes != null && relativePath.length > 10 && relativePath.startsWith('worktrees/')) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd !== -1 && relativePath.substring(idEnd + 1) === 'HEAD') {\n\t\t\t\townerId = relativePath.substring(10, idEnd);\n\t\t\t}\n\t\t}\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\t// A session rooted at the common dir sees every common file (hooks included) through its own\n\t\t\t// combined root watcher, so delivering here too would duplicate it\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (isFetchHead) {\n\t\t\t\t// FETCH_HEAD lives in the main .git dir; each worktree updates its last-fetched state\n\t\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (changes != null && (ownerId == null || entry.worktreeId !== ownerId)) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, ref);\n\t\t\t}\n\n\t\t\tif (isExclude) {\n\t\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate deliverOverflow(entry: SessionEntry): void {\n\t\t// Every kind, no ref (ref kinds stay unnamed). Never `'unknown'` — the cache would unregister the path\n\t\tentry.hooks.onRepoChanged(entry.repoPath, [...allWatchedRepositoryChanges], undefined, true);\n\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t}\n\n\tprivate onRootEvent(event: FileWatchEvent, entry: SessionEntry): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tthis.deliverOverflow(entry);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isDescendant(event.path, entry.gitDirPath)) return;\n\n\t\tconst relativePath = relative(entry.gitDirPath, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\t// Check for FETCH_HEAD before interpretation\n\t\tif (relativePath === 'FETCH_HEAD') {\n\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\t// A linked worktree's git dir isn't the common one, so no file there is the shared config\n\t\tif (entry.coversCommon && this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst changes = classifyGitDirChange(relativePath);\n\t\tif (changes != null) {\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, getChangedRef(relativePath));\n\t\t}\n\n\t\tif (relativePath === 'info/exclude') {\n\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\tprivate onConfigReported(): void {\n\t\t// The baseline may already hold the change reported, which comparing with it would lose\n\t\tif (this._configBaselining) {\n\t\t\tthis.deliverConfigChange(undefined);\n\t\t\treturn;\n\t\t}\n\t\tif (this._configReading) {\n\t\t\tthis._configRereadQueued = true;\n\t\t\treturn;\n\t\t}\n\n\t\tvoid this.readConfigChanges();\n\t}\n\n\tprivate async readConfigBaseline(): Promise<void> {\n\t\tthis._configBaselining = true;\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tconst config = await this.readConfig();\n\t\t\tif (this._disposed) return;\n\n\t\t\tthis._config = config;\n\t\t} finally {\n\t\t\tthis._configBaselining = false;\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** Reads and compares until no report came in during the last read: it may describe a change the read missed */\n\tprivate async readConfigChanges(): Promise<void> {\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tthis._configRereadQueued = false;\n\t\t\t\tconst config = await this.readConfig();\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tconst previous = this._config;\n\t\t\t\tif (config == null || previous == null) {\n\t\t\t\t\t// Unreadable now, or never readable before: the change may be anywhere. A failed read keeps the last\n\t\t\t\t\t// snapshot, so the next read still compares with what every session was told\n\t\t\t\t\tthis._config ??= config;\n\t\t\t\t\tthis.deliverConfigChange(undefined);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis._config = config;\n\t\t\t\tconst change = diffGitConfigSections(previous, config);\n\t\t\t\tif (change != null) {\n\t\t\t\t\tthis.deliverConfigChange(change);\n\t\t\t\t}\n\t\t\t} while (this._configRereadQueued);\n\t\t} finally {\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** `undefined` when the file couldn't be read or parsed, or the read didn't settle in time */\n\tprivate async readConfig(): Promise<GitConfigSections | undefined> {\n\t\tconst readFile = this.readFile;\n\t\tif (readFile == null) return undefined;\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tconst data = await Promise.race([\n\t\t\t\treadFile(joinPaths(this.commonGitDir, 'config')),\n\t\t\t\tnew Promise<undefined>(resolve => {\n\t\t\t\t\ttimer = setTimeout(resolve, configReadTimeoutMs, undefined);\n\t\t\t\t}),\n\t\t\t]);\n\t\t\treturn data != null ? parseGitConfigBytes(data) : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t}\n\n\t/**\n\t * Tells every session of a change to the shared `.git/config`: confined to some branches' sections, or (`undefined`,\n\t * or a change outside them) anywhere, which may have changed a remote's settings too. Either way no ref moved\n\t */\n\tprivate deliverConfigChange(change: GitConfigSectionsChange | undefined): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (change?.confined) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config'], false, false, change.branches);\n\t\t\t} else {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config', 'remotes'], false);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The shared `.git/config` (or the lock git writes it through), relative to the common git dir */\nfunction isConfigFile(relativePath: string): boolean {\n\treturn relativePath === 'config' || relativePath === 'config.lock';\n}\n\n/**\n * The ref a `.git`-relative path names: a loose ref (or ref directory) under `refs/`, or the `HEAD`/`ORIG_HEAD`\n * pseudo-refs. Git writes a ref as `<name>.lock` and renames it over `<name>`, so the lock file names the same ref.\n */\nfunction getChangedRef(relativePath: string): string | false | undefined {\n\tconst ref = relativePath.endsWith('.lock') ? relativePath.substring(0, relativePath.length - 5) : relativePath;\n\t// Remote config still invalidates remote and branch caches, but doesn't itself move any ref.\n\tif (ref === 'config' || ref === 'config.worktree') return false;\n\n\treturn ref.startsWith('refs/') || ref === 'HEAD' || ref === 'ORIG_HEAD' ? ref : undefined;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"watchGroup.js","names":[],"sources":["../../../../git/src/watching/watchGroup.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { isDescendant, joinPaths, relative } from '@gitlens/utils/path.js';\nimport type { GitDir, RepositoryChange } from '../models/repository.js';\nimport { allWatchedRepositoryChanges, classifyGitDirChange } from './classifyChange.js';\nimport type { GitConfigSections, GitConfigSectionsChange } from './configSections.js';\nimport { diffGitConfigSections, parseGitConfigBytes } from './configSections.js';\nimport type { GitDirPoller, GitDirProbe } from './gitDirPoller.js';\nimport { GitDirProbeTarget } from './gitDirPoller.js';\nimport type {\n\tFileWatcher,\n\tFileWatcherOptions,\n\tFileWatcherReliability,\n\tFileWatchEvent,\n\tFileWatchingProvider,\n} from './provider.js';\nimport {\n\tdotGitGlobCombined,\n\tdotGitGlobCommon,\n\tdotGitGlobRoot,\n\tgetGitDirRefProbes,\n\tgitDirCommonProbes,\n\tgitDirExcludes,\n\tgitDirProbesByEmittedPath,\n\tgitDirRootProbes,\n\tshouldIgnoreRepoPath,\n} from './watcherPatterns.js';\n\n/**\n * How long a read of the shared `.git/config` may take before it counts as failed (unconfined). Reads run one at a\n * time, so one that never settles, as on a hung network or WSL mount, would otherwise hold back every later report.\n */\nconst configReadTimeoutMs = 5000;\n\n/**\n * Hooks that the WatchGroup fires when events are interpreted.\n * These are raw, un-debounced events — the session handles debouncing.\n */\nexport interface WatchGroupHooks {\n\t/**\n\t * `ref` is the full ref name (or ref directory) or pseudo-ref (`HEAD`, `ORIG_HEAD`) whose file changed, with a\n\t * trailing `.lock` stripped; `false` when config changed without moving refs, and `undefined` when ref movement\n\t * is unknown. `overflow` marks the every-kind rescan of a watcher that dropped events, which no single write can\n\t * account for. `configBranches`, with a `config` change, are the local branches (full ref names) whose\n\t * `branch.<name>` sections are all that changed in the shared `.git/config`; without it, the config change may be\n\t * anywhere.\n\t */\n\treadonly onRepoChanged: (\n\t\trepoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\tref?: string | false,\n\t\toverflow?: boolean,\n\t\tconfigBranches?: ReadonlySet<string>,\n\t) => void;\n\treadonly onFetchHeadChanged?: (repoPath: string) => void;\n\treadonly onIgnoresChanged?: (repoPath: string) => void;\n\t/** The refs whose loose files a poll stats for this session, read at each poll */\n\treadonly getProbeRefs?: () => Iterable<string>;\n\t/** A watcher this session hears through went `unwatchable`, or stopped being it (see {@link WatchGroup.isUnwatchable}) */\n\treadonly onUnwatchableChanged?: (repoPath: string) => void;\n}\n\n/**\n * Tracks one session's connection to a WatchGroup.\n * Each session has its own root watcher but shares the common watcher.\n */\ninterface SessionEntry {\n\treadonly repoPath: string;\n\treadonly gitDirPath: string;\n\treadonly rootWatcher: FileWatcher;\n\t/** True when the root watcher is on the common dir itself, so it already sees every common file */\n\treadonly coversCommon: boolean;\n\t/** For a linked session, its `<n>` in `<common>/worktrees/<n>`; otherwise `undefined` */\n\treadonly worktreeId: string | undefined;\n\treadonly hooks: WatchGroupHooks;\n\treliability: FileWatcherReliability;\n\t/** Polls in place of (or beside) the root watcher while it isn't `reliable` */\n\ttarget?: GitDirProbeTarget;\n}\n\n/**\n * Manages filesystem watchers for a single physical `.git` directory,\n * shared across all worktrees that reference it.\n *\n * Structure:\n * - 1 common watcher: config, refs/**, info/exclude, etc. Delivers to linked sessions only; a\n * session rooted at the common dir hears the same files from its own combined root watcher.\n * - N root watchers: one per worktree (index, HEAD, *_HEAD, etc.)\n *\n * For a standard (non-worktree) repo, the common and root directories\n * are the same, but we still create separate watchers for clean separation.\n *\n * Given a `readFile`, the group keeps the shared `.git/config` as it last read it, section by section. A report of\n * that file (or its lock), from whichever watcher, is read and compared with it once, and every session hears what\n * changed: only some branches' `branch.<name>` sections (`config`, confined to them), anything else (`config` and\n * `remotes`, unconfined), or nothing at all, when an earlier report's read already delivered the change. Reads run\n * one at a time, so each compares with the one before it and none is skipped. The first read, taken as the group\n * starts, is the baseline and delivers nothing; a report arriving before it completes may describe a change it\n * already holds, so it is delivered unconfined. A read that fails or doesn't settle within a few seconds, or a file\n * git couldn't parse, is unconfined too.\n */\nexport class WatchGroup implements UnifiedDisposable {\n\tprivate readonly _sessions = new Map<string, SessionEntry>();\n\tprivate _commonWatcher: FileWatcher | undefined;\n\tprivate _commonReliability: FileWatcherReliability = 'reliable';\n\tprivate _commonTarget: GitDirProbeTarget | undefined;\n\tprivate _disposed = false;\n\n\t/** The shared `.git/config`, by section, as the last read that could parse it found it */\n\tprivate _config: GitConfigSections | undefined;\n\t/** The baseline read is in flight */\n\tprivate _configBaselining = false;\n\tprivate _configReading = false;\n\tprivate _configRereadQueued = false;\n\n\tconstructor(\n\t\treadonly commonGitDir: string,\n\t\tprivate readonly provider: FileWatchingProvider,\n\t\t/** When given, a watcher that reports it can't be trusted is polled; otherwise none is asked */\n\t\tprivate readonly poller?: GitDirPoller,\n\t\t/** When given, reports of the shared `.git/config` are classified by what changed in it (see above) */\n\t\tprivate readonly readFile?: (path: string) => Promise<Uint8Array>,\n\t) {\n\t\tif (readFile != null) {\n\t\t\tvoid this.readConfigBaseline();\n\t\t}\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\t/** Dispose the entire group (common watcher + all root watchers). */\n\tdispose(): void {\n\t\tif (this._disposed) return;\n\n\t\tthis._disposed = true;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tentry.rootWatcher.dispose();\n\t\t\tthis.stopPolling(entry);\n\t\t}\n\t\tthis._sessions.clear();\n\n\t\tthis.disposeCommonWatcher();\n\t}\n\n\tget sessions(): ReadonlyMap<string, SessionEntry> {\n\t\treturn this._sessions;\n\t}\n\n\t/** True when a watcher the session hears through — its root watcher, or a linked session's common one — can't watch */\n\tisUnwatchable(repoPath: string): boolean {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return false;\n\n\t\treturn (\n\t\t\tentry.reliability === 'unwatchable' || (!entry.coversCommon && this._commonReliability === 'unwatchable')\n\t\t);\n\t}\n\n\t/** Add a session to this group. Creates the root watcher for the session's gitDir. */\n\taddSession(repoPath: string, gitDir: GitDir, hooks: WatchGroupHooks): void {\n\t\tif (this._disposed) return;\n\t\tif (this._sessions.has(repoPath)) return; // Already tracked\n\n\t\t// Determine the glob pattern for the root watcher\n\t\tconst isStandard = gitDir.commonUri == null;\n\t\tconst rootPattern = isStandard ? dotGitGlobCombined : dotGitGlobRoot;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns, so before the entry exists\n\t\tlet entry: SessionEntry | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\n\t\tconst rootWatcher = this.provider.createWatcher(\n\t\t\tgitDir.uri.fsPath,\n\t\t\trootPattern,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tconst e = this._sessions.get(repoPath);\n\t\t\t\tif (e != null) {\n\t\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\t\te.target?.forget(event.path);\n\t\t\t\t\t}\n\t\t\t\t\tthis.onRootEvent(event, e);\n\t\t\t\t}\n\t\t\t},\n\t\t\tthis.getWatcherOptions(state => {\n\t\t\t\tif (entry == null) {\n\t\t\t\t\tinitialReliability = state;\n\t\t\t\t} else if (this._sessions.get(repoPath) === entry) {\n\t\t\t\t\tthis.setRootReliability(entry, state);\n\t\t\t\t}\n\t\t\t}),\n\t\t);\n\n\t\tconst entryGitDirPath = gitDir.uri.fsPath;\n\t\tentry = {\n\t\t\trepoPath: repoPath,\n\t\t\tgitDirPath: entryGitDirPath,\n\t\t\trootWatcher: rootWatcher,\n\t\t\tcoversCommon: isStandard,\n\t\t\tworktreeId: isStandard ? undefined : this.getWorktreeId(entryGitDirPath),\n\t\t\thooks: hooks,\n\t\t\treliability: 'reliable',\n\t\t};\n\n\t\tthis._sessions.set(repoPath, entry);\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setRootReliability(entry, initialReliability, true);\n\t\t}\n\n\t\t// Start the common watcher if this is a worktree repo and we haven't yet.\n\t\t// Standard repos don't need a separate common watcher because their root\n\t\t// watcher uses dotGitGlobCombined which already covers all patterns.\n\t\tif (!isStandard) {\n\t\t\tthis.ensureCommonWatcher();\n\t\t}\n\t}\n\n\t/** Remove a session from this group. Disposes its root watcher. */\n\tremoveSession(repoPath: string): void {\n\t\tconst entry = this._sessions.get(repoPath);\n\t\tif (entry == null) return;\n\n\t\tentry.rootWatcher.dispose();\n\t\tthis.stopPolling(entry);\n\t\tthis._sessions.delete(repoPath);\n\n\t\t// Once no linked session is left, dispose the common watcher — it delivers to linked sessions only\n\t\tif (this._commonWatcher != null && !some(this._sessions.values(), e => !e.coversCommon)) {\n\t\t\tthis.disposeCommonWatcher();\n\t\t}\n\t}\n\n\tprivate getWorktreeId(gitDirPath: string): string | undefined {\n\t\tif (!isDescendant(gitDirPath, this.commonGitDir)) return undefined;\n\n\t\tconst rel = relative(this.commonGitDir, gitDirPath);\n\t\tif (!rel.startsWith('worktrees/')) return undefined;\n\n\t\tconst id = rel.substring(10);\n\t\treturn id.length > 0 && !id.includes('/') ? id : undefined;\n\t}\n\n\tprivate ensureCommonWatcher(): void {\n\t\tif (this._commonWatcher != null || this._disposed) return;\n\n\t\t// The watcher can report its reliability before `createWatcher` returns\n\t\tlet watcher: FileWatcher | undefined;\n\t\tlet initialReliability: FileWatcherReliability = 'reliable';\n\t\tconst options = this.getWatcherOptions(state => {\n\t\t\tif (watcher == null) {\n\t\t\t\tinitialReliability = state;\n\t\t\t} else if (this._commonWatcher === watcher) {\n\t\t\t\tthis.setCommonReliability(state);\n\t\t\t}\n\t\t});\n\n\t\twatcher = this.provider.createWatcher(\n\t\t\tthis.commonGitDir,\n\t\t\tdotGitGlobCommon,\n\t\t\t(event: FileWatchEvent) => {\n\t\t\t\tif (event.reason !== 'overflow') {\n\t\t\t\t\tthis._commonTarget?.forget(event.path);\n\t\t\t\t}\n\t\t\t\tthis.onCommonEvent(event);\n\t\t\t},\n\t\t\toptions,\n\t\t);\n\t\tthis._commonWatcher = watcher;\n\t\tif (initialReliability !== 'reliable') {\n\t\t\tthis.setCommonReliability(initialReliability, true);\n\t\t}\n\t}\n\n\tprivate disposeCommonWatcher(): void {\n\t\tthis._commonWatcher?.dispose();\n\t\tthis._commonWatcher = undefined;\n\t\tthis._commonReliability = 'reliable';\n\t\tif (this._commonTarget != null) {\n\t\t\tthis.poller?.delete(this._commonTarget);\n\t\t\tthis._commonTarget = undefined;\n\t\t}\n\t}\n\n\t/** Hosts that didn't opt into polling get exactly the options they always had */\n\tprivate getWatcherOptions(onDidChangeReliability: (state: FileWatcherReliability) => void): FileWatcherOptions {\n\t\treturn this.poller != null\n\t\t\t? { excludes: gitDirExcludes, onDidChangeReliability: onDidChangeReliability }\n\t\t\t: { excludes: gitDirExcludes };\n\t}\n\n\tprivate setRootReliability(entry: SessionEntry, state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = entry.reliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tentry.reliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tthis.stopPolling(entry);\n\t\t} else if (entry.target == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tentry.gitDirPath,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getRootProbes(entry),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\t(event, probe) => {\n\t\t\t\t\tif (entry.target !== target) return;\n\n\t\t\t\t\tthis.onRootEvent(event, entry);\n\t\t\t\t\tif (probe.path === 'logs/HEAD' && !entry.coversCommon) {\n\t\t\t\t\t\tthis.onLinkedHeadMoved();\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tentry.target = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\t// The watch went down or came back: whatever changed meanwhile went unseen\n\t\tif (!initial) {\n\t\t\tthis.deliverOverflow(entry);\n\t\t}\n\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t}\n\n\tprivate setCommonReliability(state: FileWatcherReliability, initial?: boolean): void {\n\t\tconst previous = this._commonReliability;\n\t\tif (previous === state || this.poller == null) return;\n\n\t\tthis._commonReliability = state;\n\t\tif (state === 'reliable') {\n\t\t\tif (this._commonTarget != null) {\n\t\t\t\tthis.poller.delete(this._commonTarget);\n\t\t\t\tthis._commonTarget = undefined;\n\t\t\t}\n\t\t} else if (this._commonTarget == null) {\n\t\t\tconst target = new GitDirProbeTarget(\n\t\t\t\tthis.commonGitDir,\n\t\t\t\tthis.poller,\n\t\t\t\t() => this.getCommonProbes(),\n\t\t\t\tgitDirProbesByEmittedPath,\n\t\t\t\tevent => {\n\t\t\t\t\tif (this._commonTarget === target) {\n\t\t\t\t\t\tthis.onCommonEvent(event);\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t);\n\t\t\tthis._commonTarget = target;\n\t\t\tthis.poller.add(target);\n\t\t}\n\n\t\tif ((state === 'unwatchable') === (previous === 'unwatchable')) return;\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (!initial) {\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\tentry.hooks.onUnwatchableChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\tprivate stopPolling(entry: SessionEntry): void {\n\t\tif (entry.target == null) return;\n\n\t\tthis.poller?.delete(entry.target);\n\t\tentry.target = undefined;\n\t}\n\n\t/**\n\t * A session rooted at the common dir polls the common files too, with the refs of every session in the group: its\n\t * combined watcher would hear them all. While the common dir is polled too, the poller stats each shared file once\n\t * for both, and each keeps its own readings, so what one watcher reported is held back from its own sessions only\n\t */\n\tprivate getRootProbes(entry: SessionEntry): readonly GitDirProbe[] {\n\t\tif (!entry.coversCommon) return gitDirRootProbes;\n\n\t\tconst refs = new Set<string>();\n\t\tfor (const e of this._sessions.values()) {\n\t\t\tfor (const ref of e.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirRootProbes, ...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\t/**\n\t * A linked worktree's `logs/HEAD` moved — a commit or reset there, on a branch a polled session rooted at the common\n\t * dir may not probe — so each such session hears a `heads` change its combined watcher would have heard\n\t */\n\tprivate onLinkedHeadMoved(): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (!entry.coversCommon || entry.target == null) continue;\n\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['heads']);\n\t\t}\n\t}\n\n\t/** The common files, with the refs of every linked session (the only ones the common watcher delivers to) */\n\tprivate getCommonProbes(): GitDirProbe[] {\n\t\tconst refs = new Set<string>();\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tfor (const ref of entry.hooks.getProbeRefs?.() ?? []) {\n\t\t\t\trefs.add(ref);\n\t\t\t}\n\t\t}\n\t\treturn [...gitDirCommonProbes, ...getGitDirRefProbes(refs)];\n\t}\n\n\tprivate onCommonEvent(event: FileWatchEvent): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tfor (const entry of this._sessions.values()) {\n\t\t\t\tif (entry.coversCommon) continue;\n\n\t\t\t\tthis.deliverOverflow(entry);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Compute relative path from common git dir\n\t\tif (!isDescendant(event.path, this.commonGitDir)) return;\n\n\t\tconst relativePath = relative(this.commonGitDir, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\tif (this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst isFetchHead = relativePath === 'FETCH_HEAD';\n\t\tconst isExclude = relativePath === 'info/exclude';\n\t\tconst changes = isFetchHead ? undefined : classifyGitDirChange(relativePath);\n\t\tif (changes == null && !isFetchHead && !isExclude) return;\n\n\t\tconst ref = changes != null ? getChangedRef(relativePath) : undefined;\n\n\t\t// `worktrees/<n>/HEAD`: the owner hears its own HEAD from its root watcher. Its `locked` and `gitdir` aren't in\n\t\t// the root glob, so the owner hears those only from here\n\t\tlet ownerId: string | undefined;\n\t\tif (changes != null && relativePath.length > 10 && relativePath.startsWith('worktrees/')) {\n\t\t\tconst idEnd = relativePath.indexOf('/', 10);\n\t\t\tif (idEnd !== -1 && relativePath.substring(idEnd + 1) === 'HEAD') {\n\t\t\t\townerId = relativePath.substring(10, idEnd);\n\t\t\t}\n\t\t}\n\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\t// A session rooted at the common dir sees every common file (hooks included) through its own\n\t\t\t// combined root watcher, so delivering here too would duplicate it\n\t\t\tif (entry.coversCommon) continue;\n\n\t\t\tif (isFetchHead) {\n\t\t\t\t// FETCH_HEAD lives in the main .git dir; each worktree updates its last-fetched state\n\t\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (changes != null && (ownerId == null || entry.worktreeId !== ownerId)) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, ref);\n\t\t\t}\n\n\t\t\tif (isExclude) {\n\t\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate deliverOverflow(entry: SessionEntry): void {\n\t\t// Every kind, no ref (ref kinds stay unnamed). Never `'unknown'` — the cache would unregister the path\n\t\tentry.hooks.onRepoChanged(entry.repoPath, [...allWatchedRepositoryChanges], undefined, true);\n\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t}\n\n\tprivate onRootEvent(event: FileWatchEvent, entry: SessionEntry): void {\n\t\tif (event.reason === 'overflow') {\n\t\t\tthis.deliverOverflow(entry);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!isDescendant(event.path, entry.gitDirPath)) return;\n\n\t\tconst relativePath = relative(entry.gitDirPath, event.path);\n\t\tif (shouldIgnoreRepoPath(relativePath)) return;\n\n\t\t// Check for FETCH_HEAD before interpretation\n\t\tif (relativePath === 'FETCH_HEAD') {\n\t\t\tentry.hooks.onFetchHeadChanged?.(entry.repoPath);\n\t\t\treturn;\n\t\t}\n\n\t\t// A linked worktree's git dir isn't the common one, so no file there is the shared config\n\t\tif (entry.coversCommon && this.readFile != null && isConfigFile(relativePath)) {\n\t\t\tthis.onConfigReported();\n\t\t\treturn;\n\t\t}\n\n\t\tconst changes = classifyGitDirChange(relativePath);\n\t\tif (changes != null) {\n\t\t\tentry.hooks.onRepoChanged(entry.repoPath, changes, getChangedRef(relativePath));\n\t\t}\n\n\t\tif (relativePath === 'info/exclude') {\n\t\t\tentry.hooks.onIgnoresChanged?.(entry.repoPath);\n\t\t}\n\t}\n\n\tprivate onConfigReported(): void {\n\t\t// The baseline may already hold the change reported, which comparing with it would lose\n\t\tif (this._configBaselining) {\n\t\t\tthis.deliverConfigChange(undefined);\n\t\t\treturn;\n\t\t}\n\t\tif (this._configReading) {\n\t\t\tthis._configRereadQueued = true;\n\t\t\treturn;\n\t\t}\n\n\t\tvoid this.readConfigChanges();\n\t}\n\n\tprivate async readConfigBaseline(): Promise<void> {\n\t\tthis._configBaselining = true;\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tconst config = await this.readConfig();\n\t\t\tif (this._disposed) return;\n\n\t\t\tthis._config = config;\n\t\t} finally {\n\t\t\tthis._configBaselining = false;\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** Reads and compares until no report came in during the last read: it may describe a change the read missed */\n\tprivate async readConfigChanges(): Promise<void> {\n\t\tthis._configReading = true;\n\t\ttry {\n\t\t\tdo {\n\t\t\t\tthis._configRereadQueued = false;\n\t\t\t\tconst config = await this.readConfig();\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tconst previous = this._config;\n\t\t\t\tif (config == null || previous == null) {\n\t\t\t\t\t// Unreadable now, or never readable before: the change may be anywhere. A failed read keeps the last\n\t\t\t\t\t// snapshot, so the next read still compares with what every session was told\n\t\t\t\t\tthis._config ??= config;\n\t\t\t\t\tthis.deliverConfigChange(undefined);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis._config = config;\n\t\t\t\tconst change = diffGitConfigSections(previous, config);\n\t\t\t\tif (change != null) {\n\t\t\t\t\tthis.deliverConfigChange(change);\n\t\t\t\t}\n\t\t\t} while (this._configRereadQueued);\n\t\t} finally {\n\t\t\tthis._configReading = false;\n\t\t}\n\t}\n\n\t/** `undefined` when the file couldn't be read or parsed, or the read didn't settle in time */\n\tprivate async readConfig(): Promise<GitConfigSections | undefined> {\n\t\tconst readFile = this.readFile;\n\t\tif (readFile == null) return undefined;\n\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\ttry {\n\t\t\tconst data = await Promise.race([\n\t\t\t\treadFile(joinPaths(this.commonGitDir, 'config')),\n\t\t\t\tnew Promise<undefined>(resolve => {\n\t\t\t\t\ttimer = setTimeout(resolve, configReadTimeoutMs, undefined);\n\t\t\t\t}),\n\t\t\t]);\n\t\t\treturn data != null ? parseGitConfigBytes(data) : undefined;\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t}\n\n\t/**\n\t * Tells every session of a change to the shared `.git/config`: confined to some branches' sections, or (`undefined`,\n\t * or a change outside them) anywhere, which may have changed a remote's settings too. Either way no ref moved\n\t */\n\tprivate deliverConfigChange(change: GitConfigSectionsChange | undefined): void {\n\t\tfor (const entry of this._sessions.values()) {\n\t\t\tif (change?.confined) {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config'], false, false, change.branches);\n\t\t\t} else {\n\t\t\t\tentry.hooks.onRepoChanged(entry.repoPath, ['config', 'remotes'], false);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The shared `.git/config` (or the lock git writes it through), relative to the common git dir */\nfunction isConfigFile(relativePath: string): boolean {\n\treturn relativePath === 'config' || relativePath === 'config.lock';\n}\n\n/**\n * The ref a `.git`-relative path names: a loose ref (or ref directory) under `refs/`, or the `HEAD`/`ORIG_HEAD`\n * pseudo-refs. A lock file names the ref it locks; the only ones classification lets through are the pseudo-refs' and\n * `refs/stash.lock`.\n */\nfunction getChangedRef(relativePath: string): string | false | undefined {\n\tconst ref = relativePath.endsWith('.lock') ? relativePath.substring(0, relativePath.length - 5) : relativePath;\n\t// Remote config still invalidates remote and branch caches, but doesn't itself move any ref.\n\tif (ref === 'config' || ref === 'config.worktree') return false;\n\n\treturn ref.startsWith('refs/') || ref === 'HEAD' || ref === 'ORIG_HEAD' ? ref : undefined;\n}\n"],"mappings":"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"}
|