@gitkraken/core-gitlens 0.15.4 → 0.15.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +25 -1
  2. package/dist/git/cache.d.ts +9 -12
  3. package/dist/git/cache.js +34 -16
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/context.d.ts +19 -2
  6. package/dist/git/context.js.map +1 -1
  7. package/dist/git/providers/provider.d.ts +7 -0
  8. package/dist/git/service.d.ts +5 -1
  9. package/dist/git/service.js +8 -4
  10. package/dist/git/service.js.map +1 -1
  11. package/dist/git/watching/changeEvent.d.ts +25 -1
  12. package/dist/git/watching/changeEvent.js +36 -2
  13. package/dist/git/watching/changeEvent.js.map +1 -1
  14. package/dist/git/watching/configSections.d.ts +35 -0
  15. package/dist/git/watching/configSections.js +210 -0
  16. package/dist/git/watching/configSections.js.map +1 -0
  17. package/dist/git/watching/watchGroup.d.ts +36 -3
  18. package/dist/git/watching/watchGroup.js +107 -2
  19. package/dist/git/watching/watchGroup.js.map +1 -1
  20. package/dist/git/watching/watchService.d.ts +14 -3
  21. package/dist/git/watching/watchService.js +10 -7
  22. package/dist/git/watching/watchService.js.map +1 -1
  23. package/dist/git/watching/watchSession.d.ts +52 -21
  24. package/dist/git/watching/watchSession.js +164 -42
  25. package/dist/git/watching/watchSession.js.map +1 -1
  26. package/dist/git/watching/worktreeSummary.d.ts +9 -7
  27. package/dist/git/watching/worktreeSummary.js +21 -10
  28. package/dist/git/watching/worktreeSummary.js.map +1 -1
  29. package/dist/git-cli/cliGitProvider.d.ts +6 -1
  30. package/dist/git-cli/cliGitProvider.js +45 -5
  31. package/dist/git-cli/cliGitProvider.js.map +1 -1
  32. package/dist/git-cli/providers/branches.d.ts +2 -2
  33. package/dist/git-cli/providers/branches.js +17 -15
  34. package/dist/git-cli/providers/branches.js.map +1 -1
  35. package/dist/git-cli/providers/config.d.ts +13 -0
  36. package/dist/git-cli/providers/config.js +38 -1
  37. package/dist/git-cli/providers/config.js.map +1 -1
  38. package/dist/git-cli/providers/operations.d.ts +35 -1
  39. package/dist/git-cli/providers/operations.js +175 -16
  40. package/dist/git-cli/providers/operations.js.map +1 -1
  41. package/dist/git-cli/providers/stash.d.ts +1 -0
  42. package/dist/git-cli/providers/stash.js +9 -3
  43. package/dist/git-cli/providers/stash.js.map +1 -1
  44. package/dist/git-cli/providers/worktrees.js +11 -4
  45. package/dist/git-cli/providers/worktrees.js.map +1 -1
  46. package/dist/git-cli/service.js +5 -1
  47. package/dist/git-cli/service.js.map +1 -1
  48. package/dist/plus/integrations/collectionMetadata.js +4 -2
  49. package/dist/plus/integrations/collectionMetadata.js.map +1 -1
  50. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  51. package/dist/plus/integrations/providers/providerErrors.d.ts +22 -0
  52. package/dist/plus/integrations/providers/providerErrors.js +43 -2
  53. package/dist/plus/integrations/providers/providerErrors.js.map +1 -1
  54. package/dist/plus/integrations/providers/providersApi.js +12 -8
  55. package/dist/plus/integrations/providers/providersApi.js.map +1 -1
  56. package/dist/plus/integrations/reads/drains.d.ts +0 -1
  57. package/dist/plus/integrations/results.d.ts +13 -3
  58. package/dist/plus/integrations/results.js +12 -2
  59. package/dist/plus/integrations/results.js.map +1 -1
  60. package/docs/integrations.md +14 -0
  61. package/package.json +1 -1
@@ -139,8 +139,25 @@ export interface GitServiceHooks {
139
139
  };
140
140
  /** Repository state hooks — wired by the provider to its repository-changed emitter */
141
141
  readonly repository?: {
142
- /** Called when repository state has changed */
143
- onChanged?(repoPath: string, changes: RepositoryChange[]): void;
142
+ /**
143
+ * Called when repository state has changed.
144
+ *
145
+ * `refs`, when given, are the full ref names (`refs/heads/main`, `refs/remotes/origin/main`) the write
146
+ * created, moved or deleted. A ref kind in `changes` (`heads`, `remotes`, `tags`, `stash`) with at least
147
+ * one name of its kind in `refs` is fully described by those names: no other ref of that kind moved. A ref
148
+ * kind in `changes` with no name in `refs` stays unnamed — any ref of that kind may have moved. A host that
149
+ * ignores `refs` treats every ref kind as unnamed, as before.
150
+ *
151
+ * `options.configConfinedToRefs`, given only with `config` among `changes`, says the write changed
152
+ * `.git/config` only in the `branch.<name>` sections of the local branches `refs` names (`refs/heads/<name>`),
153
+ * as a branch create, delete, rename or upstream change does. A branch's section matters only to a checkout on
154
+ * that branch or one whose upstream or base is that branch, so the config change concerns exactly the
155
+ * checkouts following those branches. Without it, the config change may be anywhere in the file, whatever
156
+ * `refs` names. A host that ignores it treats every config change as unconfined, as before.
157
+ */
158
+ onChanged?(repoPath: string, changes: RepositoryChange[], refs?: readonly string[], options?: {
159
+ readonly configConfinedToRefs?: boolean;
160
+ }): void;
144
161
  };
145
162
  /** Commit lifecycle hooks */
146
163
  readonly commits?: {
@@ -1 +1 @@
1
- {"version":3,"file":"context.js","names":[],"sources":["../../../git/src/context.ts"],"sourcesContent":["import type { Event } from '@gitlens/utils/event.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { CachedGitTypes } from './cache.js';\nimport type { SigningErrorReason } from './errors.js';\nimport type { GitRemote } from './models/remote.js';\nimport type { RemoteProvider, RemoteProviderId } from './models/remoteProvider.js';\nimport type { RepositoryChange } from './models/repository.js';\nimport type { SearchQuery } from './models/search.js';\nimport type { SigningFormat } from './models/signature.js';\nimport type { GitConflictFile } from './models/staging.js';\nimport type { RemoteProviderConfig } from './remotes/matcher.js';\n\n/**\n * Context provided to sub-providers at construction time.\n * Replaces the Container dependency in the GitLens extension.\n *\n * This is intentionally minimal — most configuration is passed as explicit\n * per-call options on sub-provider methods, not hidden in a context object.\n */\nexport interface GitServiceContext {\n\t/**\n\t * Provides default values for sub-provider options when callers omit them.\n\t * If not provided, sub-providers use their own built-in defaults.\n\t */\n\treadonly config?: GitServiceConfig;\n\t/** File system abstraction for reading files, checking stats, and listing directories. */\n\treadonly fs: FileSystemProvider;\n\t/** Outbound hooks for events from library to host */\n\treadonly hooks?: GitServiceHooks;\n\t/** Host-side remote capabilities (custom providers, sorting, repo info) */\n\treadonly remotes?: RemotesProvider;\n\t/** Search query preprocessing hooks (e.g., NLP → structured search). */\n\treadonly searchQuery?: SearchQueryProvider;\n\t/** Workspace environment (folder resolution, trust state) */\n\treadonly workspace?: WorkspaceProvider;\n}\n\n/**\n * Provides default values for sub-provider options when callers omit them.\n *\n * Consumers implement this backed by their own config system (e.g., VS Code settings).\n * Properties are getters so values are read fresh each time — consumers can make them\n * reactive to config changes.\n *\n * Library sub-providers check `context.config?.{property}` when the caller doesn't\n * pass the corresponding option, falling back to sensible built-in defaults if the\n * config provider is also absent.\n */\nexport interface GitServiceConfig {\n\treadonly commits: {\n\t\t/**\n\t\t * Whether commit/stash queries include per-commit file details by default, resolved per\n\t\t * repository so a host can defer them where loading them has proven slow.\n\t\t */\n\t\treadonly includeFileDetails?: (repoPath: string) => boolean;\n\t\t/** Default commit ordering for log/branch queries (e.g., `'date'`, `'author-date'`, `'topo'`). */\n\t\treadonly ordering?: 'date' | 'author-date' | 'topo' | null;\n\t\t/** Default maximum items for operations (0 = unlimited). */\n\t\treadonly maxItems?: number;\n\t\t/** Similarity threshold for rename detection (0–100). `null` uses Git's default. */\n\t\treadonly similarityThreshold?: number | null;\n\t};\n\n\t/** File-history–specific defaults for `getLogForPath`. */\n\treadonly fileHistory?: {\n\t\t/** Whether to show commits from all branches (maps to `--all`). */\n\t\treadonly showAllBranches?: boolean;\n\t\t/** Whether to include merge commits (maps to `--no-merges` when false). */\n\t\treadonly showMergeCommits?: boolean;\n\t\t/** Whether to follow renames (maps to `--follow`). */\n\t\treadonly followRenames?: boolean;\n\t};\n\n\t/** Search-specific defaults for `searchCommits`. */\n\treadonly search?: {\n\t\t/** Default maximum items for search results (0 = unlimited). */\n\t\treadonly maxItems?: number;\n\t};\n\n\t/** Graph-specific configuration defaults. */\n\treadonly graph?: {\n\t\t/** Whether to keep git's commit-graph file up to date in the background after graph loads —\n\t\t * written when missing, periodically refreshed to cover new commits (accelerates ordered\n\t\t * history walks — the graph's initial load). Fed from the master switch\n\t\t * (`gitlens.gitOptimizations.enabled`); `false` disables the background write. */\n\t\treadonly writeCommitGraph?: boolean;\n\t\t/** Graph commit ordering (falls back to `commits.ordering` or `'date'`). */\n\t\treadonly commitOrdering?: 'date' | 'author-date' | 'topo';\n\t\t/** Whether to only follow first parent in graph. */\n\t\treadonly onlyFollowFirstParent?: boolean;\n\t\t/** Whether to use avatars in graph. */\n\t\treadonly avatars?: boolean;\n\t\t/** Maximum search items (0 = unlimited). */\n\t\treadonly maxSearchItems?: number;\n\t};\n\n\t/** Auto-tier git-optimization (maintenance) preferences. */\n\treadonly maintenance?: {\n\t\t/** Master switch for the auto-tier optimizations (maps to `gitlens.gitOptimizations.enabled`). Defaults to `true`. */\n\t\treadonly enabled?: boolean;\n\t};\n\n\t/** Force-push preferences. */\n\treadonly push?: {\n\t\t/** Whether to use `--force-with-lease` instead of a plain `--force` when force-pushing. Defaults to `true`. */\n\t\treadonly useForceWithLease?: boolean;\n\t\t/**\n\t\t * Whether to add `--force-if-includes` when force-pushing. Only applies when `useForceWithLease` is `true`\n\t\t * and the installed Git supports it. Defaults to `true`.\n\t\t */\n\t\treadonly useForceIfIncludes?: boolean;\n\t};\n\n\t/** Commit-signing preferences. */\n\treadonly signing?: {\n\t\t/**\n\t\t * Host-level override for whether commit signing is enabled (e.g. VS Code's `git.enableCommitSigning`).\n\t\t * When `true`, signing is treated as enabled even if the repo's `commit.gpgsign` is unset/false. It never\n\t\t * forces signing off.\n\t\t */\n\t\treadonly enabled?: boolean;\n\t};\n}\n\n/** Git command types that can produce conflicts. */\nexport type GitConflictCommand = 'merge' | 'rebase' | 'cherry-pick' | 'revert' | 'stash-apply' | 'stash-pop';\n\n/**\n * Hooks for outbound events from library to host.\n *\n * All hooks are optional — the library calls them when events occur,\n * and the host decides what to do (telemetry, logging, UI updates, etc.).\n *\n * Provider-augmented hooks (`cache`, `repository`) are wired by the provider\n * to its emitters at construction time. Pass-through hooks (`commits`,\n * `operations`) are provided by the host and forwarded unchanged.\n */\nexport interface GitServiceHooks {\n\t/** Cache lifecycle hooks — wired by the provider to its cache-reset emitter */\n\treadonly cache?: {\n\t\t/**\n\t\t * Called after the provider changed repository state (a typed write, or `notifyChanged`). The provider\n\t\t * has already cleared its own caches for those types and the repository's pending commands (its sibling\n\t\t * worktrees included); the handler only needs to update host-side state. Forwarding to\n\t\t * `Cache.clearCaches` as well is redundant but harmless.\n\t\t */\n\t\tonReset?(repoPath: string, ...types: CachedGitTypes[]): void;\n\t};\n\t/** Repository state hooks — wired by the provider to its repository-changed emitter */\n\treadonly repository?: {\n\t\t/** Called when repository state has changed */\n\t\tonChanged?(repoPath: string, changes: RepositoryChange[]): void;\n\t};\n\t/** Commit lifecycle hooks */\n\treadonly commits?: {\n\t\t/**\n\t\t * Called when a commit is signed successfully.\n\t\t *\n\t\t * Note: currently fired only from explicit-sign paths — the patch provider's\n\t\t * `commit-tree -S` and the commit operation when the host signing override\n\t\t * (`config.signing.enabled`) adds `-S`. Operations that sign implicitly via\n\t\t * `commit.gpgsign=true` (`commit` without the override, `merge`, `pull`,\n\t\t * `rebase`, `revert`, `cherryPick`) do not fire this hook because the\n\t\t * library cannot cheaply confirm that signing actually occurred without an\n\t\t * extra `git config`/`log --show-signature` call.\n\t\t */\n\t\tonSigned?(format: SigningFormat, source?: unknown): void;\n\t\t/** Called when commit signing fails — fired from all signing-capable paths. */\n\t\tonSigningFailed?(reason: SigningErrorReason, format: SigningFormat, source?: unknown): void;\n\t};\n\t/** Git operation hooks */\n\treadonly operations?: {\n\t\t/**\n\t\t * Called when a git command produced a conflict.\n\t\t * @param conflicts Optional conflicted file list. Populated for merge/rebase/cherry-pick/revert;\n\t\t * may be absent for stash operations or when the file list couldn't be read.\n\t\t */\n\t\tonConflicted?(command: GitConflictCommand, conflicts?: GitConflictFile[]): void;\n\t\t/** Called when getGitDir resolves to a non-existent .git directory or rev-parse fails */\n\t\tonGitDirResolveFailed?(repoPath: string, gitDir: string, errorMessage: string): void;\n\t\t/**\n\t\t * Called when a git operation that can start a rebase begins (`'started'`, before the\n\t\t * process runs) and again when the command exits (`'ended'`, on success and failure alike).\n\t\t * Fired at start (not from the result) because `git rebase` exits 0 when stopping at\n\t\t * `edit`/`break`, and fired for every pull because `pull.rebase=true` config can start\n\t\t * a rebase the host can't predict from the arguments. The `'ended'` phase lets the host\n\t\t * check whether the operation actually left a rebase in progress.\n\t\t * @param repoPath The path the rebase state will appear under (the worktree path for\n\t\t * worktree-targeted pulls)\n\t\t */\n\t\tonRebaseCapableOperation?(repoPath: string, command: 'rebase' | 'pull', phase: 'started' | 'ended'): void;\n\t};\n}\n\n/**\n * Host-provided context attached to remote providers at construction time.\n * Subset of {@link RemotesProvider} — only the fields individual providers need.\n */\nexport interface RemoteProviderContext {\n\t/** Returns repository info (e.g., ID) from the host's integration system */\n\treadonly getRepositoryInfo?: (\n\t\tproviderId: RemoteProviderId,\n\t\ttarget: { owner: string; name: string; project?: string },\n\t) => Promise<{ id: string } | undefined>;\n}\n\n/**\n * File system abstraction following the `workspace.fs` model.\n *\n * Hosts provide an implementation backed by their platform:\n * - Desktop (Node.js): wraps `workspace.fs` or `fs/promises`\n * - Browser: wraps `workspace.fs` for virtual file systems\n *\n * Used by sub-providers that need file system access without\n * importing Node.js `fs` or VS Code APIs directly.\n */\nexport interface FileStat {\n\ttype: number;\n\tctime: number;\n\tmtime: number;\n\tsize: number;\n}\n\n/**\n * File type bitmask matching `vscode.FileType`.\n * Values can be combined (e.g., `FileType.File | FileType.SymbolicLink`).\n */\nexport const FileType = {\n\tUnknown: 0,\n\tFile: 1,\n\tDirectory: 2,\n\tSymbolicLink: 64,\n} as const;\nexport type FileType = (typeof FileType)[keyof typeof FileType];\n\nexport interface FileSystemProvider {\n\treadDirectory(uri: Uri): Promise<[string, number][]>;\n\treadFile(uri: Uri): Promise<Uint8Array>;\n\tstat(uri: Uri): Promise<FileStat | undefined>;\n}\n\n/**\n * Host-side remote capabilities: custom provider configs, sorting, and repository info.\n *\n * The host provides extra provider configs (user custom remotes, cloud self-managed hosts)\n * and the library combines them with built-in providers to build a matcher.\n * Sorting lets the host control remote priority (integration metadata, user preferences).\n * Repository info enables cross-fork PR URLs via integration API lookups.\n */\nexport interface RemotesProvider {\n\t/**\n\t * Returns extra remote provider configurations beyond built-ins.\n\t * Includes user-configured custom remotes (from settings) and\n\t * cloud self-managed host integrations.\n\t */\n\tgetCustomProviders?(repoPath: string): Promise<RemoteProviderConfig[] | undefined>;\n\n\t/**\n\t * Returns repository info from the host's integration system.\n\t * Used by remote providers for cross-fork PR creation URLs.\n\t * Returns `undefined` if no integration is available or connected.\n\t */\n\tgetRepositoryInfo?(\n\t\tproviderId: RemoteProviderId,\n\t\ttarget: { owner: string; name: string; project?: string },\n\t): Promise<{ id: string } | undefined>;\n\n\t/**\n\t * Sorts remotes by priority. The host owns the full ranking policy\n\t * (remote name heuristics, integration metadata, user preferences).\n\t * If not provided, the library returns remotes in their original order.\n\t */\n\tsort?(remotes: GitRemote<RemoteProvider>[], cancellation?: AbortSignal): Promise<GitRemote<RemoteProvider>[]>;\n}\n\n/**\n * Provides search query preprocessing.\n *\n * Hosts implement this to add capabilities like NLP → structured search\n * conversion before the library executes the query.\n */\nexport interface SearchQueryProvider {\n\t/**\n\t * Pre-processes a search query before execution (e.g., converts natural language to structured search).\n\t * Called by commits sub-provider before executing `searchCommits`.\n\t */\n\tpreprocessQuery?(search: SearchQuery, source?: unknown): Promise<SearchQuery>;\n}\n\n/**\n * Provides workspace environment info: folder resolution and trust state.\n * Used by sub-providers for workspace folder lookup and to gate operations\n * in untrusted workspaces.\n *\n * When `isTrusted` is absent, the workspace is assumed trusted.\n */\nexport interface WorkspaceProvider {\n\t/** Fires when the workspace trust state changes (e.g., user grants trust) */\n\treadonly onDidChangeTrust?: Event<boolean>;\n\n\t/** Whether the workspace is currently trusted */\n\treadonly isTrusted?: boolean;\n\n\t/** Resolves the workspace folder that contains the given repoPath */\n\tgetFolder(repoPath: string): { path: string } | undefined;\n\n\t/** Returns the default URI for new worktrees, given a repository path. */\n\tgetWorktreeDefaultUri?(repoPath: string): Uri | undefined;\n}\n"],"mappings":";;;;;AAmOA,MAAa,WAAW;CACvB,SAAS;CACT,MAAM;CACN,WAAW;CACX,cAAc;AACf"}
1
+ {"version":3,"file":"context.js","names":[],"sources":["../../../git/src/context.ts"],"sourcesContent":["import type { Event } from '@gitlens/utils/event.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { CachedGitTypes } from './cache.js';\nimport type { SigningErrorReason } from './errors.js';\nimport type { GitRemote } from './models/remote.js';\nimport type { RemoteProvider, RemoteProviderId } from './models/remoteProvider.js';\nimport type { RepositoryChange } from './models/repository.js';\nimport type { SearchQuery } from './models/search.js';\nimport type { SigningFormat } from './models/signature.js';\nimport type { GitConflictFile } from './models/staging.js';\nimport type { RemoteProviderConfig } from './remotes/matcher.js';\n\n/**\n * Context provided to sub-providers at construction time.\n * Replaces the Container dependency in the GitLens extension.\n *\n * This is intentionally minimal — most configuration is passed as explicit\n * per-call options on sub-provider methods, not hidden in a context object.\n */\nexport interface GitServiceContext {\n\t/**\n\t * Provides default values for sub-provider options when callers omit them.\n\t * If not provided, sub-providers use their own built-in defaults.\n\t */\n\treadonly config?: GitServiceConfig;\n\t/** File system abstraction for reading files, checking stats, and listing directories. */\n\treadonly fs: FileSystemProvider;\n\t/** Outbound hooks for events from library to host */\n\treadonly hooks?: GitServiceHooks;\n\t/** Host-side remote capabilities (custom providers, sorting, repo info) */\n\treadonly remotes?: RemotesProvider;\n\t/** Search query preprocessing hooks (e.g., NLP → structured search). */\n\treadonly searchQuery?: SearchQueryProvider;\n\t/** Workspace environment (folder resolution, trust state) */\n\treadonly workspace?: WorkspaceProvider;\n}\n\n/**\n * Provides default values for sub-provider options when callers omit them.\n *\n * Consumers implement this backed by their own config system (e.g., VS Code settings).\n * Properties are getters so values are read fresh each time — consumers can make them\n * reactive to config changes.\n *\n * Library sub-providers check `context.config?.{property}` when the caller doesn't\n * pass the corresponding option, falling back to sensible built-in defaults if the\n * config provider is also absent.\n */\nexport interface GitServiceConfig {\n\treadonly commits: {\n\t\t/**\n\t\t * Whether commit/stash queries include per-commit file details by default, resolved per\n\t\t * repository so a host can defer them where loading them has proven slow.\n\t\t */\n\t\treadonly includeFileDetails?: (repoPath: string) => boolean;\n\t\t/** Default commit ordering for log/branch queries (e.g., `'date'`, `'author-date'`, `'topo'`). */\n\t\treadonly ordering?: 'date' | 'author-date' | 'topo' | null;\n\t\t/** Default maximum items for operations (0 = unlimited). */\n\t\treadonly maxItems?: number;\n\t\t/** Similarity threshold for rename detection (0–100). `null` uses Git's default. */\n\t\treadonly similarityThreshold?: number | null;\n\t};\n\n\t/** File-history–specific defaults for `getLogForPath`. */\n\treadonly fileHistory?: {\n\t\t/** Whether to show commits from all branches (maps to `--all`). */\n\t\treadonly showAllBranches?: boolean;\n\t\t/** Whether to include merge commits (maps to `--no-merges` when false). */\n\t\treadonly showMergeCommits?: boolean;\n\t\t/** Whether to follow renames (maps to `--follow`). */\n\t\treadonly followRenames?: boolean;\n\t};\n\n\t/** Search-specific defaults for `searchCommits`. */\n\treadonly search?: {\n\t\t/** Default maximum items for search results (0 = unlimited). */\n\t\treadonly maxItems?: number;\n\t};\n\n\t/** Graph-specific configuration defaults. */\n\treadonly graph?: {\n\t\t/** Whether to keep git's commit-graph file up to date in the background after graph loads —\n\t\t * written when missing, periodically refreshed to cover new commits (accelerates ordered\n\t\t * history walks — the graph's initial load). Fed from the master switch\n\t\t * (`gitlens.gitOptimizations.enabled`); `false` disables the background write. */\n\t\treadonly writeCommitGraph?: boolean;\n\t\t/** Graph commit ordering (falls back to `commits.ordering` or `'date'`). */\n\t\treadonly commitOrdering?: 'date' | 'author-date' | 'topo';\n\t\t/** Whether to only follow first parent in graph. */\n\t\treadonly onlyFollowFirstParent?: boolean;\n\t\t/** Whether to use avatars in graph. */\n\t\treadonly avatars?: boolean;\n\t\t/** Maximum search items (0 = unlimited). */\n\t\treadonly maxSearchItems?: number;\n\t};\n\n\t/** Auto-tier git-optimization (maintenance) preferences. */\n\treadonly maintenance?: {\n\t\t/** Master switch for the auto-tier optimizations (maps to `gitlens.gitOptimizations.enabled`). Defaults to `true`. */\n\t\treadonly enabled?: boolean;\n\t};\n\n\t/** Force-push preferences. */\n\treadonly push?: {\n\t\t/** Whether to use `--force-with-lease` instead of a plain `--force` when force-pushing. Defaults to `true`. */\n\t\treadonly useForceWithLease?: boolean;\n\t\t/**\n\t\t * Whether to add `--force-if-includes` when force-pushing. Only applies when `useForceWithLease` is `true`\n\t\t * and the installed Git supports it. Defaults to `true`.\n\t\t */\n\t\treadonly useForceIfIncludes?: boolean;\n\t};\n\n\t/** Commit-signing preferences. */\n\treadonly signing?: {\n\t\t/**\n\t\t * Host-level override for whether commit signing is enabled (e.g. VS Code's `git.enableCommitSigning`).\n\t\t * When `true`, signing is treated as enabled even if the repo's `commit.gpgsign` is unset/false. It never\n\t\t * forces signing off.\n\t\t */\n\t\treadonly enabled?: boolean;\n\t};\n}\n\n/** Git command types that can produce conflicts. */\nexport type GitConflictCommand = 'merge' | 'rebase' | 'cherry-pick' | 'revert' | 'stash-apply' | 'stash-pop';\n\n/**\n * Hooks for outbound events from library to host.\n *\n * All hooks are optional — the library calls them when events occur,\n * and the host decides what to do (telemetry, logging, UI updates, etc.).\n *\n * Provider-augmented hooks (`cache`, `repository`) are wired by the provider\n * to its emitters at construction time. Pass-through hooks (`commits`,\n * `operations`) are provided by the host and forwarded unchanged.\n */\nexport interface GitServiceHooks {\n\t/** Cache lifecycle hooks — wired by the provider to its cache-reset emitter */\n\treadonly cache?: {\n\t\t/**\n\t\t * Called after the provider changed repository state (a typed write, or `notifyChanged`). The provider\n\t\t * has already cleared its own caches for those types and the repository's pending commands (its sibling\n\t\t * worktrees included); the handler only needs to update host-side state. Forwarding to\n\t\t * `Cache.clearCaches` as well is redundant but harmless.\n\t\t */\n\t\tonReset?(repoPath: string, ...types: CachedGitTypes[]): void;\n\t};\n\t/** Repository state hooks — wired by the provider to its repository-changed emitter */\n\treadonly repository?: {\n\t\t/**\n\t\t * Called when repository state has changed.\n\t\t *\n\t\t * `refs`, when given, are the full ref names (`refs/heads/main`, `refs/remotes/origin/main`) the write\n\t\t * created, moved or deleted. A ref kind in `changes` (`heads`, `remotes`, `tags`, `stash`) with at least\n\t\t * one name of its kind in `refs` is fully described by those names: no other ref of that kind moved. A ref\n\t\t * kind in `changes` with no name in `refs` stays unnamed — any ref of that kind may have moved. A host that\n\t\t * ignores `refs` treats every ref kind as unnamed, as before.\n\t\t *\n\t\t * `options.configConfinedToRefs`, given only with `config` among `changes`, says the write changed\n\t\t * `.git/config` only in the `branch.<name>` sections of the local branches `refs` names (`refs/heads/<name>`),\n\t\t * as a branch create, delete, rename or upstream change does. A branch's section matters only to a checkout on\n\t\t * that branch or one whose upstream or base is that branch, so the config change concerns exactly the\n\t\t * checkouts following those branches. Without it, the config change may be anywhere in the file, whatever\n\t\t * `refs` names. A host that ignores it treats every config change as unconfined, as before.\n\t\t */\n\t\tonChanged?(\n\t\t\trepoPath: string,\n\t\t\tchanges: RepositoryChange[],\n\t\t\trefs?: readonly string[],\n\t\t\toptions?: { readonly configConfinedToRefs?: boolean },\n\t\t): void;\n\t};\n\t/** Commit lifecycle hooks */\n\treadonly commits?: {\n\t\t/**\n\t\t * Called when a commit is signed successfully.\n\t\t *\n\t\t * Note: currently fired only from explicit-sign paths — the patch provider's\n\t\t * `commit-tree -S` and the commit operation when the host signing override\n\t\t * (`config.signing.enabled`) adds `-S`. Operations that sign implicitly via\n\t\t * `commit.gpgsign=true` (`commit` without the override, `merge`, `pull`,\n\t\t * `rebase`, `revert`, `cherryPick`) do not fire this hook because the\n\t\t * library cannot cheaply confirm that signing actually occurred without an\n\t\t * extra `git config`/`log --show-signature` call.\n\t\t */\n\t\tonSigned?(format: SigningFormat, source?: unknown): void;\n\t\t/** Called when commit signing fails — fired from all signing-capable paths. */\n\t\tonSigningFailed?(reason: SigningErrorReason, format: SigningFormat, source?: unknown): void;\n\t};\n\t/** Git operation hooks */\n\treadonly operations?: {\n\t\t/**\n\t\t * Called when a git command produced a conflict.\n\t\t * @param conflicts Optional conflicted file list. Populated for merge/rebase/cherry-pick/revert;\n\t\t * may be absent for stash operations or when the file list couldn't be read.\n\t\t */\n\t\tonConflicted?(command: GitConflictCommand, conflicts?: GitConflictFile[]): void;\n\t\t/** Called when getGitDir resolves to a non-existent .git directory or rev-parse fails */\n\t\tonGitDirResolveFailed?(repoPath: string, gitDir: string, errorMessage: string): void;\n\t\t/**\n\t\t * Called when a git operation that can start a rebase begins (`'started'`, before the\n\t\t * process runs) and again when the command exits (`'ended'`, on success and failure alike).\n\t\t * Fired at start (not from the result) because `git rebase` exits 0 when stopping at\n\t\t * `edit`/`break`, and fired for every pull because `pull.rebase=true` config can start\n\t\t * a rebase the host can't predict from the arguments. The `'ended'` phase lets the host\n\t\t * check whether the operation actually left a rebase in progress.\n\t\t * @param repoPath The path the rebase state will appear under (the worktree path for\n\t\t * worktree-targeted pulls)\n\t\t */\n\t\tonRebaseCapableOperation?(repoPath: string, command: 'rebase' | 'pull', phase: 'started' | 'ended'): void;\n\t};\n}\n\n/**\n * Host-provided context attached to remote providers at construction time.\n * Subset of {@link RemotesProvider} — only the fields individual providers need.\n */\nexport interface RemoteProviderContext {\n\t/** Returns repository info (e.g., ID) from the host's integration system */\n\treadonly getRepositoryInfo?: (\n\t\tproviderId: RemoteProviderId,\n\t\ttarget: { owner: string; name: string; project?: string },\n\t) => Promise<{ id: string } | undefined>;\n}\n\n/**\n * File system abstraction following the `workspace.fs` model.\n *\n * Hosts provide an implementation backed by their platform:\n * - Desktop (Node.js): wraps `workspace.fs` or `fs/promises`\n * - Browser: wraps `workspace.fs` for virtual file systems\n *\n * Used by sub-providers that need file system access without\n * importing Node.js `fs` or VS Code APIs directly.\n */\nexport interface FileStat {\n\ttype: number;\n\tctime: number;\n\tmtime: number;\n\tsize: number;\n}\n\n/**\n * File type bitmask matching `vscode.FileType`.\n * Values can be combined (e.g., `FileType.File | FileType.SymbolicLink`).\n */\nexport const FileType = {\n\tUnknown: 0,\n\tFile: 1,\n\tDirectory: 2,\n\tSymbolicLink: 64,\n} as const;\nexport type FileType = (typeof FileType)[keyof typeof FileType];\n\nexport interface FileSystemProvider {\n\treadDirectory(uri: Uri): Promise<[string, number][]>;\n\treadFile(uri: Uri): Promise<Uint8Array>;\n\tstat(uri: Uri): Promise<FileStat | undefined>;\n}\n\n/**\n * Host-side remote capabilities: custom provider configs, sorting, and repository info.\n *\n * The host provides extra provider configs (user custom remotes, cloud self-managed hosts)\n * and the library combines them with built-in providers to build a matcher.\n * Sorting lets the host control remote priority (integration metadata, user preferences).\n * Repository info enables cross-fork PR URLs via integration API lookups.\n */\nexport interface RemotesProvider {\n\t/**\n\t * Returns extra remote provider configurations beyond built-ins.\n\t * Includes user-configured custom remotes (from settings) and\n\t * cloud self-managed host integrations.\n\t */\n\tgetCustomProviders?(repoPath: string): Promise<RemoteProviderConfig[] | undefined>;\n\n\t/**\n\t * Returns repository info from the host's integration system.\n\t * Used by remote providers for cross-fork PR creation URLs.\n\t * Returns `undefined` if no integration is available or connected.\n\t */\n\tgetRepositoryInfo?(\n\t\tproviderId: RemoteProviderId,\n\t\ttarget: { owner: string; name: string; project?: string },\n\t): Promise<{ id: string } | undefined>;\n\n\t/**\n\t * Sorts remotes by priority. The host owns the full ranking policy\n\t * (remote name heuristics, integration metadata, user preferences).\n\t * If not provided, the library returns remotes in their original order.\n\t */\n\tsort?(remotes: GitRemote<RemoteProvider>[], cancellation?: AbortSignal): Promise<GitRemote<RemoteProvider>[]>;\n}\n\n/**\n * Provides search query preprocessing.\n *\n * Hosts implement this to add capabilities like NLP → structured search\n * conversion before the library executes the query.\n */\nexport interface SearchQueryProvider {\n\t/**\n\t * Pre-processes a search query before execution (e.g., converts natural language to structured search).\n\t * Called by commits sub-provider before executing `searchCommits`.\n\t */\n\tpreprocessQuery?(search: SearchQuery, source?: unknown): Promise<SearchQuery>;\n}\n\n/**\n * Provides workspace environment info: folder resolution and trust state.\n * Used by sub-providers for workspace folder lookup and to gate operations\n * in untrusted workspaces.\n *\n * When `isTrusted` is absent, the workspace is assumed trusted.\n */\nexport interface WorkspaceProvider {\n\t/** Fires when the workspace trust state changes (e.g., user grants trust) */\n\treadonly onDidChangeTrust?: Event<boolean>;\n\n\t/** Whether the workspace is currently trusted */\n\treadonly isTrusted?: boolean;\n\n\t/** Resolves the workspace folder that contains the given repoPath */\n\tgetFolder(repoPath: string): { path: string } | undefined;\n\n\t/** Returns the default URI for new worktrees, given a repository path. */\n\tgetWorktreeDefaultUri?(repoPath: string): Uri | undefined;\n}\n"],"mappings":";;;;;AAuPA,MAAa,WAAW;CACvB,SAAS;CACT,MAAM;CACN,WAAW;CACX,cAAc;AACf"}
@@ -29,6 +29,13 @@ import { GitWorktreesSubProvider } from "./worktrees.js";
29
29
  export interface AnnouncedRepositoryChanges {
30
30
  readonly repoPath: string;
31
31
  readonly changes: readonly RepositoryChange[];
32
+ /** Full ref names the write moved, when it knows them — see `GitServiceHooks.repository.onChanged` */
33
+ readonly refs?: readonly string[];
34
+ /**
35
+ * The write's `config` change touched only the `branch.<name>` sections of the local branches in {@link refs} —
36
+ * see `GitServiceHooks.repository.onChanged`
37
+ */
38
+ readonly configConfinedToRefs?: boolean;
32
39
  }
33
40
  /**
34
41
  * Common interface for all git providers (CLI, GitHub, etc.).
@@ -60,9 +60,13 @@ export type ValidateRepoResult = {
60
60
  */
61
61
  export declare class GitService implements UnifiedDisposable {
62
62
  private static _instance;
63
- /** `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch */
63
+ /**
64
+ * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch, and
65
+ * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`)
66
+ */
64
67
  static createSingleton(watchingProvider?: FileWatchingProvider, options?: {
65
68
  watchPolling?: WatchPollingOptions;
69
+ readFile?: (path: string) => Promise<Uint8Array>;
66
70
  }): GitService;
67
71
  private readonly _onDidChangeProviders;
68
72
  get onDidChangeProviders(): Event<ProvidersChangeEvent>;
@@ -33,10 +33,13 @@ import { WorktreeSummaryService } from "./watching/worktreeSummary.js";
33
33
  */
34
34
  var GitService = class GitService {
35
35
  static _instance;
36
- /** `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch */
36
+ /**
37
+ * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch, and
38
+ * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`)
39
+ */
37
40
  static createSingleton(watchingProvider, options) {
38
41
  if (GitService._instance != null) throw new Error("GitService already exists — only one instance is allowed");
39
- const instance = new GitService(watchingProvider, options?.watchPolling);
42
+ const instance = new GitService(watchingProvider, options?.watchPolling, options?.readFile);
40
43
  GitService._instance = instance;
41
44
  return instance;
42
45
  }
@@ -60,11 +63,12 @@ var GitService = class GitService {
60
63
  _serviceCache = /* @__PURE__ */ new Map();
61
64
  _watchService;
62
65
  _summaries;
63
- constructor(watchingProvider, watchPolling) {
66
+ constructor(watchingProvider, watchPolling, readFile) {
64
67
  if (watchingProvider != null) {
65
68
  this._watchService = new RepositoryWatchService({
66
69
  watchingProvider,
67
70
  polling: watchPolling,
71
+ readFile,
68
72
  getIgnoreFilter: (repoPath, gitDirPath) => {
69
73
  return this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);
70
74
  },
@@ -120,7 +124,7 @@ var GitService = class GitService {
120
124
  const entry = {
121
125
  provider,
122
126
  canHandle,
123
- announcements: watchService != null ? provider.onDidAnnounceChanges?.((e) => watchService.noteSelfWrite(e.repoPath, e.changes)) : void 0
127
+ announcements: watchService != null ? provider.onDidAnnounceChanges?.((e) => watchService.noteSelfWrite(e.repoPath, e.changes, e.refs, { configConfinedToRefs: e.configConfinedToRefs })) : void 0
124
128
  };
125
129
  this._providers.push(entry);
126
130
  this._etag++;
@@ -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 { 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 type { WatchPollingOptions } from './watching/watchService.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\t/** Forwards the provider's write announcements to {@link GitService.watchService}, when there is one */\n\tannouncements?: UnifiedDisposable;\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\t/** `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch */\n\tstatic createSingleton(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\toptions?: { watchPolling?: WatchPollingOptions },\n\t): 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, options?.watchPolling);\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 a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that\n\t * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming\n\t * the branch. Once per change per repository (`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, watchPolling?: WatchPollingOptions) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tpolling: watchPolling,\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\tonDidChangeMergeTargets: this.onDidChangeMergeTargets,\n\t\t\t\tgetCommonPath: repoPath => this.getProvider(repoPath)?.provider.getCommonPath?.(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\tfor (const entry of this._providers) {\n\t\t\tentry.announcements?.dispose();\n\t\t}\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 watchService = this._watchService;\n\t\tconst entry: RegisteredProvider = {\n\t\t\tprovider: provider,\n\t\t\tcanHandle: canHandle,\n\t\t\tannouncements:\n\t\t\t\twatchService != null\n\t\t\t\t\t? provider.onDidAnnounceChanges?.(e => watchService.noteSelfWrite(e.repoPath, e.changes))\n\t\t\t\t\t: undefined,\n\t\t};\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\tentry.announcements?.dispose();\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
+ {"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 type { WatchPollingOptions } from './watching/watchService.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\t/** Forwards the provider's write announcements to {@link GitService.watchService}, when there is one */\n\tannouncements?: UnifiedDisposable;\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\t/**\n\t * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch, and\n\t * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`)\n\t */\n\tstatic createSingleton(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\toptions?: { watchPolling?: WatchPollingOptions; readFile?: (path: string) => Promise<Uint8Array> },\n\t): 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, options?.watchPolling, options?.readFile);\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 a write changed a branch's merge-relevant `.git/gk/config` keys, for any registered provider that\n\t * reports it — never for activity timestamps. Core's own key write fires it once, as the write completes, naming\n\t * the branch. Once per change per repository (`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(\n\t\twatchingProvider?: FileWatchingProvider,\n\t\twatchPolling?: WatchPollingOptions,\n\t\treadFile?: (path: string) => Promise<Uint8Array>,\n\t) {\n\t\tif (watchingProvider != null) {\n\t\t\tthis._watchService = new RepositoryWatchService({\n\t\t\t\twatchingProvider: watchingProvider,\n\t\t\t\tpolling: watchPolling,\n\t\t\t\treadFile: readFile,\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\tonDidChangeMergeTargets: this.onDidChangeMergeTargets,\n\t\t\t\tgetCommonPath: repoPath => this.getProvider(repoPath)?.provider.getCommonPath?.(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\tfor (const entry of this._providers) {\n\t\t\tentry.announcements?.dispose();\n\t\t}\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 watchService = this._watchService;\n\t\tconst entry: RegisteredProvider = {\n\t\t\tprovider: provider,\n\t\t\tcanHandle: canHandle,\n\t\t\tannouncements:\n\t\t\t\twatchService != null\n\t\t\t\t\t? provider.onDidAnnounceChanges?.(e =>\n\t\t\t\t\t\t\twatchService.noteSelfWrite(e.repoPath, e.changes, e.refs, {\n\t\t\t\t\t\t\t\tconfigConfinedToRefs: e.configConfinedToRefs,\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t)\n\t\t\t\t\t: undefined,\n\t\t};\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\tentry.announcements?.dispose();\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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"}
@@ -7,6 +7,11 @@ export interface WatcherRefChanges {
7
7
  readonly names?: Iterable<string>;
8
8
  /** Ref kinds that changed without a name (packed-refs rewrite, a ref namespace directory like `refs/remotes`, shallow/replace, an injected change): any ref of that kind may have moved. */
9
9
  readonly unnamed?: Iterable<RefChangeKind>;
10
+ /**
11
+ * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are all that every `config` change
12
+ * among the changes touched in `.git/config`. Omitted when any of them may have changed anything else.
13
+ */
14
+ readonly configBranches?: Iterable<string>;
10
15
  }
11
16
  export declare function isRefChangeKind(change: RepositoryChange): change is RefChangeKind;
12
17
  /**
@@ -30,6 +35,7 @@ export declare class WatcherRepoChangeEvent {
30
35
  private readonly _refs;
31
36
  private readonly _unnamedRefKinds;
32
37
  private readonly _announced;
38
+ private readonly _configBranches;
33
39
  constructor(
34
40
  /** The repository path this event is for */
35
41
  repoPath: string, changes: RepositoryChange[],
@@ -50,6 +56,23 @@ export declare class WatcherRepoChangeEvent {
50
56
  get refs(): ReadonlySet<string>;
51
57
  /** Ref kinds that changed without a name — any ref of these kinds may have moved */
52
58
  get unnamedRefKinds(): ReadonlySet<RefChangeKind>;
59
+ /**
60
+ * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are the only part of `.git/config`
61
+ * that changed; `undefined` when `config` didn't change, or any config change may be elsewhere. Independent of
62
+ * {@link refs}: an outside edit to a branch's tracking moves no ref.
63
+ */
64
+ get configBranches(): ReadonlySet<string> | undefined;
65
+ /** Whether `config` changed only in the sections of {@link configBranches} */
66
+ get configConfined(): boolean;
67
+ /**
68
+ * Test whether `config` may have changed in a way that concerns a subscriber following `relevantRefs`.
69
+ *
70
+ * A branch's config section matters only to a checkout on that branch or one whose upstream or base is that branch,
71
+ * and those follow `refs/heads/<name>` (or `HEAD`, which may be on any branch). So a {@link configConfined} change
72
+ * concerns `relevantRefs` only when one of them is `HEAD` or one of {@link configBranches}; any other config change
73
+ * concerns everyone.
74
+ */
75
+ mayHaveChangedConfig(relevantRefs: Iterable<string>): boolean;
53
76
  toString(changesOnly?: boolean): string;
54
77
  /**
55
78
  * Test whether the given ref may have moved in this event.
@@ -79,7 +102,8 @@ export declare class WatcherRepoChangeEvent {
79
102
  changedExclusive(...affected: RepositoryChange[]): boolean;
80
103
  /**
81
104
  * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,
82
- * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}.
105
+ * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}. `config` stays
106
+ * {@link configConfined}, to the branches of both, only when each side that changed it was confined.
83
107
  */
84
108
  with(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent;
85
109
  }
@@ -29,6 +29,7 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
29
29
  _refs;
30
30
  _unnamedRefKinds;
31
31
  _announced;
32
+ _configBranches;
32
33
  constructor(repoPath, changes, refs, announced) {
33
34
  this.repoPath = repoPath;
34
35
  this._changes = new Set(changes);
@@ -39,6 +40,7 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
39
40
  this._refs = new Set(refs.names);
40
41
  this._unnamedRefKinds = new Set(refs.unnamed);
41
42
  }
43
+ this._configBranches = refs?.configBranches != null && this._changes.has("config") ? new Set(refs.configBranches) : void 0;
42
44
  this._announced = announced != null ? new Set(filter(announced, (c) => this._changes.has(c))) : /* @__PURE__ */ new Set();
43
45
  }
44
46
  get changes() {
@@ -64,6 +66,32 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
64
66
  get unnamedRefKinds() {
65
67
  return this._unnamedRefKinds;
66
68
  }
69
+ /**
70
+ * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are the only part of `.git/config`
71
+ * that changed; `undefined` when `config` didn't change, or any config change may be elsewhere. Independent of
72
+ * {@link refs}: an outside edit to a branch's tracking moves no ref.
73
+ */
74
+ get configBranches() {
75
+ return this._configBranches;
76
+ }
77
+ /** Whether `config` changed only in the sections of {@link configBranches} */
78
+ get configConfined() {
79
+ return this._configBranches != null;
80
+ }
81
+ /**
82
+ * Test whether `config` may have changed in a way that concerns a subscriber following `relevantRefs`.
83
+ *
84
+ * A branch's config section matters only to a checkout on that branch or one whose upstream or base is that branch,
85
+ * and those follow `refs/heads/<name>` (or `HEAD`, which may be on any branch). So a {@link configConfined} change
86
+ * concerns `relevantRefs` only when one of them is `HEAD` or one of {@link configBranches}; any other config change
87
+ * concerns everyone.
88
+ */
89
+ mayHaveChangedConfig(relevantRefs) {
90
+ if (!this._changes.has("config")) return false;
91
+ if (this._configBranches == null) return true;
92
+ for (const ref of relevantRefs) if (ref === "HEAD" || this._configBranches.has(ref)) return true;
93
+ return false;
94
+ }
67
95
  toString(changesOnly = false) {
68
96
  const changeList = [...this._changes].join(", ");
69
97
  const refCount = this._refs.size;
@@ -117,7 +145,8 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
117
145
  }
118
146
  /**
119
147
  * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,
120
- * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}.
148
+ * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}. `config` stays
149
+ * {@link configConfined}, to the branches of both, only when each side that changed it was confined.
121
150
  */
122
151
  with(changes, refs) {
123
152
  const names = [...this._refs];
@@ -127,9 +156,14 @@ var WatcherRepoChangeEvent = class WatcherRepoChangeEvent {
127
156
  if (refs.names != null) names.push(...refs.names);
128
157
  if (refs.unnamed != null) unnamed.push(...refs.unnamed);
129
158
  }
159
+ let configBranches;
160
+ const own = this._changes.has("config") ? this._configBranches : /* @__PURE__ */ new Set();
161
+ const other = changes.includes("config") ? refs?.configBranches : [];
162
+ if (own != null && other != null) configBranches = /* @__PURE__ */ new Set([...own, ...other]);
130
163
  return new WatcherRepoChangeEvent(this.repoPath, [...this._changes, ...changes], {
131
164
  names,
132
- unnamed
165
+ unnamed,
166
+ configBranches
133
167
  }, filter(this._announced, (c) => !changes.includes(c)));
134
168
  }
135
169
  };
@@ -1 +1 @@
1
- {"version":3,"file":"changeEvent.js","names":[],"sources":["../../../../git/src/watching/changeEvent.ts"],"sourcesContent":["import { filter, some } from '@gitlens/utils/iterable.js';\nimport type { RepositoryChange } from '../models/repository.js';\n\n/** The {@link RepositoryChange} kinds that describe ref movement, which an event can attribute to named refs */\nexport type RefChangeKind = 'heads' | 'remotes' | 'tags' | 'stash';\n\nexport interface WatcherRefChanges {\n\t/** 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. */\n\treadonly names?: Iterable<string>;\n\t/** Ref kinds that changed without a name (packed-refs rewrite, a ref namespace directory like `refs/remotes`, shallow/replace, an injected change): any ref of that kind may have moved. */\n\treadonly unnamed?: Iterable<RefChangeKind>;\n}\n\nexport function isRefChangeKind(change: RepositoryChange): change is RefChangeKind {\n\treturn change === 'heads' || change === 'remotes' || change === 'tags' || change === 'stash';\n}\n\n/**\n * The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,\n * `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind, and neither\n * has a namespace directory itself (`refs/remotes`), so a change reported for one names no ref: any ref of the kind it\n * holds may have moved.\n */\nexport function getRefChangeKind(ref: string): RefChangeKind | undefined {\n\tif (!ref.startsWith('refs/')) return 'heads';\n\tif (ref.startsWith('refs/heads/')) return 'heads';\n\tif (ref.startsWith('refs/remotes/')) return 'remotes';\n\tif (ref.startsWith('refs/tags/')) return 'tags';\n\tif (ref === 'refs/stash') return 'stash';\n\n\treturn undefined;\n}\n\n/**\n * An event describing changes to a repository, produced by the watching pipeline.\n *\n * Changes are coalesced within a debounce window — a single event may represent\n * multiple filesystem changes. Use {@link changed} to test for specific change types,\n * and {@link mayHaveChangedRef} to test whether a specific ref may have moved.\n */\nexport class WatcherRepoChangeEvent {\n\tprotected readonly _changes: Set<RepositoryChange>;\n\tprivate readonly _refs: Set<string>;\n\tprivate readonly _unnamedRefKinds: Set<RefChangeKind>;\n\tprivate readonly _announced: Set<RepositoryChange>;\n\n\tconstructor(\n\t\t/** The repository path this event is for */\n\t\tpublic readonly repoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\t/** Which refs changed; when omitted, every ref kind in `changes` is unnamed (any ref of that kind may have moved) */\n\t\trefs?: WatcherRefChanges,\n\t\t/** The kinds of `changes` that come only from core's own announced writes (see {@link announced}) */\n\t\tannounced?: Iterable<RepositoryChange>,\n\t) {\n\t\tthis._changes = new Set(changes);\n\t\tif (refs == null) {\n\t\t\tthis._refs = new Set();\n\t\t\tthis._unnamedRefKinds = new Set(filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tthis._refs = new Set(refs.names);\n\t\t\tthis._unnamedRefKinds = new Set(refs.unnamed);\n\t\t}\n\t\tthis._announced = announced != null ? new Set(filter(announced, c => this._changes.has(c))) : new Set();\n\t}\n\n\tget changes(): ReadonlySet<RepositoryChange> {\n\t\treturn this._changes;\n\t}\n\n\t/**\n\t * The kinds in {@link changes} that come only from core's own writes, which core already announced through\n\t * `hooks.repository.onChanged` after evicting their caches. A host that handled that announcement can skip them;\n\t * a kind the watcher or an injected change also reported is not among them.\n\t */\n\tget announced(): ReadonlySet<RepositoryChange> {\n\t\treturn this._announced;\n\t}\n\n\t/** The kinds in {@link changes} that aren't {@link announced}: what a cache that core already evicted for its write still has to clear */\n\tgetUnannouncedChanges(): RepositoryChange[] {\n\t\treturn this._announced.size ? [...filter(this._changes, c => !this._announced.has(c))] : [...this._changes];\n\t}\n\n\t/** Full ref names (or ref directories, or pseudo-refs) known to have changed. See {@link WatcherRefChanges.names}. */\n\tget refs(): ReadonlySet<string> {\n\t\treturn this._refs;\n\t}\n\n\t/** Ref kinds that changed without a name — any ref of these kinds may have moved */\n\tget unnamedRefKinds(): ReadonlySet<RefChangeKind> {\n\t\treturn this._unnamedRefKinds;\n\t}\n\n\ttoString(changesOnly: boolean = false): string {\n\t\tconst changeList = [...this._changes].join(', ');\n\t\tconst refCount = this._refs.size;\n\t\tif (changesOnly) return refCount ? `changes=${changeList}, refs=${refCount}` : `changes=${changeList}`;\n\n\t\treturn refCount\n\t\t\t? `{ repoPath: ${this.repoPath}, changes: ${changeList}, refs: ${refCount} }`\n\t\t\t: `{ repoPath: ${this.repoPath}, changes: ${changeList} }`;\n\t}\n\n\t/**\n\t * Test whether the given ref may have moved in this event.\n\t *\n\t * @param ref - A full ref name (`refs/heads/main`) or a pseudo-ref (`HEAD`).\n\t * @returns `false` when the ref's kind didn't change (or the ref is outside the watched kinds); `true` when its kind\n\t * changed without a name, or when the ref itself or a ref directory above it changed. `HEAD` is usually a symref\n\t * to the checked-out branch, so it may have moved whenever any `heads` ref did.\n\t */\n\tmayHaveChangedRef(ref: string): boolean {\n\t\tconst kind = getRefChangeKind(ref);\n\t\tif (kind == null || !this._changes.has(kind)) return false;\n\t\tif (this._unnamedRefKinds.has(kind) || ref === 'HEAD') return true;\n\n\t\treturn findCoveringRefName(this._refs, ref) != null;\n\t}\n\n\t/**\n\t * Test whether any of the specified change types are present in the event.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if any of the specified changes are present.\n\t */\n\tchanged(...affected: RepositoryChange[]): boolean {\n\t\treturn some(this._changes, c => affected.includes(c));\n\t}\n\n\t/**\n\t * Test whether the event contains ONLY the specified change types.\n\t *\n\t * `pausedOp` is treated as a union of `cherryPick`, `merge`, `rebase`,\n\t * and `revert` for convenience.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if the event contains only the specified changes.\n\t */\n\tchangedExclusive(...affected: RepositoryChange[]): boolean {\n\t\tlet changes = this._changes;\n\n\t\t// When checking for specific paused operation types, also accept the union type\n\t\tif (\n\t\t\taffected.includes('cherryPick') ||\n\t\t\taffected.includes('merge') ||\n\t\t\taffected.includes('rebase') ||\n\t\t\taffected.includes('revert')\n\t\t) {\n\t\t\tif (!affected.includes('pausedOp')) {\n\t\t\t\taffected = [...affected, 'pausedOp'];\n\t\t\t}\n\t\t} else if (affected.includes('pausedOp')) {\n\t\t\t// When checking for the union type exclusively, ignore specific subtypes\n\t\t\tchanges = new Set(changes);\n\t\t\tchanges.delete('cherryPick');\n\t\t\tchanges.delete('merge');\n\t\t\tchanges.delete('rebase');\n\t\t\tchanges.delete('revert');\n\t\t}\n\n\t\tconst intersection = [...filter(changes, c => affected.includes(c))];\n\t\treturn intersection.length === changes.size;\n\t}\n\n\t/**\n\t * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,\n\t * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}.\n\t */\n\twith(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent {\n\t\tconst names = [...this._refs];\n\t\tconst unnamed = [...this._unnamedRefKinds];\n\t\tif (refs == null) {\n\t\t\tunnamed.push(...filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tif (refs.names != null) {\n\t\t\t\tnames.push(...refs.names);\n\t\t\t}\n\t\t\tif (refs.unnamed != null) {\n\t\t\t\tunnamed.push(...refs.unnamed);\n\t\t\t}\n\t\t}\n\n\t\treturn new WatcherRepoChangeEvent(\n\t\t\tthis.repoPath,\n\t\t\t[...this._changes, ...changes],\n\t\t\t{ names: names, unnamed: unnamed },\n\t\t\tfilter(this._announced, c => !changes.includes(c)),\n\t\t);\n\t}\n}\n\n/**\n * Returns the name in `names` that covers `ref` — `ref` itself, or a ref directory above it (compared on `/`\n * boundaries, so `refs/heads/fe` doesn't cover `refs/heads/feature`) — or `undefined` if none does.\n */\nexport function findCoveringRefName(names: ReadonlySet<string>, ref: string): string | undefined {\n\tif (names.size === 0) return undefined;\n\tif (names.has(ref)) return ref;\n\n\tlet index = ref.lastIndexOf('/');\n\twhile (index > 0) {\n\t\tconst dir = ref.substring(0, index);\n\t\tif (names.has(dir)) return dir;\n\n\t\tindex = ref.lastIndexOf('/', index - 1);\n\t}\n\treturn undefined;\n}\n\n/**\n * An event describing working tree file changes, produced by the working tree pipeline.\n *\n * Paths have already been filtered through the gitignore filter and noise filter.\n */\nexport interface WorkingTreeChangeEvent {\n\treadonly repoPath: string;\n\t/**\n\t * Absolute paths of changed files or directories (post-gitignore filtering). A directory stands for everything\n\t * beneath it. Partial when {@link incomplete}.\n\t */\n\treadonly paths: ReadonlySet<string>;\n\t/**\n\t * `true` when more paths changed than the accumulator retains, so {@link paths} is a partial set and a\n\t * miss does NOT mean the file is unchanged. Consumers testing membership must treat this as a match.\n\t */\n\treadonly incomplete: boolean;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"changeEvent.js","names":[],"sources":["../../../../git/src/watching/changeEvent.ts"],"sourcesContent":["import { filter, some } from '@gitlens/utils/iterable.js';\nimport type { RepositoryChange } from '../models/repository.js';\n\n/** The {@link RepositoryChange} kinds that describe ref movement, which an event can attribute to named refs */\nexport type RefChangeKind = 'heads' | 'remotes' | 'tags' | 'stash';\n\nexport interface WatcherRefChanges {\n\t/** 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. */\n\treadonly names?: Iterable<string>;\n\t/** Ref kinds that changed without a name (packed-refs rewrite, a ref namespace directory like `refs/remotes`, shallow/replace, an injected change): any ref of that kind may have moved. */\n\treadonly unnamed?: Iterable<RefChangeKind>;\n\t/**\n\t * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are all that every `config` change\n\t * among the changes touched in `.git/config`. Omitted when any of them may have changed anything else.\n\t */\n\treadonly configBranches?: Iterable<string>;\n}\n\nexport function isRefChangeKind(change: RepositoryChange): change is RefChangeKind {\n\treturn change === 'heads' || change === 'remotes' || change === 'tags' || change === 'stash';\n}\n\n/**\n * The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,\n * `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind, and neither\n * has a namespace directory itself (`refs/remotes`), so a change reported for one names no ref: any ref of the kind it\n * holds may have moved.\n */\nexport function getRefChangeKind(ref: string): RefChangeKind | undefined {\n\tif (!ref.startsWith('refs/')) return 'heads';\n\tif (ref.startsWith('refs/heads/')) return 'heads';\n\tif (ref.startsWith('refs/remotes/')) return 'remotes';\n\tif (ref.startsWith('refs/tags/')) return 'tags';\n\tif (ref === 'refs/stash') return 'stash';\n\n\treturn undefined;\n}\n\n/**\n * An event describing changes to a repository, produced by the watching pipeline.\n *\n * Changes are coalesced within a debounce window — a single event may represent\n * multiple filesystem changes. Use {@link changed} to test for specific change types,\n * and {@link mayHaveChangedRef} to test whether a specific ref may have moved.\n */\nexport class WatcherRepoChangeEvent {\n\tprotected readonly _changes: Set<RepositoryChange>;\n\tprivate readonly _refs: Set<string>;\n\tprivate readonly _unnamedRefKinds: Set<RefChangeKind>;\n\tprivate readonly _announced: Set<RepositoryChange>;\n\tprivate readonly _configBranches: Set<string> | undefined;\n\n\tconstructor(\n\t\t/** The repository path this event is for */\n\t\tpublic readonly repoPath: string,\n\t\tchanges: RepositoryChange[],\n\t\t/** Which refs changed; when omitted, every ref kind in `changes` is unnamed (any ref of that kind may have moved) */\n\t\trefs?: WatcherRefChanges,\n\t\t/** The kinds of `changes` that come only from core's own announced writes (see {@link announced}) */\n\t\tannounced?: Iterable<RepositoryChange>,\n\t) {\n\t\tthis._changes = new Set(changes);\n\t\tif (refs == null) {\n\t\t\tthis._refs = new Set();\n\t\t\tthis._unnamedRefKinds = new Set(filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tthis._refs = new Set(refs.names);\n\t\t\tthis._unnamedRefKinds = new Set(refs.unnamed);\n\t\t}\n\t\tthis._configBranches =\n\t\t\trefs?.configBranches != null && this._changes.has('config') ? new Set(refs.configBranches) : undefined;\n\t\tthis._announced = announced != null ? new Set(filter(announced, c => this._changes.has(c))) : new Set();\n\t}\n\n\tget changes(): ReadonlySet<RepositoryChange> {\n\t\treturn this._changes;\n\t}\n\n\t/**\n\t * The kinds in {@link changes} that come only from core's own writes, which core already announced through\n\t * `hooks.repository.onChanged` after evicting their caches. A host that handled that announcement can skip them;\n\t * a kind the watcher or an injected change also reported is not among them.\n\t */\n\tget announced(): ReadonlySet<RepositoryChange> {\n\t\treturn this._announced;\n\t}\n\n\t/** The kinds in {@link changes} that aren't {@link announced}: what a cache that core already evicted for its write still has to clear */\n\tgetUnannouncedChanges(): RepositoryChange[] {\n\t\treturn this._announced.size ? [...filter(this._changes, c => !this._announced.has(c))] : [...this._changes];\n\t}\n\n\t/** Full ref names (or ref directories, or pseudo-refs) known to have changed. See {@link WatcherRefChanges.names}. */\n\tget refs(): ReadonlySet<string> {\n\t\treturn this._refs;\n\t}\n\n\t/** Ref kinds that changed without a name — any ref of these kinds may have moved */\n\tget unnamedRefKinds(): ReadonlySet<RefChangeKind> {\n\t\treturn this._unnamedRefKinds;\n\t}\n\n\t/**\n\t * The local branches (full `refs/heads/` names) whose `branch.<name>` sections are the only part of `.git/config`\n\t * that changed; `undefined` when `config` didn't change, or any config change may be elsewhere. Independent of\n\t * {@link refs}: an outside edit to a branch's tracking moves no ref.\n\t */\n\tget configBranches(): ReadonlySet<string> | undefined {\n\t\treturn this._configBranches;\n\t}\n\n\t/** Whether `config` changed only in the sections of {@link configBranches} */\n\tget configConfined(): boolean {\n\t\treturn this._configBranches != null;\n\t}\n\n\t/**\n\t * Test whether `config` may have changed in a way that concerns a subscriber following `relevantRefs`.\n\t *\n\t * A branch's config section matters only to a checkout on that branch or one whose upstream or base is that branch,\n\t * and those follow `refs/heads/<name>` (or `HEAD`, which may be on any branch). So a {@link configConfined} change\n\t * concerns `relevantRefs` only when one of them is `HEAD` or one of {@link configBranches}; any other config change\n\t * concerns everyone.\n\t */\n\tmayHaveChangedConfig(relevantRefs: Iterable<string>): boolean {\n\t\tif (!this._changes.has('config')) return false;\n\t\tif (this._configBranches == null) return true;\n\n\t\tfor (const ref of relevantRefs) {\n\t\t\tif (ref === 'HEAD' || this._configBranches.has(ref)) return true;\n\t\t}\n\t\treturn false;\n\t}\n\n\ttoString(changesOnly: boolean = false): string {\n\t\tconst changeList = [...this._changes].join(', ');\n\t\tconst refCount = this._refs.size;\n\t\tif (changesOnly) return refCount ? `changes=${changeList}, refs=${refCount}` : `changes=${changeList}`;\n\n\t\treturn refCount\n\t\t\t? `{ repoPath: ${this.repoPath}, changes: ${changeList}, refs: ${refCount} }`\n\t\t\t: `{ repoPath: ${this.repoPath}, changes: ${changeList} }`;\n\t}\n\n\t/**\n\t * Test whether the given ref may have moved in this event.\n\t *\n\t * @param ref - A full ref name (`refs/heads/main`) or a pseudo-ref (`HEAD`).\n\t * @returns `false` when the ref's kind didn't change (or the ref is outside the watched kinds); `true` when its kind\n\t * changed without a name, or when the ref itself or a ref directory above it changed. `HEAD` is usually a symref\n\t * to the checked-out branch, so it may have moved whenever any `heads` ref did.\n\t */\n\tmayHaveChangedRef(ref: string): boolean {\n\t\tconst kind = getRefChangeKind(ref);\n\t\tif (kind == null || !this._changes.has(kind)) return false;\n\t\tif (this._unnamedRefKinds.has(kind) || ref === 'HEAD') return true;\n\n\t\treturn findCoveringRefName(this._refs, ref) != null;\n\t}\n\n\t/**\n\t * Test whether any of the specified change types are present in the event.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if any of the specified changes are present.\n\t */\n\tchanged(...affected: RepositoryChange[]): boolean {\n\t\treturn some(this._changes, c => affected.includes(c));\n\t}\n\n\t/**\n\t * Test whether the event contains ONLY the specified change types.\n\t *\n\t * `pausedOp` is treated as a union of `cherryPick`, `merge`, `rebase`,\n\t * and `revert` for convenience.\n\t *\n\t * @param affected - Change types to test for.\n\t * @returns `true` if the event contains only the specified changes.\n\t */\n\tchangedExclusive(...affected: RepositoryChange[]): boolean {\n\t\tlet changes = this._changes;\n\n\t\t// When checking for specific paused operation types, also accept the union type\n\t\tif (\n\t\t\taffected.includes('cherryPick') ||\n\t\t\taffected.includes('merge') ||\n\t\t\taffected.includes('rebase') ||\n\t\t\taffected.includes('revert')\n\t\t) {\n\t\t\tif (!affected.includes('pausedOp')) {\n\t\t\t\taffected = [...affected, 'pausedOp'];\n\t\t\t}\n\t\t} else if (affected.includes('pausedOp')) {\n\t\t\t// When checking for the union type exclusively, ignore specific subtypes\n\t\t\tchanges = new Set(changes);\n\t\t\tchanges.delete('cherryPick');\n\t\t\tchanges.delete('merge');\n\t\t\tchanges.delete('rebase');\n\t\t\tchanges.delete('revert');\n\t\t}\n\n\t\tconst intersection = [...filter(changes, c => affected.includes(c))];\n\t\treturn intersection.length === changes.size;\n\t}\n\n\t/**\n\t * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,\n\t * every ref kind in `changes` counts as unnamed. A kind in `changes` is no longer {@link announced}. `config` stays\n\t * {@link configConfined}, to the branches of both, only when each side that changed it was confined.\n\t */\n\twith(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent {\n\t\tconst names = [...this._refs];\n\t\tconst unnamed = [...this._unnamedRefKinds];\n\t\tif (refs == null) {\n\t\t\tunnamed.push(...filter(changes, isRefChangeKind));\n\t\t} else {\n\t\t\tif (refs.names != null) {\n\t\t\t\tnames.push(...refs.names);\n\t\t\t}\n\t\t\tif (refs.unnamed != null) {\n\t\t\t\tunnamed.push(...refs.unnamed);\n\t\t\t}\n\t\t}\n\n\t\tlet configBranches: Set<string> | undefined;\n\t\tconst own = this._changes.has('config') ? this._configBranches : new Set<string>();\n\t\tconst other = changes.includes('config') ? refs?.configBranches : [];\n\t\tif (own != null && other != null) {\n\t\t\tconfigBranches = new Set([...own, ...other]);\n\t\t}\n\n\t\treturn new WatcherRepoChangeEvent(\n\t\t\tthis.repoPath,\n\t\t\t[...this._changes, ...changes],\n\t\t\t{ names: names, unnamed: unnamed, configBranches: configBranches },\n\t\t\tfilter(this._announced, c => !changes.includes(c)),\n\t\t);\n\t}\n}\n\n/**\n * Returns the name in `names` that covers `ref` — `ref` itself, or a ref directory above it (compared on `/`\n * boundaries, so `refs/heads/fe` doesn't cover `refs/heads/feature`) — or `undefined` if none does.\n */\nexport function findCoveringRefName(names: ReadonlySet<string>, ref: string): string | undefined {\n\tif (names.size === 0) return undefined;\n\tif (names.has(ref)) return ref;\n\n\tlet index = ref.lastIndexOf('/');\n\twhile (index > 0) {\n\t\tconst dir = ref.substring(0, index);\n\t\tif (names.has(dir)) return dir;\n\n\t\tindex = ref.lastIndexOf('/', index - 1);\n\t}\n\treturn undefined;\n}\n\n/**\n * An event describing working tree file changes, produced by the working tree pipeline.\n *\n * Paths have already been filtered through the gitignore filter and noise filter.\n */\nexport interface WorkingTreeChangeEvent {\n\treadonly repoPath: string;\n\t/**\n\t * Absolute paths of changed files or directories (post-gitignore filtering). A directory stands for everything\n\t * beneath it. Partial when {@link incomplete}.\n\t */\n\treadonly paths: ReadonlySet<string>;\n\t/**\n\t * `true` when more paths changed than the accumulator retains, so {@link paths} is a partial set and a\n\t * miss does NOT mean the file is unchanged. Consumers testing membership must treat this as a match.\n\t */\n\treadonly incomplete: boolean;\n}\n"],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ //#region ../git/src/watching/configSections.d.ts
2
+ /**
3
+ * A git config file's variables by section, as git reads them: each key is a section's full name (`core`,
4
+ * `branch.feature/x`: the section name lowercased, then its subsection), and each value lists that section's variables
5
+ * in file order as `<key>=<value>` (the key lowercased, the value unquoted and unescaped), or a bare `<key>` for one
6
+ * with no value. A section whose header repeats lists the variables of every occurrence.
7
+ */
8
+ export type GitConfigSections = ReadonlyMap<string, readonly string[]>;
9
+ /**
10
+ * What changed between two reads of the same config file: every local branch (full `refs/heads/` name) whose
11
+ * `branch.<name>` section changed, and whether that is all that changed. `undefined` when nothing changed.
12
+ */
13
+ export interface GitConfigSectionsChange {
14
+ readonly branches: ReadonlySet<string>;
15
+ readonly confined: boolean;
16
+ }
17
+ /** Parses a config file's bytes; `undefined` when they aren't UTF-8 or git would reject them */
18
+ export declare function parseGitConfigBytes(data: Uint8Array): GitConfigSections | undefined;
19
+ /**
20
+ * Parses a config file's text the way git does (`config.c`): a header is `[section]`, `[section "subsection"]` (the
21
+ * subsection kept as written, `\` escaping the next character) or the legacy `[section.subsection]` (lowercased); a
22
+ * variable may follow a header on its line; `#` and `;` start a comment outside quotes; a value may be quoted, and `\`
23
+ * escapes `"`, `\`, `n`, `t` and `b`, or continues the value on the next line. `undefined` for anything git rejects
24
+ * (a malformed header or name, an unknown escape, a line ending inside quotes), and for a variable before any header,
25
+ * which has no section to compare.
26
+ */
27
+ export declare function parseGitConfigSections(text: string): GitConfigSections | undefined;
28
+ /**
29
+ * Compares two reads of a config file section by section. A changed `branch.<name>` section (a subsection of
30
+ * `branch`) names that local branch; any other changed section, the bare `[branch]` included, makes the change
31
+ * unconfined.
32
+ */
33
+ export declare function diffGitConfigSections(before: GitConfigSections, after: GitConfigSections): GitConfigSectionsChange | undefined;
34
+ //#endregion
35
+ //# sourceMappingURL=configSections.d.ts.map