@gitkraken/core-gitlens 0.10.7 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +39 -1
- package/dist/git/cache.d.ts +34 -2
- package/dist/git/cache.js +55 -7
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +1 -1
- package/dist/git/models/repository.d.ts +3 -2
- package/dist/git/models/repository.js +5 -4
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/providers/branches.d.ts +24 -0
- package/dist/git/providers/config.d.ts +3 -1
- package/dist/git/providers/provider.d.ts +13 -1
- package/dist/git/providers/staging.d.ts +1 -1
- package/dist/git/service.d.ts +17 -1
- package/dist/git/service.js +59 -8
- package/dist/git/service.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +48 -5
- package/dist/git/watching/changeEvent.js +82 -7
- package/dist/git/watching/changeEvent.js.map +1 -1
- package/dist/git/watching/classifyChange.d.ts +13 -2
- package/dist/git/watching/classifyChange.js +40 -5
- package/dist/git/watching/classifyChange.js.map +1 -1
- package/dist/git/watching/gitIgnoreFilter.d.ts +57 -10
- package/dist/git/watching/gitIgnoreFilter.js +186 -29
- package/dist/git/watching/gitIgnoreFilter.js.map +1 -1
- package/dist/git/watching/initWatcher.d.ts +5 -1
- package/dist/git/watching/initWatcher.js +1 -1
- package/dist/git/watching/initWatcher.js.map +1 -1
- package/dist/git/watching/provider.d.ts +33 -8
- package/dist/git/watching/watchGroup.d.ts +13 -2
- package/dist/git/watching/watchGroup.js +60 -14
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +42 -1
- package/dist/git/watching/watchService.js +172 -20
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +50 -17
- package/dist/git/watching/watchSession.js +274 -83
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +13 -2
- package/dist/git/watching/watcherPatterns.js +22 -2
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +132 -0
- package/dist/git/watching/worktreeSummary.js +429 -0
- package/dist/git/watching/worktreeSummary.js.map +1 -0
- package/dist/git-cli/cliGitProvider.d.ts +6 -1
- package/dist/git-cli/cliGitProvider.js +87 -2
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/providers/blame.js +1 -1
- package/dist/git-cli/providers/branches.d.ts +32 -1
- package/dist/git-cli/providers/branches.js +217 -0
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.js +1 -1
- package/dist/git-cli/providers/config.d.ts +3 -0
- package/dist/git-cli/providers/config.js +12 -6
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.js +1 -1
- package/dist/git-cli/providers/graph.js +1 -1
- package/dist/git-cli/providers/maintenance.js +1 -1
- package/dist/git-cli/providers/refs.js +1 -1
- package/dist/git-cli/providers/staging.js +1 -1
- package/dist/git-cli/providers/status.js +1 -1
- package/dist/git-cli/service.js +1 -0
- package/dist/git-cli/service.js.map +1 -1
- package/dist/ipc/ipcServer.js +1 -1
- package/dist/plus/agents/providers/gkAgentProvider.js +2 -2
- package/dist/plus/git-github/api/github.d.ts +1 -1
- package/dist/plus/git-github/api/github.js +1 -1
- package/dist/plus/git-github/providers/github/blame.js +1 -1
- package/dist/plus/integrations/authentication/integrationAuthenticationProvider.js +1 -1
- package/dist/plus/integrations/context.d.ts +1 -1
- package/dist/plus/integrations/integrationService.js +1 -1
- package/dist/plus/integrations/models/integration.js +1 -1
- package/dist/plus/integrations/providers/bitbucket-server.d.ts +1 -1
- package/dist/plus/integrations/reads/batchEtags.d.ts +5 -2
- package/dist/plus/integrations/reads/batchEtags.js +31 -10
- package/dist/plus/integrations/reads/batchEtags.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequestBranches.js +10 -3
- package/dist/plus/integrations/reads/pullRequestBranches.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +16 -1
- package/dist/plus/integrations/results.js +22 -3
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/event.d.ts +1 -1
- package/docs/integrations.md +5 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repository.js","names":[],"sources":["../../../../git/src/models/repository.ts"],"sourcesContent":["import { exhaustiveArray } from '@gitlens/utils/array.js';\nimport { getScopedCounter } from '@gitlens/utils/counter.js';\nimport { md5 } from '@gitlens/utils/crypto.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { basename, isDescendant, normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { GitProviderDescriptor } from '../providers/types.js';\nimport { getCommonRepositoryUri } from '../utils/repository.utils.js';\nimport type { WatcherRepoChangeEvent, WorkingTreeChangeEvent } from '../watching/changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle, WatchHooks } from '../watching/watchService.js';\nimport type { RepositorySubscription } from '../watching/watchSession.js';\nimport { RepositoryChangeEvent } from './repositoryChangeEvent.js';\n\nconst instanceCounter = getScopedCounter();\n\nexport interface GitDir {\n\treadonly uri: Uri;\n\t/** The common git directory for worktrees */\n\treadonly commonUri?: Uri;\n\t/** The parent (superproject) directory for submodules */\n\treadonly parentUri?: Uri;\n}\n\nexport type RepositoryChange =\n\t| 'unknown'\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| 'pausedOp'\n\t| 'cherryPick'\n\t| 'merge'\n\t| 'rebase'\n\t| 'revert'\n\t| 'closed'\n\t| 'ignores'\n\t| 'remoteProviders'\n\t| 'starred'\n\t| 'opened'\n\t| 'gkConfig'\n\t| 'lastFetched';\n\nexport const repositoryChanges = exhaustiveArray<RepositoryChange>()([\n\t'unknown',\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'pausedOp',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'closed',\n\t'ignores',\n\t'remoteProviders',\n\t'starred',\n\t'opened',\n\t'gkConfig',\n\t'lastFetched',\n]);\n\n/**\n * The only changes {@link Repository.fireChange} accepts with `force` — delivered straight to the repository's\n * emitter, bypassing the watch session, so they still arrive while it's suspended or when there's no watch handle\n * at all (a repo being opened, or closed, or a virtual repo that never had one).\n *\n * These have nothing intrinsic in common — being forced IS the shared property, which is why this is keyed off\n * `force` rather than some category of change.\n *\n * It's the partition consumers need: everything NOT listed here goes through the session, which does its\n * pre-notification work (e.g. advancing a cache's status clock) exactly once, before it notifies anyone. Nothing\n * runs upstream of a forced change, so a consumer bridging repository changes into a cache must handle these\n * itself. Forcing a change that isn't listed is a compile error — and consumers gate on this same array, so\n * listing one wires it up for free.\n */\nexport const forcedRepositoryChanges = [\n\t'opened',\n\t'closed',\n\t'lastFetched',\n] as const satisfies readonly RepositoryChange[];\nexport type ForcedRepositoryChange = (typeof forcedRepositoryChanges)[number];\n\nexport interface RepositoryInit {\n\treadonly id: string;\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly name: string;\n\treadonly provider: GitProviderDescriptor;\n\treadonly gitDir: GitDir | undefined;\n\treadonly index: number;\n\treadonly root: boolean;\n\treadonly watchService: RepositoryWatchService;\n}\n\n/**\n * An event describing working tree file changes.\n *\n * Deliberately exposes no path collection. Consumers react by refreshing, and a refresh re-reads current\n * state — so the paths only ever gate whether to bother, never what the answer is. A partial set would\n * read as usable while quietly licensing the one harmful outcome (concluding a file did NOT change), so\n * {@link changed} is the only way to ask.\n */\nexport class RepositoryWorkingTreeChangeEvent {\n\tconstructor(\n\t\treadonly repository: Repository,\n\t\t/** Absolute paths changed in this batch; partial when {@link incomplete}. */\n\t\tprivate readonly paths: ReadonlySet<string>,\n\t\t/** Whether paths were dropped, so {@link paths} can't be used to rule anything out. */\n\t\tprivate readonly incomplete: boolean,\n\t) {}\n\n\t/**\n\t * Whether `uri` — or anything beneath it, when it's a folder — changed in this batch.\n\t *\n\t * Conservative: `true` whenever the batch is incomplete, since a miss would then prove nothing. Matches\n\t * on paths rather than URIs so nothing has to be allocated to answer.\n\t */\n\tchanged(uri: Uri): boolean {\n\t\tif (this.incomplete) return true;\n\n\t\tconst path = uri.fsPath;\n\t\treturn some(this.paths, p => p === path || isDescendant(p, path));\n\t}\n\n\ttoString(): string {\n\t\tif (this.incomplete) return `{ repository: ${this.repository.name ?? ''}, paths: incomplete }`;\n\n\t\tconst [first] = this.paths;\n\t\treturn `{ repository: ${this.repository.name ?? ''}, paths(${this.paths.size}): [${first ?? ''}${\n\t\t\tthis.paths.size > 1 ? ', ...' : ''\n\t\t}] }`;\n\t}\n}\n\nexport class Repository {\n\tprivate readonly _onDidChange = new Emitter<RepositoryChangeEvent>();\n\tget onDidChange(): Event<RepositoryChangeEvent> {\n\t\treturn this._onDidChange.event;\n\t}\n\n\tprivate readonly _onDidChangeWorkingTree = new Emitter<RepositoryWorkingTreeChangeEvent>();\n\tget onDidChangeWorkingTree(): Event<RepositoryWorkingTreeChangeEvent> {\n\t\treturn this._onDidChangeWorkingTree.event;\n\t}\n\n\treadonly commonPath: string | undefined;\n\treadonly commonRepositoryName: string | undefined;\n\treadonly commonUri: Uri | undefined;\n\treadonly id: string;\n\treadonly index: number;\n\treadonly instance = instanceCounter.next();\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly provider: GitProviderDescriptor;\n\treadonly root: boolean;\n\n\tprotected readonly _gitDir: GitDir | undefined;\n\tprotected _repoSubscription: RepositorySubscription | undefined;\n\tprotected _watchHandle: WatchHandle | undefined;\n\tprotected readonly _watchService: RepositoryWatchService;\n\n\tprivate _pendingWorkingTreePaths?: Set<string>;\n\tprivate _pendingWorkingTreeIncomplete = false;\n\tprivate _pendingWorkingTreeFlush = false;\n\tprivate _repoChangeListener: UnifiedDisposable | undefined;\n\t/** Outstanding leases on the shared {@link _watchHandle} across `watch()` + `watchWorkingTree()`. */\n\tprivate _watchHandleRefs = 0;\n\t/** Outstanding `watch()` (repo-change) leases; drives the single shared bridge subscription. */\n\tprivate _watchRefs = 0;\n\tprivate _disposed = false;\n\n\tconstructor(init: RepositoryInit) {\n\t\t({\n\t\t\tid: this.id,\n\t\t\tindex: this.index,\n\t\t\tgitDir: this._gitDir,\n\t\t\tname: this._name = init.name ?? basename(init.uri.path),\n\t\t\tpath: this.path,\n\t\t\tprovider: this.provider,\n\t\t\troot: this.root,\n\t\t\turi: this.uri,\n\t\t\twatchService: this._watchService,\n\t\t} = init);\n\n\t\t// Pre-compute common* properties from gitDir\n\t\tconst commonGitDirUri = this._gitDir?.commonUri;\n\t\tthis.commonUri = commonGitDirUri != null ? getCommonRepositoryUri(commonGitDirUri) : undefined;\n\t\tthis.commonPath = this.commonUri != null ? normalizePath(this.commonUri.path) : undefined;\n\t\tthis.commonRepositoryName = this.commonPath != null ? basename(this.commonPath) : undefined;\n\n\t\t// Apply commonRepositoryName prefix\n\t\tif (this.commonRepositoryName) {\n\t\t\tconst prefix = `${this.commonRepositoryName}: `;\n\t\t\tif (!this._name.startsWith(prefix)) {\n\t\t\t\tthis._name = `${prefix}${this._name}`;\n\t\t\t}\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._disposed = true;\n\t\tthis._repoChangeListener?.dispose();\n\t\tthis._repoChangeListener = undefined;\n\t\tthis._repoSubscription?.dispose();\n\t\tthis._repoSubscription = undefined;\n\t\tthis._watchHandle?.dispose();\n\t\tthis._watchHandle = undefined;\n\t\tthis._watchHandleRefs = 0;\n\t\tthis._watchRefs = 0;\n\t\tthis._onDidChange.dispose();\n\t\tthis._onDidChangeWorkingTree.dispose();\n\t}\n\n\t/** Indicates whether this repository currently has an active `watch()` (repo-change) lease. */\n\tget watching(): boolean {\n\t\treturn this._watchRefs > 0;\n\t}\n\n\tget etag(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget etagWorkingTree(): number | undefined {\n\t\tconst etag = this._watchHandle?.session.etagWorkingTree;\n\t\treturn etag != null && etag > 0 ? etag : undefined;\n\t}\n\n\tget hasPendingChanges(): boolean {\n\t\treturn this._watchHandle?.session.hasPendingChanges ?? false;\n\t}\n\n\t@memoize()\n\tget idHash(): string {\n\t\treturn md5(this.id);\n\t}\n\n\t/** Indicates whether this repository is a submodule */\n\tget isSubmodule(): boolean {\n\t\treturn this._gitDir?.parentUri != null;\n\t}\n\n\t/** Indicates whether this repository is a worktree */\n\tget isWorktree(): boolean {\n\t\treturn this._gitDir?.commonUri != null;\n\t}\n\n\tprotected _lastFetched: number | undefined;\n\tget lastFetchedCached(): number | undefined {\n\t\treturn this._lastFetched;\n\t}\n\n\tprotected readonly _name: string;\n\tget name(): string {\n\t\treturn this._name;\n\t}\n\n\t/** The parent repository URI (for submodules) */\n\tget parentUri(): Uri | undefined {\n\t\treturn this._gitDir?.parentUri;\n\t}\n\n\tprotected _updatedAt: number = 0;\n\tget updatedAt(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget virtual(): boolean {\n\t\treturn this.provider.virtual;\n\t}\n\n\twaitForRepoChange(timeoutMs: number): Promise<boolean> {\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\tlet listener: UnifiedDisposable | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutId != null) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\ttimeoutId = undefined;\n\t\t\t}\n\t\t\tlistener?.dispose();\n\t\t\tlistener = undefined;\n\t\t};\n\n\t\treturn Promise.race([\n\t\t\tnew Promise<false>(r => {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(false);\n\t\t\t\t}, timeoutMs);\n\t\t\t}),\n\t\t\tnew Promise<true>(r => {\n\t\t\t\tlistener = this.onDidChange(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(true);\n\t\t\t\t});\n\t\t\t}),\n\t\t]);\n\t}\n\n\t/**\n\t * Starts watching this repository's `.git` directory for repo-change events (delivered via\n\t * {@link onDidChange}) and returns a lease. Ref-counted: the underlying watch handle is acquired\n\t * on the first lease (across `watch()` and {@link watchWorkingTree}) and released when the last\n\t * is disposed. The model is inert until watched. No-ops (returns a disposable that does nothing)\n\t * for repositories without a git directory (e.g. virtual repos).\n\t */\n\t@trace({ onlyExit: true })\n\twatch(): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tif (this._watchRefs++ === 0) {\n\t\t\tconst sub = acquired.handle.session.subscribe();\n\t\t\tthis._repoSubscription = sub;\n\t\t\tthis._repoChangeListener = sub.onDidChange(e => this.onSessionRepoChange(e));\n\t\t}\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — its handle/subscriptions are gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchRefs === 0) {\n\t\t\t\t\tthis._repoChangeListener?.dispose();\n\t\t\t\t\tthis._repoChangeListener = undefined;\n\t\t\t\t\tthis._repoSubscription?.dispose();\n\t\t\t\t\tthis._repoSubscription = undefined;\n\t\t\t\t}\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t@trace({ onlyExit: true })\n\twatchWorkingTree(delay: number = 2500): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tconst sub = acquired.handle.session.subscribeToWorkingTree({ delayMs: delay });\n\t\tconst listener = sub.onDidChangeWorkingTree(e => this.onWorkingTreeChanged(e));\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\tlistener.dispose();\n\t\t\t\tsub.dispose();\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t/**\n\t * Creates a RepositoryChangeEvent for this repository.\n\t * Override in subclass to return a subclass-specific event type.\n\t */\n\tprotected createChangeEvent(changes: RepositoryChange[]): RepositoryChangeEvent {\n\t\treturn new RepositoryChangeEvent(this, changes);\n\t}\n\n\tprotected fireChange(...changes: RepositoryChange[]): void;\n\t/** Force-fire: delivered straight to the emitter, bypassing the watch session — see\n\t * {@link forcedRepositoryChanges}, which is the only set this accepts. */\n\tprotected fireChange(change: ForcedRepositoryChange, force: true): void;\n\t@trace()\n\tprotected fireChange(...args: RepositoryChange[] | [RepositoryChange, boolean]): void {\n\t\tconst lastArg = args.at(-1);\n\t\tconst force = typeof lastArg === 'boolean' ? lastArg : false;\n\t\tconst changes = (force ? args.slice(0, -1) : args) as RepositoryChange[];\n\n\t\tthis._updatedAt = Date.now();\n\n\t\tif (force) {\n\t\t\tconst e = this.createChangeEvent(changes);\n\t\t\tthis._onDidChange.fire(e);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._watchHandle?.session.fireChange(...changes);\n\t}\n\n\t/** Called when the watch service notifies of FETCH_HEAD changes. */\n\tprotected onFetchHeadChanged(): void {\n\t\t// Don't reset `_lastFetched` here: the FS watcher can fire even when the on-disk mtime\n\t\t// hasn't advanced (atomic-rename / lock-file activity emits `change` events without an\n\t\t// mtime bump), and wiping would clobber a fresher value set by `markFetched()`.\n\t\t// `getLastFetched()` reconciles in-memory vs FS via `Math.max`. Force-fire so consumers\n\t\t// invalidate their caches and re-read.\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/**\n\t * Marks the repository as having just been fetched. Use after a GitLens-initiated fetch/pull\n\t * completes so the \"last fetched\" UI tracks the attempt even when git skipped rewriting\n\t * `.git/FETCH_HEAD` (modern git omits the rewrite when all refs are up-to-date). The\n\t * in-memory timestamp is reconciled with the on-disk mtime by {@link getLastFetched} via\n\t * `Math.max`, so this is monotonic.\n\t */\n\tmarkFetched(timestamp: number = Date.now()): void {\n\t\tconst next = Math.max(this._lastFetched ?? 0, timestamp);\n\t\tif (next === this._lastFetched) return;\n\n\t\tthis._lastFetched = next;\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/** Called when .gitignore changes in the working tree. */\n\tprotected onGitIgnoreChanged(): void {\n\t\tthis.fireChange('ignores');\n\t}\n\n\t/** Called by the watch service when info/exclude changes. */\n\tprotected onIgnoresChanged(): void {}\n\n\t/** Called by the session when a debounced repo change event fires */\n\tprivate onSessionRepoChange(e: WatcherRepoChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tconst extEvent = this.createChangeEvent([...e.changes]);\n\t\tthis._onDidChange.fire(extEvent);\n\t}\n\n\t/**\n\t * Called by every {@link watchWorkingTree} subscription when its debounced batch arrives. Each\n\t * call to {@link watchWorkingTree} adds a separate bridge listener on the underlying watch\n\t * session, so a single fs change can invoke this method multiple times in the same microtask —\n\t * once per active subscription. We accumulate paths into a single pending event and defer the\n\t * actual fire to a microtask so back-to-back synchronous bridge calls collapse into ONE\n\t * `_onDidChangeWorkingTree` emission. Without this coalescing, every WIP-dependent listener\n\t * (graph, file history, compare branch, etc.) would fire N times for N active subscriptions.\n\t */\n\t/** Takes the session event whole rather than spreading it across parameters — a subclass override that\n\t * dropped a trailing positional argument silently defeated this pipeline once already. */\n\tprotected onWorkingTreeChanged(e: WorkingTreeChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tthis._pendingWorkingTreePaths ??= new Set<string>();\n\t\tfor (const p of e.paths) {\n\t\t\tthis._pendingWorkingTreePaths.add(p);\n\t\t}\n\t\tthis._pendingWorkingTreeIncomplete ||= e.incomplete;\n\n\t\tif (this._pendingWorkingTreeFlush) return;\n\n\t\tthis._pendingWorkingTreeFlush = true;\n\t\tqueueMicrotask(() => {\n\t\t\tthis._pendingWorkingTreeFlush = false;\n\t\t\tconst paths = this._pendingWorkingTreePaths;\n\t\t\tif (paths == null) return;\n\n\t\t\tconst incomplete = this._pendingWorkingTreeIncomplete;\n\t\t\tthis._pendingWorkingTreePaths = undefined;\n\t\t\tthis._pendingWorkingTreeIncomplete = false;\n\n\t\t\tthis._onDidChangeWorkingTree.fire(new RepositoryWorkingTreeChangeEvent(this, paths, incomplete));\n\t\t});\n\t}\n\n\t/**\n\t * Lazily acquires (and ref-counts) the shared {@link _watchHandle} for this repository, returning\n\t * the handle plus a one-shot lease whose disposal decrements the ref count (releasing the handle\n\t * when the last lease is gone). Returns `undefined` for repositories without a git directory.\n\t */\n\tprivate acquireWatchHandle(): { handle: WatchHandle; lease: UnifiedDisposable } | undefined {\n\t\tif (this._gitDir == null || this._disposed) return undefined;\n\n\t\tthis._watchHandle ??= this._watchService.watch(this.path, this._gitDir, {\n\t\t\tonFetchHeadChanged: () => this.onFetchHeadChanged(),\n\t\t\tonGitIgnoreChanged: () => this.onGitIgnoreChanged(),\n\t\t\tonIgnoresChanged: () => this.onIgnoresChanged(),\n\t\t} satisfies WatchHooks);\n\t\tconst handle = this._watchHandle;\n\t\tif (handle == null) return undefined;\n\n\t\tthis._watchHandleRefs++;\n\t\tconst lease = createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — the handle is gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchHandleRefs === 0) {\n\t\t\t\t\tthis._watchHandle?.dispose();\n\t\t\t\t\tthis._watchHandle = undefined;\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\treturn { handle: handle, lease: lease };\n\t}\n\n\tstatic is(repository: unknown): repository is Repository {\n\t\treturn repository instanceof Repository;\n\t}\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"repository.js","names":[],"sources":["../../../../git/src/models/repository.ts"],"sourcesContent":["import { exhaustiveArray } from '@gitlens/utils/array.js';\nimport { getScopedCounter } from '@gitlens/utils/counter.js';\nimport { md5 } from '@gitlens/utils/crypto.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { basename, isDescendant, normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { GitProviderDescriptor } from '../providers/types.js';\nimport { getCommonRepositoryUri } from '../utils/repository.utils.js';\nimport type { WatcherRepoChangeEvent, WorkingTreeChangeEvent } from '../watching/changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle, WatchHooks } from '../watching/watchService.js';\nimport type { RepositorySubscription } from '../watching/watchSession.js';\nimport { RepositoryChangeEvent } from './repositoryChangeEvent.js';\n\nconst instanceCounter = getScopedCounter();\n\nexport interface GitDir {\n\treadonly uri: Uri;\n\t/** The common git directory for worktrees */\n\treadonly commonUri?: Uri;\n\t/** The parent (superproject) directory for submodules */\n\treadonly parentUri?: Uri;\n}\n\nexport type RepositoryChange =\n\t| 'unknown'\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| 'pausedOp'\n\t| 'cherryPick'\n\t| 'merge'\n\t| 'rebase'\n\t| 'revert'\n\t| 'closed'\n\t| 'ignores'\n\t| 'remoteProviders'\n\t| 'starred'\n\t| 'opened'\n\t| 'gkConfig'\n\t| 'lastFetched';\n\nexport const repositoryChanges = exhaustiveArray<RepositoryChange>()([\n\t'unknown',\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'pausedOp',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'closed',\n\t'ignores',\n\t'remoteProviders',\n\t'starred',\n\t'opened',\n\t'gkConfig',\n\t'lastFetched',\n]);\n\n/**\n * The only changes {@link Repository.fireChange} accepts with `force` — delivered straight to the repository's\n * emitter, bypassing the watch session, so they still arrive while it's suspended or when there's no watch handle\n * at all (a repo being opened, or closed, or a virtual repo that never had one).\n *\n * These have nothing intrinsic in common — being forced IS the shared property, which is why this is keyed off\n * `force` rather than some category of change.\n *\n * It's the partition consumers need: everything NOT listed here goes through the session, which does its\n * pre-notification work (e.g. advancing a cache's status clock) exactly once, before it notifies anyone. Nothing\n * runs upstream of a forced change, so a consumer bridging repository changes into a cache must handle these\n * itself. Forcing a change that isn't listed is a compile error — and consumers gate on this same array, so\n * listing one wires it up for free.\n */\nexport const forcedRepositoryChanges = [\n\t'opened',\n\t'closed',\n\t'lastFetched',\n] as const satisfies readonly RepositoryChange[];\nexport type ForcedRepositoryChange = (typeof forcedRepositoryChanges)[number];\n\nexport interface RepositoryInit {\n\treadonly id: string;\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly name: string;\n\treadonly provider: GitProviderDescriptor;\n\treadonly gitDir: GitDir | undefined;\n\treadonly index: number;\n\treadonly root: boolean;\n\treadonly watchService: RepositoryWatchService;\n}\n\n/**\n * An event describing working tree file changes.\n *\n * Deliberately exposes no path collection. Consumers react by refreshing, and a refresh re-reads current\n * state — so the paths only ever gate whether to bother, never what the answer is. A partial set would\n * read as usable while quietly licensing the one harmful outcome (concluding a file did NOT change), so\n * {@link changed} is the only way to ask.\n */\nexport class RepositoryWorkingTreeChangeEvent {\n\tconstructor(\n\t\treadonly repository: Repository,\n\t\t/** Absolute paths changed in this batch; partial when {@link incomplete}. */\n\t\tprivate readonly paths: ReadonlySet<string>,\n\t\t/** Whether paths were dropped, so {@link paths} can't be used to rule anything out. */\n\t\tprivate readonly incomplete: boolean,\n\t) {}\n\n\t/**\n\t * Whether `uri` — or anything beneath it, when it's a folder, or a folder containing it — changed in this batch.\n\t * A changed folder stands for everything beneath it (a watcher can miss the files of a just-created folder).\n\t *\n\t * Conservative: `true` whenever the batch is incomplete, since a miss would then prove nothing. Matches\n\t * on paths rather than URIs so nothing has to be allocated to answer.\n\t */\n\tchanged(uri: Uri): boolean {\n\t\tif (this.incomplete) return true;\n\n\t\tconst path = uri.fsPath;\n\t\treturn some(this.paths, p => p === path || isDescendant(p, path) || isDescendant(path, p));\n\t}\n\n\ttoString(): string {\n\t\tif (this.incomplete) return `{ repository: ${this.repository.name ?? ''}, paths: incomplete }`;\n\n\t\tconst [first] = this.paths;\n\t\treturn `{ repository: ${this.repository.name ?? ''}, paths(${this.paths.size}): [${first ?? ''}${\n\t\t\tthis.paths.size > 1 ? ', ...' : ''\n\t\t}] }`;\n\t}\n}\n\nexport class Repository {\n\tprivate readonly _onDidChange = new Emitter<RepositoryChangeEvent>();\n\tget onDidChange(): Event<RepositoryChangeEvent> {\n\t\treturn this._onDidChange.event;\n\t}\n\n\tprivate readonly _onDidChangeWorkingTree = new Emitter<RepositoryWorkingTreeChangeEvent>();\n\tget onDidChangeWorkingTree(): Event<RepositoryWorkingTreeChangeEvent> {\n\t\treturn this._onDidChangeWorkingTree.event;\n\t}\n\n\treadonly commonPath: string | undefined;\n\treadonly commonRepositoryName: string | undefined;\n\treadonly commonUri: Uri | undefined;\n\treadonly id: string;\n\treadonly index: number;\n\treadonly instance = instanceCounter.next();\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly provider: GitProviderDescriptor;\n\treadonly root: boolean;\n\n\tprotected readonly _gitDir: GitDir | undefined;\n\tprotected _repoSubscription: RepositorySubscription | undefined;\n\tprotected _watchHandle: WatchHandle | undefined;\n\tprotected readonly _watchService: RepositoryWatchService;\n\n\tprivate _pendingWorkingTreePaths?: Set<string>;\n\tprivate _pendingWorkingTreeIncomplete = false;\n\tprivate _pendingWorkingTreeFlush = false;\n\tprivate _repoChangeListener: UnifiedDisposable | undefined;\n\t/** Outstanding leases on the shared {@link _watchHandle} across `watch()` + `watchWorkingTree()`. */\n\tprivate _watchHandleRefs = 0;\n\t/** Outstanding `watch()` (repo-change) leases; drives the single shared bridge subscription. */\n\tprivate _watchRefs = 0;\n\tprivate _disposed = false;\n\n\tconstructor(init: RepositoryInit) {\n\t\t({\n\t\t\tid: this.id,\n\t\t\tindex: this.index,\n\t\t\tgitDir: this._gitDir,\n\t\t\tname: this._name = init.name ?? basename(init.uri.path),\n\t\t\tpath: this.path,\n\t\t\tprovider: this.provider,\n\t\t\troot: this.root,\n\t\t\turi: this.uri,\n\t\t\twatchService: this._watchService,\n\t\t} = init);\n\n\t\t// Pre-compute common* properties from gitDir\n\t\tconst commonGitDirUri = this._gitDir?.commonUri;\n\t\tthis.commonUri = commonGitDirUri != null ? getCommonRepositoryUri(commonGitDirUri) : undefined;\n\t\tthis.commonPath = this.commonUri != null ? normalizePath(this.commonUri.path) : undefined;\n\t\tthis.commonRepositoryName = this.commonPath != null ? basename(this.commonPath) : undefined;\n\n\t\t// Apply commonRepositoryName prefix\n\t\tif (this.commonRepositoryName) {\n\t\t\tconst prefix = `${this.commonRepositoryName}: `;\n\t\t\tif (!this._name.startsWith(prefix)) {\n\t\t\t\tthis._name = `${prefix}${this._name}`;\n\t\t\t}\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._disposed = true;\n\t\tthis._repoChangeListener?.dispose();\n\t\tthis._repoChangeListener = undefined;\n\t\tthis._repoSubscription?.dispose();\n\t\tthis._repoSubscription = undefined;\n\t\tthis._watchHandle?.dispose();\n\t\tthis._watchHandle = undefined;\n\t\tthis._watchHandleRefs = 0;\n\t\tthis._watchRefs = 0;\n\t\tthis._onDidChange.dispose();\n\t\tthis._onDidChangeWorkingTree.dispose();\n\t}\n\n\t/** Indicates whether this repository currently has an active `watch()` (repo-change) lease. */\n\tget watching(): boolean {\n\t\treturn this._watchRefs > 0;\n\t}\n\n\tget etag(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget etagWorkingTree(): number | undefined {\n\t\tconst etag = this._watchHandle?.session.etagWorkingTree;\n\t\treturn etag != null && etag > 0 ? etag : undefined;\n\t}\n\n\tget hasPendingChanges(): boolean {\n\t\treturn this._watchHandle?.session.hasPendingChanges ?? false;\n\t}\n\n\t@memoize()\n\tget idHash(): string {\n\t\treturn md5(this.id);\n\t}\n\n\t/** Indicates whether this repository is a submodule */\n\tget isSubmodule(): boolean {\n\t\treturn this._gitDir?.parentUri != null;\n\t}\n\n\t/** Indicates whether this repository is a worktree */\n\tget isWorktree(): boolean {\n\t\treturn this._gitDir?.commonUri != null;\n\t}\n\n\tprotected _lastFetched: number | undefined;\n\tget lastFetchedCached(): number | undefined {\n\t\treturn this._lastFetched;\n\t}\n\n\tprotected readonly _name: string;\n\tget name(): string {\n\t\treturn this._name;\n\t}\n\n\t/** The parent repository URI (for submodules) */\n\tget parentUri(): Uri | undefined {\n\t\treturn this._gitDir?.parentUri;\n\t}\n\n\tprotected _updatedAt: number = 0;\n\tget updatedAt(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget virtual(): boolean {\n\t\treturn this.provider.virtual;\n\t}\n\n\twaitForRepoChange(timeoutMs: number): Promise<boolean> {\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\tlet listener: UnifiedDisposable | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutId != null) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\ttimeoutId = undefined;\n\t\t\t}\n\t\t\tlistener?.dispose();\n\t\t\tlistener = undefined;\n\t\t};\n\n\t\treturn Promise.race([\n\t\t\tnew Promise<false>(r => {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(false);\n\t\t\t\t}, timeoutMs);\n\t\t\t}),\n\t\t\tnew Promise<true>(r => {\n\t\t\t\tlistener = this.onDidChange(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(true);\n\t\t\t\t});\n\t\t\t}),\n\t\t]);\n\t}\n\n\t/**\n\t * Starts watching this repository's `.git` directory for repo-change events (delivered via\n\t * {@link onDidChange}) and returns a lease. Ref-counted: the underlying watch handle is acquired\n\t * on the first lease (across `watch()` and {@link watchWorkingTree}) and released when the last\n\t * is disposed. The model is inert until watched. No-ops (returns a disposable that does nothing)\n\t * for repositories without a git directory (e.g. virtual repos).\n\t */\n\t@trace({ onlyExit: true })\n\twatch(): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tif (this._watchRefs++ === 0) {\n\t\t\tconst sub = acquired.handle.session.subscribe();\n\t\t\tthis._repoSubscription = sub;\n\t\t\tthis._repoChangeListener = sub.onDidChange(e => this.onSessionRepoChange(e));\n\t\t}\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — its handle/subscriptions are gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchRefs === 0) {\n\t\t\t\t\tthis._repoChangeListener?.dispose();\n\t\t\t\t\tthis._repoChangeListener = undefined;\n\t\t\t\t\tthis._repoSubscription?.dispose();\n\t\t\t\t\tthis._repoSubscription = undefined;\n\t\t\t\t}\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t@trace({ onlyExit: true })\n\twatchWorkingTree(delay: number = 2500): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tconst sub = acquired.handle.session.subscribeToWorkingTree({ delayMs: delay });\n\t\tconst listener = sub.onDidChangeWorkingTree(e => this.onWorkingTreeChanged(e));\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\tlistener.dispose();\n\t\t\t\tsub.dispose();\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t/**\n\t * Creates a RepositoryChangeEvent for this repository.\n\t * Override in subclass to return a subclass-specific event type.\n\t */\n\tprotected createChangeEvent(changes: RepositoryChange[]): RepositoryChangeEvent {\n\t\treturn new RepositoryChangeEvent(this, changes);\n\t}\n\n\tprotected fireChange(...changes: RepositoryChange[]): void;\n\t/** Force-fire: delivered straight to the emitter, bypassing the watch session — see\n\t * {@link forcedRepositoryChanges}, which is the only set this accepts. */\n\tprotected fireChange(change: ForcedRepositoryChange, force: true): void;\n\t@trace()\n\tprotected fireChange(...args: RepositoryChange[] | [RepositoryChange, boolean]): void {\n\t\tconst lastArg = args.at(-1);\n\t\tconst force = typeof lastArg === 'boolean' ? lastArg : false;\n\t\tconst changes = (force ? args.slice(0, -1) : args) as RepositoryChange[];\n\n\t\tthis._updatedAt = Date.now();\n\n\t\tif (force) {\n\t\t\tconst e = this.createChangeEvent(changes);\n\t\t\tthis._onDidChange.fire(e);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._watchHandle?.session.fireChange(...changes);\n\t}\n\n\t/** Called when the watch service notifies of FETCH_HEAD changes. */\n\tprotected onFetchHeadChanged(): void {\n\t\t// Don't reset `_lastFetched` here: the FS watcher can fire even when the on-disk mtime\n\t\t// hasn't advanced (atomic-rename / lock-file activity emits `change` events without an\n\t\t// mtime bump), and wiping would clobber a fresher value set by `markFetched()`.\n\t\t// `getLastFetched()` reconciles in-memory vs FS via `Math.max`. Force-fire so consumers\n\t\t// invalidate their caches and re-read.\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/**\n\t * Marks the repository as having just been fetched. Use after a GitLens-initiated fetch/pull\n\t * completes so the \"last fetched\" UI tracks the attempt even when git skipped rewriting\n\t * `.git/FETCH_HEAD` (modern git omits the rewrite when all refs are up-to-date). The\n\t * in-memory timestamp is reconciled with the on-disk mtime by {@link getLastFetched} via\n\t * `Math.max`, so this is monotonic.\n\t */\n\tmarkFetched(timestamp: number = Date.now()): void {\n\t\tconst next = Math.max(this._lastFetched ?? 0, timestamp);\n\t\tif (next === this._lastFetched) return;\n\n\t\tthis._lastFetched = next;\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/** Called when .gitignore changes in the working tree. */\n\tprotected onGitIgnoreChanged(): void {\n\t\tthis.fireChange('ignores');\n\t}\n\n\t/** Called by the watch service when info/exclude changes. */\n\tprotected onIgnoresChanged(): void {}\n\n\t/** Called by the session when a debounced repo change event fires */\n\tprivate onSessionRepoChange(e: WatcherRepoChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tconst extEvent = this.createChangeEvent([...e.changes]);\n\t\tthis._onDidChange.fire(extEvent);\n\t}\n\n\t/**\n\t * Called by every {@link watchWorkingTree} subscription when its debounced batch arrives. Each\n\t * call to {@link watchWorkingTree} adds a separate bridge listener on the underlying watch\n\t * session, so a single fs change can invoke this method multiple times in the same microtask —\n\t * once per active subscription. We accumulate paths into a single pending event and defer the\n\t * actual fire to a microtask so back-to-back synchronous bridge calls collapse into ONE\n\t * `_onDidChangeWorkingTree` emission. Without this coalescing, every WIP-dependent listener\n\t * (graph, file history, compare branch, etc.) would fire N times for N active subscriptions.\n\t */\n\t/** Takes the session event whole rather than spreading it across parameters — a subclass override that\n\t * dropped a trailing positional argument silently defeated this pipeline once already. */\n\tprotected onWorkingTreeChanged(e: WorkingTreeChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tthis._pendingWorkingTreePaths ??= new Set<string>();\n\t\tfor (const p of e.paths) {\n\t\t\tthis._pendingWorkingTreePaths.add(p);\n\t\t}\n\t\tthis._pendingWorkingTreeIncomplete ||= e.incomplete;\n\n\t\tif (this._pendingWorkingTreeFlush) return;\n\n\t\tthis._pendingWorkingTreeFlush = true;\n\t\tqueueMicrotask(() => {\n\t\t\tthis._pendingWorkingTreeFlush = false;\n\t\t\tconst paths = this._pendingWorkingTreePaths;\n\t\t\tif (paths == null) return;\n\n\t\t\tconst incomplete = this._pendingWorkingTreeIncomplete;\n\t\t\tthis._pendingWorkingTreePaths = undefined;\n\t\t\tthis._pendingWorkingTreeIncomplete = false;\n\n\t\t\tthis._onDidChangeWorkingTree.fire(new RepositoryWorkingTreeChangeEvent(this, paths, incomplete));\n\t\t});\n\t}\n\n\t/**\n\t * Lazily acquires (and ref-counts) the shared {@link _watchHandle} for this repository, returning\n\t * the handle plus a one-shot lease whose disposal decrements the ref count (releasing the handle\n\t * when the last lease is gone). Returns `undefined` for repositories without a git directory.\n\t */\n\tprivate acquireWatchHandle(): { handle: WatchHandle; lease: UnifiedDisposable } | undefined {\n\t\tif (this._gitDir == null || this._disposed) return undefined;\n\n\t\tthis._watchHandle ??= this._watchService.watch(this.path, this._gitDir, {\n\t\t\tonFetchHeadChanged: () => this.onFetchHeadChanged(),\n\t\t\tonGitIgnoreChanged: () => this.onGitIgnoreChanged(),\n\t\t\tonIgnoresChanged: () => this.onIgnoresChanged(),\n\t\t} satisfies WatchHooks);\n\t\tconst handle = this._watchHandle;\n\t\tif (handle == null) return undefined;\n\n\t\tthis._watchHandleRefs++;\n\t\tconst lease = createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — the handle is gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchHandleRefs === 0) {\n\t\t\t\t\tthis._watchHandle?.dispose();\n\t\t\t\t\tthis._watchHandle = undefined;\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\treturn { handle: handle, lease: lease };\n\t}\n\n\tstatic is(repository: unknown): repository is Repository {\n\t\treturn repository instanceof Repository;\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -21,6 +21,19 @@ export interface BranchContributionsOverview extends GitCommitStats<number> {
|
|
|
21
21
|
readonly firstCommitDate: Date | undefined;
|
|
22
22
|
readonly contributors: GitContributor[];
|
|
23
23
|
}
|
|
24
|
+
export interface AheadOfBase {
|
|
25
|
+
/** The base HEAD was measured against; `undefined` when there is none (see `getAheadOfBase`) */
|
|
26
|
+
readonly base: {
|
|
27
|
+
/** The ref the count was taken against: the chosen base or its tracking ref, whichever forked nearer HEAD */
|
|
28
|
+
readonly ref: string;
|
|
29
|
+
/** Commits on HEAD not on `ref` */
|
|
30
|
+
readonly ahead: number;
|
|
31
|
+
/** The commit `ref` pointed at, from the cached ref list; `undefined` when no listed ref matches it */
|
|
32
|
+
readonly sha: string | undefined;
|
|
33
|
+
} | undefined;
|
|
34
|
+
/** Full ref names (`refs/heads/main`, `refs/remotes/origin/main`, `refs/remotes/origin/HEAD`) whose movement can change this answer, given with or without a base so a watcher also learns when one appears: the branch, every candidate compared up to the chosen one, their tracking refs, and the default-branch symref when the default was consulted. Lets a watcher recompute only when one of them moves. */
|
|
35
|
+
readonly dependsOn: readonly string[];
|
|
36
|
+
}
|
|
24
37
|
export type MergeDetectionConfidence = 'highest' | 'high' | 'medium';
|
|
25
38
|
export type GitBranchMergedStatus = {
|
|
26
39
|
merged: false;
|
|
@@ -85,6 +98,17 @@ export interface GitBranchesSubProvider {
|
|
|
85
98
|
getPotentialMergeConflicts?(repoPath: string, branch: string, targetBranch: string, cancellation?: AbortSignal): Promise<ConflictDetectionResult>;
|
|
86
99
|
/** Detects conflicts when an autostash is reapplied onto the tree an integration produced */
|
|
87
100
|
getPotentialStashReapplyConflicts?(repoPath: string, ontoTreeOid: string, cancellation?: AbortSignal): Promise<ConflictDetectionResult>;
|
|
101
|
+
/**
|
|
102
|
+
* How far HEAD has moved past the base `branch` (the branch checked out at HEAD) was cut from. Candidates in trust
|
|
103
|
+
* order: the user's merge target, the detected one, then the base recorded against the branch — only recorded bases
|
|
104
|
+
* count, never a reflog guess. Each is measured as itself or its tracking ref, whichever forked nearer HEAD; a stale
|
|
105
|
+
* target (one naming no ref, a flag-like one, or the branch itself) falls through to the next, and the local default
|
|
106
|
+
* branch is used when none survives. The count is the right side of `<base>...HEAD`. `base` is `undefined` when no
|
|
107
|
+
* base resolves, when the base is the branch itself (by name, or as its copy on one of the repo's remotes) at HEAD's
|
|
108
|
+
* own commit, or when HEAD is detached or has no commit yet; `dependsOn` is given either way. `base.sha` is the tip of
|
|
109
|
+
* `ref`, read from the cached ref list. Rejects when the count can't be read.
|
|
110
|
+
*/
|
|
111
|
+
getAheadOfBase?(repoPath: string, branch: string, cancellation?: AbortSignal): Promise<AheadOfBase>;
|
|
88
112
|
getBaseBranchName?(repoPath: string, ref: string, options?: {
|
|
89
113
|
priority?: GitCommandPriority;
|
|
90
114
|
}, cancellation?: AbortSignal): Promise<string | undefined>;
|
|
@@ -5,7 +5,9 @@ import { GkConfigInvalidationTarget } from "../cache.js";
|
|
|
5
5
|
//#region ../git/src/providers/config.d.ts
|
|
6
6
|
export type GitCoreConfigKeys = 'commit.gpgsign' | 'core.excludesFile' |
|
|
7
7
|
/** `core.hooksPath` — the directory git runs hooks from instead of `$GIT_DIR/hooks` */
|
|
8
|
-
'core.hooksPath' |
|
|
8
|
+
'core.hooksPath' |
|
|
9
|
+
/** `core.ignorecase` — whether git matches paths (including ignore patterns) case-insensitively */
|
|
10
|
+
'core.ignorecase' | 'diff.guitool' | 'diff.tool' |
|
|
9
11
|
/** `fetch.prune` — whether `git fetch` removes remote-tracking branches that no longer exist on the remote */
|
|
10
12
|
'fetch.prune' | 'gpg.format' | 'gpg.program' | 'gpg.ssh.program' | 'gpg.ssh.allowedSignersFile' | 'init.defaultBranch' |
|
|
11
13
|
/** `merge.autoStash` — whether `git merge` (and so a merging `git pull`) stashes and reapplies uncommitted changes */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Event } from "../../utils/event.js";
|
|
1
2
|
import { Uri } from "../../utils/uri.js";
|
|
2
3
|
import { GitProviderDescriptor } from "./types.js";
|
|
3
4
|
import { GitIgnoreFilter } from "../watching/gitIgnoreFilter.js";
|
|
@@ -7,7 +8,7 @@ import { GitCommitsSubProvider } from "./commits.js";
|
|
|
7
8
|
import { GitBranchesSubProvider } from "./branches.js";
|
|
8
9
|
import { GitContributorsSubProvider } from "./contributors.js";
|
|
9
10
|
import { GitRevisionSubProvider } from "./revision.js";
|
|
10
|
-
import { CachedGitTypes } from "../cache.js";
|
|
11
|
+
import { CachedGitTypes, MergeTargetsChangeEvent } from "../cache.js";
|
|
11
12
|
import { GitOperationRunOptions, GitOperationsSubProvider } from "./operations.js";
|
|
12
13
|
import { GitBlameSubProvider } from "./blame.js";
|
|
13
14
|
import { GitConfigSubProvider } from "./config.js";
|
|
@@ -41,8 +42,19 @@ export interface GitProvider {
|
|
|
41
42
|
runOptions?: GitOperationRunOptions): Promise<string | undefined>;
|
|
42
43
|
excludeIgnoredUris?(repoPath: string, uris: Uri[]): Promise<Uri[]>;
|
|
43
44
|
getIgnoreFilter?(repoPath: string, gitDirPath: string): GitIgnoreFilter;
|
|
45
|
+
/**
|
|
46
|
+
* The directories in the working tree that an ignore rule matches, as repo-relative `/`-separated paths — each
|
|
47
|
+
* to be pruned from a file watcher along with its subtree. Nested entries are collapsed into their outermost
|
|
48
|
+
* ancestor, and literal paths containing glob metacharacters are dropped (a watcher can't express them safely).
|
|
49
|
+
*/
|
|
50
|
+
getIgnoredDirectories?(repoPath: string, cancellation?: AbortSignal): Promise<string[]>;
|
|
44
51
|
getIgnoredUrisFilter?(repoPath: string): Promise<(uri: Uri) => boolean>;
|
|
45
52
|
getLastFetchedTimestamp?(repoPath: string): Promise<number | undefined>;
|
|
53
|
+
/**
|
|
54
|
+
* Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys — not for activity
|
|
55
|
+
* timestamps, and not for the provider's own writes. See `Cache.onDidChangeMergeTargets`.
|
|
56
|
+
*/
|
|
57
|
+
readonly onDidChangeMergeTargets?: Event<MergeTargetsChangeEvent>;
|
|
46
58
|
/** Creates a new repository at `path` (`git init`), creating `path` itself if it doesn't yet exist. */
|
|
47
59
|
init?(path: string, options?: {
|
|
48
60
|
defaultBranch?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Uri } from "../../utils/uri.js";
|
|
2
1
|
import { UnifiedAsyncDisposable } from "../../utils/disposable.js";
|
|
2
|
+
import { Uri } from "../../utils/uri.js";
|
|
3
3
|
import { GitOperationRunOptions } from "./operations.js";
|
|
4
4
|
//#region ../git/src/providers/staging.d.ts
|
|
5
5
|
export interface DisposableTemporaryGitIndex extends UnifiedAsyncDisposable {
|
package/dist/git/service.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { Uri } from "../utils/uri.js";
|
|
2
1
|
import { UnifiedDisposable } from "../utils/disposable.js";
|
|
3
2
|
import { Event } from "../utils/event.js";
|
|
3
|
+
import { Uri } from "../utils/uri.js";
|
|
4
4
|
import { FileWatchingProvider } from "./watching/provider.js";
|
|
5
5
|
import { RepositoryWatchService } from "./watching/watchService.js";
|
|
6
|
+
import { MergeTargetsChangeEvent } from "./cache.js";
|
|
6
7
|
import { GitProvider } from "./providers/provider.js";
|
|
7
8
|
import { RepositoryService } from "./repositoryService.js";
|
|
9
|
+
import { WorktreeSummaryService } from "./watching/worktreeSummary.js";
|
|
8
10
|
//#region ../git/src/service.d.ts
|
|
9
11
|
export interface ProvidersChangeEvent {
|
|
10
12
|
readonly added: readonly GitProvider[];
|
|
@@ -61,10 +63,20 @@ export declare class GitService implements UnifiedDisposable {
|
|
|
61
63
|
static createSingleton(watchingProvider?: FileWatchingProvider): GitService;
|
|
62
64
|
private readonly _onDidChangeProviders;
|
|
63
65
|
get onDidChangeProviders(): Event<ProvidersChangeEvent>;
|
|
66
|
+
private readonly _onDidChangeMergeTargets;
|
|
67
|
+
/**
|
|
68
|
+
* Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys, for any registered
|
|
69
|
+
* provider that reports it — never for activity timestamps or core's own writes. Once per change per repository
|
|
70
|
+
* (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.
|
|
71
|
+
*/
|
|
72
|
+
get onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent>;
|
|
73
|
+
/** Keyed by the provider's event, so providers sharing one cache are forwarded once */
|
|
74
|
+
private readonly _mergeTargetsSources;
|
|
64
75
|
private _etag;
|
|
65
76
|
private readonly _providers;
|
|
66
77
|
private readonly _serviceCache;
|
|
67
78
|
private readonly _watchService;
|
|
79
|
+
private readonly _summaries;
|
|
68
80
|
private constructor();
|
|
69
81
|
[Symbol.dispose](): void;
|
|
70
82
|
dispose(): void;
|
|
@@ -73,12 +85,16 @@ export declare class GitService implements UnifiedDisposable {
|
|
|
73
85
|
/** Whether any providers are registered. */
|
|
74
86
|
get hasProviders(): boolean;
|
|
75
87
|
get watchService(): RepositoryWatchService | undefined;
|
|
88
|
+
/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */
|
|
89
|
+
get summaries(): WorktreeSummaryService | undefined;
|
|
76
90
|
/**
|
|
77
91
|
* Registers a provider with a routing predicate.
|
|
78
92
|
*
|
|
79
93
|
* @returns A disposable that unregisters the provider.
|
|
80
94
|
*/
|
|
81
95
|
register(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable;
|
|
96
|
+
private addMergeTargetsSource;
|
|
97
|
+
private removeMergeTargetsSource;
|
|
82
98
|
/** Returns the provider that can handle the given path, or undefined if none matches. */
|
|
83
99
|
getProvider(repoPath: string): {
|
|
84
100
|
provider: GitProvider;
|
package/dist/git/service.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { createDisposable } from "../utils/disposable.js";
|
|
2
|
+
import { Emitter } from "../utils/event.js";
|
|
1
3
|
import { getRepositoryKey } from "../utils/uri.js";
|
|
2
4
|
import { normalizePath } from "../utils/path.js";
|
|
3
5
|
import { clearGlobalRepositoryServiceResolver, createSubProviderProxyForRepo, setGlobalRepositoryServiceResolver } from "./repositoryService.js";
|
|
4
|
-
import { createDisposable } from "../utils/disposable.js";
|
|
5
|
-
import { Emitter } from "../utils/event.js";
|
|
6
6
|
import { RepositoryWatchService } from "./watching/watchService.js";
|
|
7
|
+
import { WorktreeSummaryService } from "./watching/worktreeSummary.js";
|
|
7
8
|
//#region ../git/src/service.ts
|
|
8
9
|
/**
|
|
9
10
|
* Entry point — provider router, repo-scoped proxy cache, and global configuration.
|
|
@@ -42,17 +43,36 @@ var GitService = class GitService {
|
|
|
42
43
|
get onDidChangeProviders() {
|
|
43
44
|
return this._onDidChangeProviders.event;
|
|
44
45
|
}
|
|
46
|
+
_onDidChangeMergeTargets = new Emitter();
|
|
47
|
+
/**
|
|
48
|
+
* Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys, for any registered
|
|
49
|
+
* provider that reports it — never for activity timestamps or core's own writes. Once per change per repository
|
|
50
|
+
* (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.
|
|
51
|
+
*/
|
|
52
|
+
get onDidChangeMergeTargets() {
|
|
53
|
+
return this._onDidChangeMergeTargets.event;
|
|
54
|
+
}
|
|
55
|
+
/** Keyed by the provider's event, so providers sharing one cache are forwarded once */
|
|
56
|
+
_mergeTargetsSources = /* @__PURE__ */ new Map();
|
|
45
57
|
_etag = 0;
|
|
46
58
|
_providers = [];
|
|
47
59
|
_serviceCache = /* @__PURE__ */ new Map();
|
|
48
60
|
_watchService;
|
|
61
|
+
_summaries;
|
|
49
62
|
constructor(watchingProvider) {
|
|
50
|
-
if (watchingProvider != null)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
63
|
+
if (watchingProvider != null) {
|
|
64
|
+
this._watchService = new RepositoryWatchService({
|
|
65
|
+
watchingProvider,
|
|
66
|
+
getIgnoreFilter: (repoPath, gitDirPath) => {
|
|
67
|
+
return this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);
|
|
68
|
+
},
|
|
69
|
+
getIgnoredDirectories: (repoPath) => this.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([])
|
|
70
|
+
});
|
|
71
|
+
this._summaries = new WorktreeSummaryService({
|
|
72
|
+
watchService: this._watchService,
|
|
73
|
+
getRepository: (repoPath) => this.forRepo(repoPath)
|
|
74
|
+
});
|
|
75
|
+
}
|
|
56
76
|
setGlobalRepositoryServiceResolver((repoPath) => this.forRepo(repoPath));
|
|
57
77
|
}
|
|
58
78
|
[Symbol.dispose]() {
|
|
@@ -62,9 +82,13 @@ var GitService = class GitService {
|
|
|
62
82
|
clearGlobalRepositoryServiceResolver();
|
|
63
83
|
GitService._instance = void 0;
|
|
64
84
|
this._serviceCache.clear();
|
|
85
|
+
this._summaries?.dispose();
|
|
65
86
|
this._watchService?.dispose();
|
|
66
87
|
this._providers.length = 0;
|
|
88
|
+
for (const source of this._mergeTargetsSources.values()) source.subscription.dispose();
|
|
89
|
+
this._mergeTargetsSources.clear();
|
|
67
90
|
this._onDidChangeProviders.dispose();
|
|
91
|
+
this._onDidChangeMergeTargets.dispose();
|
|
68
92
|
}
|
|
69
93
|
/** Monotonically increasing counter incremented on structural changes. */
|
|
70
94
|
get etag() {
|
|
@@ -77,6 +101,10 @@ var GitService = class GitService {
|
|
|
77
101
|
get watchService() {
|
|
78
102
|
return this._watchService;
|
|
79
103
|
}
|
|
104
|
+
/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */
|
|
105
|
+
get summaries() {
|
|
106
|
+
return this._summaries;
|
|
107
|
+
}
|
|
80
108
|
/**
|
|
81
109
|
* Registers a provider with a routing predicate.
|
|
82
110
|
*
|
|
@@ -89,6 +117,7 @@ var GitService = class GitService {
|
|
|
89
117
|
};
|
|
90
118
|
this._providers.push(entry);
|
|
91
119
|
this._etag++;
|
|
120
|
+
this.addMergeTargetsSource(provider);
|
|
92
121
|
this._onDidChangeProviders.fire({
|
|
93
122
|
added: [provider],
|
|
94
123
|
removed: [],
|
|
@@ -100,6 +129,7 @@ var GitService = class GitService {
|
|
|
100
129
|
if (idx !== -1) {
|
|
101
130
|
this._providers.splice(idx, 1);
|
|
102
131
|
this._etag++;
|
|
132
|
+
this.removeMergeTargetsSource(provider);
|
|
103
133
|
this._serviceCache.clear();
|
|
104
134
|
this._onDidChangeProviders.fire({
|
|
105
135
|
added: [],
|
|
@@ -109,6 +139,27 @@ var GitService = class GitService {
|
|
|
109
139
|
}
|
|
110
140
|
});
|
|
111
141
|
}
|
|
142
|
+
addMergeTargetsSource(provider) {
|
|
143
|
+
const event = provider.onDidChangeMergeTargets;
|
|
144
|
+
if (event == null) return;
|
|
145
|
+
const source = this._mergeTargetsSources.get(event);
|
|
146
|
+
if (source != null) {
|
|
147
|
+
source.refCount++;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
this._mergeTargetsSources.set(event, {
|
|
151
|
+
subscription: event((e) => this._onDidChangeMergeTargets.fire(e)),
|
|
152
|
+
refCount: 1
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
removeMergeTargetsSource(provider) {
|
|
156
|
+
const event = provider.onDidChangeMergeTargets;
|
|
157
|
+
if (event == null) return;
|
|
158
|
+
const source = this._mergeTargetsSources.get(event);
|
|
159
|
+
if (source == null || --source.refCount > 0) return;
|
|
160
|
+
source.subscription.dispose();
|
|
161
|
+
this._mergeTargetsSources.delete(event);
|
|
162
|
+
}
|
|
112
163
|
/** Returns the provider that can handle the given path, or undefined if none matches. */
|
|
113
164
|
getProvider(repoPath) {
|
|
114
165
|
repoPath = normalizePath(repoPath);
|
package/dist/git/service.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","names":[],"sources":["../../../git/src/service.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { GitProvider } from './providers/provider.js';\nimport type { RepositoryService, SubProviderForRepo } from './repositoryService.js';\nimport {\n\tclearGlobalRepositoryServiceResolver,\n\tcreateSubProviderProxyForRepo,\n\tsetGlobalRepositoryServiceResolver,\n} from './repositoryService.js';\nimport type { FileWatchingProvider } from './watching/provider.js';\nimport { RepositoryWatchService } from './watching/watchService.js';\n\nexport interface ProvidersChangeEvent {\n\treadonly added: readonly GitProvider[];\n\treadonly removed: readonly GitProvider[];\n\treadonly etag: number;\n}\n\n/**\n * Result of {@link GitService.validateRepo}.\n *\n * - `valid: true, safe: true` — a usable git repository at `repoPath`; `gitDir` / `commonGitDir` / `superprojectPath` are populated when available\n * - `valid: true, safe: false` — git detected unsafe (dubious) ownership at the given path; no `repoPath` is surfaced (the raw call doesn't carry one back)\n * - `valid: false` — not a git repository, no registered provider can handle the path, or the routed provider doesn't implement `getRepositoryInfo`\n */\nexport type ValidateRepoResult =\n\t| {\n\t\t\tvalid: true;\n\t\t\tsafe: true;\n\t\t\trepoPath: string;\n\t\t\tgitDir?: string;\n\t\t\tcommonGitDir?: string;\n\t\t\tsuperprojectPath?: string;\n\t }\n\t| { valid: true; safe: false }\n\t| { valid: false };\n\ninterface RegisteredProvider {\n\tprovider: GitProvider;\n\tcanHandle: (repoPath: string) => boolean;\n}\n\n/**\n * Entry point — provider router, repo-scoped proxy cache, and global configuration.\n *\n * Creating an instance wires up the module-level repository\n * resolver. Calling {@link dispose} tears it down.\n *\n * Consumers register providers with routing predicates, then call\n * {@link forRepo} to get a {@link RepositoryService} with sub-provider\n * proxies that auto-inject the repository path.\n *\n * The service has no discovery or repository tracking state — it trusts\n * the caller to pass valid repository paths. Discovery and lifecycle\n * (open/close, file watching, etc.) are application-level concerns.\n *\n * @example\n * ```typescript\n * const service = GitService.createSingleton();\n * service.register(cliProvider, (path) => true);\n *\n * const repo = service.forRepo('/home/user/repo');\n * const branches = await repo.branches.getBranches();\n *\n * service.dispose(); // cleans up module-level hooks\n * ```\n */\nexport class GitService implements UnifiedDisposable {\n\tprivate static _instance: GitService | undefined;\n\n\tstatic createSingleton(watchingProvider?: FileWatchingProvider): GitService {\n\t\tif (GitService._instance != null) {\n\t\t\tthrow new Error('GitService already exists — only one instance is allowed');\n\t\t}\n\n\t\tconst instance = new GitService(watchingProvider);\n\t\tGitService._instance = instance;\n\t\treturn instance;\n\t}\n\n\tprivate readonly _onDidChangeProviders = new Emitter<ProvidersChangeEvent>();\n\tget onDidChangeProviders(): Event<ProvidersChangeEvent> {\n\t\treturn this._onDidChangeProviders.event;\n\t}\n\n\tprivate _etag = 0;\n\tprivate readonly _providers: RegisteredProvider[] = [];\n\tprivate readonly _serviceCache = new Map<string, RepositoryService>();\n\tprivate readonly _watchService: RepositoryWatchService | undefined;\n\n\tprivate constructor(watchingProvider?: FileWatchingProvider) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tgetIgnoreFilter: (repoPath, gitDirPath) => {\n\t\t\t\t\treturn this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tsetGlobalRepositoryServiceResolver(repoPath => this.forRepo(repoPath));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tclearGlobalRepositoryServiceResolver();\n\t\tGitService._instance = undefined;\n\t\tthis._serviceCache.clear();\n\t\tthis._watchService?.dispose();\n\n\t\t// Don't dispose providers since they're owned by the caller, but do clear the array to release references\n\t\tthis._providers.length = 0;\n\n\t\tthis._onDidChangeProviders.dispose();\n\t}\n\n\t/** Monotonically increasing counter incremented on structural changes. */\n\tget etag(): number {\n\t\treturn this._etag;\n\t}\n\n\t/** Whether any providers are registered. */\n\tget hasProviders(): boolean {\n\t\treturn this._providers.length > 0;\n\t}\n\n\tget watchService(): RepositoryWatchService | undefined {\n\t\treturn this._watchService;\n\t}\n\n\t/**\n\t * Registers a provider with a routing predicate.\n\t *\n\t * @returns A disposable that unregisters the provider.\n\t */\n\tregister(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable {\n\t\tconst entry: RegisteredProvider = { provider: provider, canHandle: canHandle };\n\t\tthis._providers.push(entry);\n\t\tthis._etag++;\n\n\t\tthis._onDidChangeProviders.fire({\n\t\t\tadded: [provider],\n\t\t\tremoved: [],\n\t\t\tetag: this._etag,\n\t\t});\n\n\t\t// Invalidate cached proxies since provider routing may have changed\n\t\tthis._serviceCache.clear();\n\n\t\treturn createDisposable(() => {\n\t\t\tconst idx = this._providers.indexOf(entry);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis._providers.splice(idx, 1);\n\t\t\t\tthis._etag++;\n\t\t\t\tthis._serviceCache.clear();\n\n\t\t\t\tthis._onDidChangeProviders.fire({\n\t\t\t\t\tadded: [],\n\t\t\t\t\tremoved: [provider],\n\t\t\t\t\tetag: this._etag,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\t/** Returns the provider that can handle the given path, or undefined if none matches. */\n\tgetProvider(repoPath: string): { provider: GitProvider; path: string } | undefined {\n\t\trepoPath = normalizePath(repoPath);\n\t\tfor (const entry of this._providers) {\n\t\t\tif (entry.canHandle(repoPath)) {\n\t\t\t\treturn { provider: entry.provider, path: repoPath };\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Returns all registered providers. */\n\t*getProviders(): Iterable<GitProvider> {\n\t\tfor (const entry of this._providers) {\n\t\t\tyield entry.provider;\n\t\t}\n\t}\n\n\t/**\n\t * Routes a repo URI/path to a provider and returns a cached\n\t * {@link RepositoryService} with repo-scoped sub-provider proxies.\n\t * Returns undefined if no provider can handle the path.\n\t */\n\tforRepo(repoUri: Uri | string): RepositoryService | undefined {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\n\t\tlet repo = this._serviceCache.get(repoPath);\n\t\tif (repo != null) return repo;\n\n\t\tconst result = this.getProvider(repoPath);\n\t\tif (result == null) return undefined;\n\n\t\trepo = createRepositoryService(result.provider, repoPath, this._watchService);\n\t\tthis._serviceCache.set(repoPath, repo);\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Evicts the cached {@link RepositoryService} for a closed or removed repository.\n\t * No-op if the path isn't cached.\n\t */\n\tcloseRepo(repoUri: Uri | string): void {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\t\tthis._serviceCache.delete(repoPath);\n\t}\n\n\t/**\n\t * Validates whether `pathOrUri` points to (or resides inside) a git repository,\n\t * and returns information about it when it does.\n\t *\n\t * Routes the path to a registered provider and delegates to its config sub-provider's\n\t * `getRepositoryInfo` implementation, then normalizes the raw result into a clean\n\t * discriminated union. For a simple yes/no check, use `(await validateRepo(path)).valid`.\n\t *\n\t * Returns `{ valid: false }` if no provider can handle the path or if the routed\n\t * provider does not implement `getRepositoryInfo`.\n\t */\n\tasync validateRepo(pathOrUri: Uri | string): Promise<ValidateRepoResult> {\n\t\tconst key = getRepositoryKey(pathOrUri);\n\t\tconst routed = this.getProvider(key);\n\t\tif (routed == null) return { valid: false };\n\n\t\tconst info = await routed.provider.config.getRepositoryInfo?.(routed.path);\n\t\tif (info == null) return { valid: false };\n\n\t\tif (Array.isArray(info)) {\n\t\t\tif (info.length === 0) return { valid: false };\n\t\t\tif (info[0] === false) return { valid: true, safe: false };\n\t\t\treturn { valid: true, safe: true, repoPath: info[1] };\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsafe: true,\n\t\t\trepoPath: info.repoPath,\n\t\t\tgitDir: info.gitDir,\n\t\t\tcommonGitDir: info.commonGitDir,\n\t\t\tsuperprojectPath: info.superprojectPath,\n\t\t};\n\t}\n}\n\nfunction createRepositoryService(\n\tprovider: GitProvider,\n\trepoPath: string,\n\twatchService?: RepositoryWatchService,\n): RepositoryService {\n\tconst proxies = new Map<string, SubProviderForRepo<unknown>>();\n\n\tfunction getProxy<T extends object>(prop: string, subProvider: T | undefined): SubProviderForRepo<T> | undefined {\n\t\tif (subProvider == null) return undefined;\n\n\t\tlet proxy = proxies.get(prop);\n\t\tif (proxy == null) {\n\t\t\tproxy = createSubProviderProxyForRepo(subProvider, repoPath);\n\t\t\tproxies.set(prop, proxy);\n\t\t}\n\t\treturn proxy as SubProviderForRepo<T>;\n\t}\n\n\treturn {\n\t\tpath: repoPath,\n\t\tprovider: provider.descriptor,\n\n\t\tget etagWorkingTree(): number | undefined {\n\t\t\treturn watchService?.getSession(repoPath)?.etagWorkingTree;\n\t\t},\n\n\t\tgetAbsoluteUri: (relativePath: string): Uri => {\n\t\t\treturn provider.getAbsoluteUri(relativePath, repoPath);\n\t\t},\n\n\t\t// Required sub-providers\n\t\tget branches() {\n\t\t\tconst proxy = getProxy('branches', provider.branches);\n\t\t\tif (proxy == null) throw new Error('GitService: branches sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget commits() {\n\t\t\tconst proxy = getProxy('commits', provider.commits);\n\t\t\tif (proxy == null) throw new Error('GitService: commits sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget config() {\n\t\t\tconst proxy = getProxy('config', provider.config);\n\t\t\tif (proxy == null) throw new Error('GitService: config sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget contributors() {\n\t\t\tconst proxy = getProxy('contributors', provider.contributors);\n\t\t\tif (proxy == null) throw new Error('GitService: contributors sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget diff() {\n\t\t\tconst proxy = getProxy('diff', provider.diff);\n\t\t\tif (proxy == null) throw new Error('GitService: diff sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget graph() {\n\t\t\tconst proxy = getProxy('graph', provider.graph);\n\t\t\tif (proxy == null) throw new Error('GitService: graph sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget refs() {\n\t\t\tconst proxy = getProxy('refs', provider.refs);\n\t\t\tif (proxy == null) throw new Error('GitService: refs sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget remotes() {\n\t\t\tconst proxy = getProxy('remotes', provider.remotes);\n\t\t\tif (proxy == null) throw new Error('GitService: remotes sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget revision() {\n\t\t\tconst proxy = getProxy('revision', provider.revision);\n\t\t\tif (proxy == null) throw new Error('GitService: revision sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget status() {\n\t\t\tconst proxy = getProxy('status', provider.status);\n\t\t\tif (proxy == null) throw new Error('GitService: status sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget tags() {\n\t\t\tconst proxy = getProxy('tags', provider.tags);\n\t\t\tif (proxy == null) throw new Error('GitService: tags sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\n\t\t// Optional sub-providers\n\t\tget blame() {\n\t\t\treturn getProxy('blame', provider.blame);\n\t\t},\n\t\tget maintenance() {\n\t\t\treturn getProxy('maintenance', provider.maintenance);\n\t\t},\n\t\tget ops() {\n\t\t\treturn getProxy('ops', provider.ops);\n\t\t},\n\t\tget patch() {\n\t\t\treturn getProxy('patch', provider.patch);\n\t\t},\n\t\tget pausedOps() {\n\t\t\treturn getProxy('pausedOps', provider.pausedOps);\n\t\t},\n\t\tget staging() {\n\t\t\treturn getProxy('staging', provider.staging);\n\t\t},\n\t\tget stash() {\n\t\t\treturn getProxy('stash', provider.stash);\n\t\t},\n\t\tget worktrees() {\n\t\t\treturn getProxy('worktrees', provider.worktrees);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"service.js","names":[],"sources":["../../../git/src/service.ts"],"sourcesContent":["import type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryKey } from '@gitlens/utils/uri.js';\nimport type { MergeTargetsChangeEvent } from './cache.js';\nimport type { GitProvider } from './providers/provider.js';\nimport type { RepositoryService, SubProviderForRepo } from './repositoryService.js';\nimport {\n\tclearGlobalRepositoryServiceResolver,\n\tcreateSubProviderProxyForRepo,\n\tsetGlobalRepositoryServiceResolver,\n} from './repositoryService.js';\nimport type { FileWatchingProvider } from './watching/provider.js';\nimport { RepositoryWatchService } from './watching/watchService.js';\nimport { WorktreeSummaryService } from './watching/worktreeSummary.js';\n\nexport interface ProvidersChangeEvent {\n\treadonly added: readonly GitProvider[];\n\treadonly removed: readonly GitProvider[];\n\treadonly etag: number;\n}\n\n/**\n * Result of {@link GitService.validateRepo}.\n *\n * - `valid: true, safe: true` — a usable git repository at `repoPath`; `gitDir` / `commonGitDir` / `superprojectPath` are populated when available\n * - `valid: true, safe: false` — git detected unsafe (dubious) ownership at the given path; no `repoPath` is surfaced (the raw call doesn't carry one back)\n * - `valid: false` — not a git repository, no registered provider can handle the path, or the routed provider doesn't implement `getRepositoryInfo`\n */\nexport type ValidateRepoResult =\n\t| {\n\t\t\tvalid: true;\n\t\t\tsafe: true;\n\t\t\trepoPath: string;\n\t\t\tgitDir?: string;\n\t\t\tcommonGitDir?: string;\n\t\t\tsuperprojectPath?: string;\n\t }\n\t| { valid: true; safe: false }\n\t| { valid: false };\n\ninterface RegisteredProvider {\n\tprovider: GitProvider;\n\tcanHandle: (repoPath: string) => boolean;\n}\n\ninterface MergeTargetsSource {\n\treadonly subscription: UnifiedDisposable;\n\trefCount: number;\n}\n\n/**\n * Entry point — provider router, repo-scoped proxy cache, and global configuration.\n *\n * Creating an instance wires up the module-level repository\n * resolver. Calling {@link dispose} tears it down.\n *\n * Consumers register providers with routing predicates, then call\n * {@link forRepo} to get a {@link RepositoryService} with sub-provider\n * proxies that auto-inject the repository path.\n *\n * The service has no discovery or repository tracking state — it trusts\n * the caller to pass valid repository paths. Discovery and lifecycle\n * (open/close, file watching, etc.) are application-level concerns.\n *\n * @example\n * ```typescript\n * const service = GitService.createSingleton();\n * service.register(cliProvider, (path) => true);\n *\n * const repo = service.forRepo('/home/user/repo');\n * const branches = await repo.branches.getBranches();\n *\n * service.dispose(); // cleans up module-level hooks\n * ```\n */\nexport class GitService implements UnifiedDisposable {\n\tprivate static _instance: GitService | undefined;\n\n\tstatic createSingleton(watchingProvider?: FileWatchingProvider): GitService {\n\t\tif (GitService._instance != null) {\n\t\t\tthrow new Error('GitService already exists — only one instance is allowed');\n\t\t}\n\n\t\tconst instance = new GitService(watchingProvider);\n\t\tGitService._instance = instance;\n\t\treturn instance;\n\t}\n\n\tprivate readonly _onDidChangeProviders = new Emitter<ProvidersChangeEvent>();\n\tget onDidChangeProviders(): Event<ProvidersChangeEvent> {\n\t\treturn this._onDidChangeProviders.event;\n\t}\n\n\tprivate readonly _onDidChangeMergeTargets = new Emitter<MergeTargetsChangeEvent>();\n\t/**\n\t * Fires when an external write changed a branch's merge-relevant `.git/gk/config` keys, for any registered\n\t * provider that reports it — never for activity timestamps or core's own writes. Once per change per repository\n\t * (`repoPath` is its common path); see {@link MergeTargetsChangeEvent}.\n\t */\n\tget onDidChangeMergeTargets(): Event<MergeTargetsChangeEvent> {\n\t\treturn this._onDidChangeMergeTargets.event;\n\t}\n\n\t/** Keyed by the provider's event, so providers sharing one cache are forwarded once */\n\tprivate readonly _mergeTargetsSources = new Map<Event<MergeTargetsChangeEvent>, MergeTargetsSource>();\n\n\tprivate _etag = 0;\n\tprivate readonly _providers: RegisteredProvider[] = [];\n\tprivate readonly _serviceCache = new Map<string, RepositoryService>();\n\tprivate readonly _watchService: RepositoryWatchService | undefined;\n\tprivate readonly _summaries: WorktreeSummaryService | undefined;\n\n\tprivate constructor(watchingProvider?: FileWatchingProvider) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tgetIgnoreFilter: (repoPath, gitDirPath) => {\n\t\t\t\t\treturn this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);\n\t\t\t\t},\n\t\t\t\tgetIgnoredDirectories: repoPath =>\n\t\t\t\t\tthis.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([]),\n\t\t\t});\n\t\t\tthis._summaries = new WorktreeSummaryService({\n\t\t\t\twatchService: this._watchService,\n\t\t\t\tgetRepository: repoPath => this.forRepo(repoPath),\n\t\t\t});\n\t\t}\n\n\t\tsetGlobalRepositoryServiceResolver(repoPath => this.forRepo(repoPath));\n\t}\n\n\t[Symbol.dispose](): void {\n\t\tthis.dispose();\n\t}\n\n\tdispose(): void {\n\t\tclearGlobalRepositoryServiceResolver();\n\t\tGitService._instance = undefined;\n\t\tthis._serviceCache.clear();\n\t\tthis._summaries?.dispose();\n\t\tthis._watchService?.dispose();\n\n\t\t// Don't dispose providers since they're owned by the caller, but do clear the array to release references\n\t\tthis._providers.length = 0;\n\n\t\tfor (const source of this._mergeTargetsSources.values()) {\n\t\t\tsource.subscription.dispose();\n\t\t}\n\t\tthis._mergeTargetsSources.clear();\n\n\t\tthis._onDidChangeProviders.dispose();\n\t\tthis._onDidChangeMergeTargets.dispose();\n\t}\n\n\t/** Monotonically increasing counter incremented on structural changes. */\n\tget etag(): number {\n\t\treturn this._etag;\n\t}\n\n\t/** Whether any providers are registered. */\n\tget hasProviders(): boolean {\n\t\treturn this._providers.length > 0;\n\t}\n\n\tget watchService(): RepositoryWatchService | undefined {\n\t\treturn this._watchService;\n\t}\n\n\t/** Cached, change-notifying per-worktree summaries, kept current by {@link watchService}; present when it is */\n\tget summaries(): WorktreeSummaryService | undefined {\n\t\treturn this._summaries;\n\t}\n\n\t/**\n\t * Registers a provider with a routing predicate.\n\t *\n\t * @returns A disposable that unregisters the provider.\n\t */\n\tregister(provider: GitProvider, canHandle: (repoPath: string) => boolean): UnifiedDisposable {\n\t\tconst entry: RegisteredProvider = { provider: provider, canHandle: canHandle };\n\t\tthis._providers.push(entry);\n\t\tthis._etag++;\n\t\tthis.addMergeTargetsSource(provider);\n\n\t\tthis._onDidChangeProviders.fire({\n\t\t\tadded: [provider],\n\t\t\tremoved: [],\n\t\t\tetag: this._etag,\n\t\t});\n\n\t\t// Invalidate cached proxies since provider routing may have changed\n\t\tthis._serviceCache.clear();\n\n\t\treturn createDisposable(() => {\n\t\t\tconst idx = this._providers.indexOf(entry);\n\t\t\tif (idx !== -1) {\n\t\t\t\tthis._providers.splice(idx, 1);\n\t\t\t\tthis._etag++;\n\t\t\t\tthis.removeMergeTargetsSource(provider);\n\t\t\t\tthis._serviceCache.clear();\n\n\t\t\t\tthis._onDidChangeProviders.fire({\n\t\t\t\t\tadded: [],\n\t\t\t\t\tremoved: [provider],\n\t\t\t\t\tetag: this._etag,\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate addMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source != null) {\n\t\t\tsource.refCount++;\n\t\t\treturn;\n\t\t}\n\n\t\tthis._mergeTargetsSources.set(event, {\n\t\t\tsubscription: event(e => this._onDidChangeMergeTargets.fire(e)),\n\t\t\trefCount: 1,\n\t\t});\n\t}\n\n\tprivate removeMergeTargetsSource(provider: GitProvider): void {\n\t\tconst event = provider.onDidChangeMergeTargets;\n\t\tif (event == null) return;\n\n\t\tconst source = this._mergeTargetsSources.get(event);\n\t\tif (source == null || --source.refCount > 0) return;\n\n\t\tsource.subscription.dispose();\n\t\tthis._mergeTargetsSources.delete(event);\n\t}\n\n\t/** Returns the provider that can handle the given path, or undefined if none matches. */\n\tgetProvider(repoPath: string): { provider: GitProvider; path: string } | undefined {\n\t\trepoPath = normalizePath(repoPath);\n\t\tfor (const entry of this._providers) {\n\t\t\tif (entry.canHandle(repoPath)) {\n\t\t\t\treturn { provider: entry.provider, path: repoPath };\n\t\t\t}\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/** Returns all registered providers. */\n\t*getProviders(): Iterable<GitProvider> {\n\t\tfor (const entry of this._providers) {\n\t\t\tyield entry.provider;\n\t\t}\n\t}\n\n\t/**\n\t * Routes a repo URI/path to a provider and returns a cached\n\t * {@link RepositoryService} with repo-scoped sub-provider proxies.\n\t * Returns undefined if no provider can handle the path.\n\t */\n\tforRepo(repoUri: Uri | string): RepositoryService | undefined {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\n\t\tlet repo = this._serviceCache.get(repoPath);\n\t\tif (repo != null) return repo;\n\n\t\tconst result = this.getProvider(repoPath);\n\t\tif (result == null) return undefined;\n\n\t\trepo = createRepositoryService(result.provider, repoPath, this._watchService);\n\t\tthis._serviceCache.set(repoPath, repo);\n\t\treturn repo;\n\t}\n\n\t/**\n\t * Evicts the cached {@link RepositoryService} for a closed or removed repository.\n\t * No-op if the path isn't cached.\n\t */\n\tcloseRepo(repoUri: Uri | string): void {\n\t\tconst repoPath = getRepositoryKey(repoUri);\n\t\tthis._serviceCache.delete(repoPath);\n\t}\n\n\t/**\n\t * Validates whether `pathOrUri` points to (or resides inside) a git repository,\n\t * and returns information about it when it does.\n\t *\n\t * Routes the path to a registered provider and delegates to its config sub-provider's\n\t * `getRepositoryInfo` implementation, then normalizes the raw result into a clean\n\t * discriminated union. For a simple yes/no check, use `(await validateRepo(path)).valid`.\n\t *\n\t * Returns `{ valid: false }` if no provider can handle the path or if the routed\n\t * provider does not implement `getRepositoryInfo`.\n\t */\n\tasync validateRepo(pathOrUri: Uri | string): Promise<ValidateRepoResult> {\n\t\tconst key = getRepositoryKey(pathOrUri);\n\t\tconst routed = this.getProvider(key);\n\t\tif (routed == null) return { valid: false };\n\n\t\tconst info = await routed.provider.config.getRepositoryInfo?.(routed.path);\n\t\tif (info == null) return { valid: false };\n\n\t\tif (Array.isArray(info)) {\n\t\t\tif (info.length === 0) return { valid: false };\n\t\t\tif (info[0] === false) return { valid: true, safe: false };\n\t\t\treturn { valid: true, safe: true, repoPath: info[1] };\n\t\t}\n\n\t\treturn {\n\t\t\tvalid: true,\n\t\t\tsafe: true,\n\t\t\trepoPath: info.repoPath,\n\t\t\tgitDir: info.gitDir,\n\t\t\tcommonGitDir: info.commonGitDir,\n\t\t\tsuperprojectPath: info.superprojectPath,\n\t\t};\n\t}\n}\n\nfunction createRepositoryService(\n\tprovider: GitProvider,\n\trepoPath: string,\n\twatchService?: RepositoryWatchService,\n): RepositoryService {\n\tconst proxies = new Map<string, SubProviderForRepo<unknown>>();\n\n\tfunction getProxy<T extends object>(prop: string, subProvider: T | undefined): SubProviderForRepo<T> | undefined {\n\t\tif (subProvider == null) return undefined;\n\n\t\tlet proxy = proxies.get(prop);\n\t\tif (proxy == null) {\n\t\t\tproxy = createSubProviderProxyForRepo(subProvider, repoPath);\n\t\t\tproxies.set(prop, proxy);\n\t\t}\n\t\treturn proxy as SubProviderForRepo<T>;\n\t}\n\n\treturn {\n\t\tpath: repoPath,\n\t\tprovider: provider.descriptor,\n\n\t\tget etagWorkingTree(): number | undefined {\n\t\t\treturn watchService?.getSession(repoPath)?.etagWorkingTree;\n\t\t},\n\n\t\tgetAbsoluteUri: (relativePath: string): Uri => {\n\t\t\treturn provider.getAbsoluteUri(relativePath, repoPath);\n\t\t},\n\n\t\t// Required sub-providers\n\t\tget branches() {\n\t\t\tconst proxy = getProxy('branches', provider.branches);\n\t\t\tif (proxy == null) throw new Error('GitService: branches sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget commits() {\n\t\t\tconst proxy = getProxy('commits', provider.commits);\n\t\t\tif (proxy == null) throw new Error('GitService: commits sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget config() {\n\t\t\tconst proxy = getProxy('config', provider.config);\n\t\t\tif (proxy == null) throw new Error('GitService: config sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget contributors() {\n\t\t\tconst proxy = getProxy('contributors', provider.contributors);\n\t\t\tif (proxy == null) throw new Error('GitService: contributors sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget diff() {\n\t\t\tconst proxy = getProxy('diff', provider.diff);\n\t\t\tif (proxy == null) throw new Error('GitService: diff sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget graph() {\n\t\t\tconst proxy = getProxy('graph', provider.graph);\n\t\t\tif (proxy == null) throw new Error('GitService: graph sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget refs() {\n\t\t\tconst proxy = getProxy('refs', provider.refs);\n\t\t\tif (proxy == null) throw new Error('GitService: refs sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget remotes() {\n\t\t\tconst proxy = getProxy('remotes', provider.remotes);\n\t\t\tif (proxy == null) throw new Error('GitService: remotes sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget revision() {\n\t\t\tconst proxy = getProxy('revision', provider.revision);\n\t\t\tif (proxy == null) throw new Error('GitService: revision sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget status() {\n\t\t\tconst proxy = getProxy('status', provider.status);\n\t\t\tif (proxy == null) throw new Error('GitService: status sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\t\tget tags() {\n\t\t\tconst proxy = getProxy('tags', provider.tags);\n\t\t\tif (proxy == null) throw new Error('GitService: tags sub-provider is required but not registered');\n\t\t\treturn proxy;\n\t\t},\n\n\t\t// Optional sub-providers\n\t\tget blame() {\n\t\t\treturn getProxy('blame', provider.blame);\n\t\t},\n\t\tget maintenance() {\n\t\t\treturn getProxy('maintenance', provider.maintenance);\n\t\t},\n\t\tget ops() {\n\t\t\treturn getProxy('ops', provider.ops);\n\t\t},\n\t\tget patch() {\n\t\t\treturn getProxy('patch', provider.patch);\n\t\t},\n\t\tget pausedOps() {\n\t\t\treturn getProxy('pausedOps', provider.pausedOps);\n\t\t},\n\t\tget staging() {\n\t\t\treturn getProxy('staging', provider.staging);\n\t\t},\n\t\tget stash() {\n\t\t\treturn getProxy('stash', provider.stash);\n\t\t},\n\t\tget worktrees() {\n\t\t\treturn getProxy('worktrees', provider.worktrees);\n\t\t},\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1,20 +1,52 @@
|
|
|
1
1
|
import { RepositoryChange } from "../models/repository.js";
|
|
2
2
|
//#region ../git/src/watching/changeEvent.d.ts
|
|
3
|
+
/** The {@link RepositoryChange} kinds that describe ref movement, which an event can attribute to named refs */
|
|
4
|
+
export type RefChangeKind = 'heads' | 'remotes' | 'tags' | 'stash';
|
|
5
|
+
export interface WatcherRefChanges {
|
|
6
|
+
/** Full ref names whose files changed (`refs/heads/main`), plus per-worktree pseudo-refs (`HEAD`, `ORIG_HEAD`). A name may be a ref directory and then covers every ref beneath it. */
|
|
7
|
+
readonly names?: Iterable<string>;
|
|
8
|
+
/** Ref kinds that changed without a name (packed-refs rewrite, config, shallow/replace, an injected change): any ref of that kind may have moved. */
|
|
9
|
+
readonly unnamed?: Iterable<RefChangeKind>;
|
|
10
|
+
}
|
|
11
|
+
export declare function isRefChangeKind(change: RepositoryChange): change is RefChangeKind;
|
|
12
|
+
/**
|
|
13
|
+
* The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,
|
|
14
|
+
* `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getRefChangeKind(ref: string): RefChangeKind | undefined;
|
|
3
17
|
/**
|
|
4
18
|
* An event describing changes to a repository, produced by the watching pipeline.
|
|
5
19
|
*
|
|
6
20
|
* Changes are coalesced within a debounce window — a single event may represent
|
|
7
|
-
* multiple filesystem changes. Use {@link changed} to test for specific change types
|
|
21
|
+
* multiple filesystem changes. Use {@link changed} to test for specific change types,
|
|
22
|
+
* and {@link mayHaveChangedRef} to test whether a specific ref may have moved.
|
|
8
23
|
*/
|
|
9
24
|
export declare class WatcherRepoChangeEvent {
|
|
10
25
|
/** The repository path this event is for */
|
|
11
26
|
readonly repoPath: string;
|
|
12
27
|
protected readonly _changes: Set<RepositoryChange>;
|
|
28
|
+
private readonly _refs;
|
|
29
|
+
private readonly _unnamedRefKinds;
|
|
13
30
|
constructor(
|
|
14
31
|
/** The repository path this event is for */
|
|
15
|
-
repoPath: string, changes: RepositoryChange[]
|
|
32
|
+
repoPath: string, changes: RepositoryChange[],
|
|
33
|
+
/** Which refs changed; when omitted, every ref kind in `changes` is unnamed (any ref of that kind may have moved) */
|
|
34
|
+
refs?: WatcherRefChanges);
|
|
16
35
|
get changes(): ReadonlySet<RepositoryChange>;
|
|
36
|
+
/** Full ref names (or ref directories, or pseudo-refs) known to have changed. See {@link WatcherRefChanges.names}. */
|
|
37
|
+
get refs(): ReadonlySet<string>;
|
|
38
|
+
/** Ref kinds that changed without a name — any ref of these kinds may have moved */
|
|
39
|
+
get unnamedRefKinds(): ReadonlySet<RefChangeKind>;
|
|
17
40
|
toString(changesOnly?: boolean): string;
|
|
41
|
+
/**
|
|
42
|
+
* Test whether the given ref may have moved in this event.
|
|
43
|
+
*
|
|
44
|
+
* @param ref - A full ref name (`refs/heads/main`) or a pseudo-ref (`HEAD`).
|
|
45
|
+
* @returns `false` when the ref's kind didn't change (or the ref is outside the watched kinds); `true` when its kind
|
|
46
|
+
* changed without a name, or when the ref itself or a ref directory above it changed. `HEAD` is usually a symref
|
|
47
|
+
* to the checked-out branch, so it may have moved whenever any `heads` ref did.
|
|
48
|
+
*/
|
|
49
|
+
mayHaveChangedRef(ref: string): boolean;
|
|
18
50
|
/**
|
|
19
51
|
* Test whether any of the specified change types are present in the event.
|
|
20
52
|
*
|
|
@@ -32,9 +64,17 @@ export declare class WatcherRepoChangeEvent {
|
|
|
32
64
|
* @returns `true` if the event contains only the specified changes.
|
|
33
65
|
*/
|
|
34
66
|
changedExclusive(...affected: RepositoryChange[]): boolean;
|
|
35
|
-
/**
|
|
36
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,
|
|
69
|
+
* every ref kind in `changes` counts as unnamed.
|
|
70
|
+
*/
|
|
71
|
+
with(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent;
|
|
37
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns the name in `names` that covers `ref` — `ref` itself, or a ref directory above it (compared on `/`
|
|
75
|
+
* boundaries, so `refs/heads/fe` doesn't cover `refs/heads/feature`) — or `undefined` if none does.
|
|
76
|
+
*/
|
|
77
|
+
export declare function findCoveringRefName(names: ReadonlySet<string>, ref: string): string | undefined;
|
|
38
78
|
/**
|
|
39
79
|
* An event describing working tree file changes, produced by the working tree pipeline.
|
|
40
80
|
*
|
|
@@ -42,7 +82,10 @@ export declare class WatcherRepoChangeEvent {
|
|
|
42
82
|
*/
|
|
43
83
|
export interface WorkingTreeChangeEvent {
|
|
44
84
|
readonly repoPath: string;
|
|
45
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* Absolute paths of changed files or directories (post-gitignore filtering). A directory stands for everything
|
|
87
|
+
* beneath it. Partial when {@link incomplete}.
|
|
88
|
+
*/
|
|
46
89
|
readonly paths: ReadonlySet<string>;
|
|
47
90
|
/**
|
|
48
91
|
* `true` when more paths changed than the accumulator retains, so {@link paths} is a partial set and a
|