@gitkraken/core-gitlens 0.15.3 → 0.15.5

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 (91) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/dist/git/cache.d.ts +28 -18
  3. package/dist/git/cache.js +71 -25
  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/models/pullRequest.d.ts +7 -5
  8. package/dist/git/models/pullRequest.js.map +1 -1
  9. package/dist/git/models/reference.d.ts +8 -0
  10. package/dist/git/providers/branches.d.ts +43 -0
  11. package/dist/git/providers/provider.d.ts +7 -0
  12. package/dist/git/service.d.ts +5 -1
  13. package/dist/git/service.js +8 -4
  14. package/dist/git/service.js.map +1 -1
  15. package/dist/git/watching/changeEvent.d.ts +29 -3
  16. package/dist/git/watching/changeEvent.js +39 -3
  17. package/dist/git/watching/changeEvent.js.map +1 -1
  18. package/dist/git/watching/configSections.d.ts +35 -0
  19. package/dist/git/watching/configSections.js +210 -0
  20. package/dist/git/watching/configSections.js.map +1 -0
  21. package/dist/git/watching/watchGroup.d.ts +37 -4
  22. package/dist/git/watching/watchGroup.js +108 -2
  23. package/dist/git/watching/watchGroup.js.map +1 -1
  24. package/dist/git/watching/watchService.d.ts +14 -3
  25. package/dist/git/watching/watchService.js +10 -7
  26. package/dist/git/watching/watchService.js.map +1 -1
  27. package/dist/git/watching/watchSession.d.ts +52 -20
  28. package/dist/git/watching/watchSession.js +166 -43
  29. package/dist/git/watching/watchSession.js.map +1 -1
  30. package/dist/git/watching/watcherPatterns.js +1 -0
  31. package/dist/git/watching/watcherPatterns.js.map +1 -1
  32. package/dist/git/watching/worktreeSummary.d.ts +21 -3
  33. package/dist/git/watching/worktreeSummary.js +118 -17
  34. package/dist/git/watching/worktreeSummary.js.map +1 -1
  35. package/dist/git-cli/cliGitProvider.d.ts +6 -1
  36. package/dist/git-cli/cliGitProvider.js +45 -5
  37. package/dist/git-cli/cliGitProvider.js.map +1 -1
  38. package/dist/git-cli/parsers/refParser.js +2 -1
  39. package/dist/git-cli/parsers/refParser.js.map +1 -1
  40. package/dist/git-cli/providers/branches.d.ts +10 -3
  41. package/dist/git-cli/providers/branches.js +99 -25
  42. package/dist/git-cli/providers/branches.js.map +1 -1
  43. package/dist/git-cli/providers/config.d.ts +45 -9
  44. package/dist/git-cli/providers/config.js +150 -24
  45. package/dist/git-cli/providers/config.js.map +1 -1
  46. package/dist/git-cli/providers/operations.d.ts +35 -1
  47. package/dist/git-cli/providers/operations.js +175 -16
  48. package/dist/git-cli/providers/operations.js.map +1 -1
  49. package/dist/git-cli/providers/stash.d.ts +1 -0
  50. package/dist/git-cli/providers/stash.js +9 -3
  51. package/dist/git-cli/providers/stash.js.map +1 -1
  52. package/dist/git-cli/providers/worktrees.js +11 -4
  53. package/dist/git-cli/providers/worktrees.js.map +1 -1
  54. package/dist/git-cli/service.js +5 -1
  55. package/dist/git-cli/service.js.map +1 -1
  56. package/dist/plus/integrations/models/gitHostIntegration.d.ts +29 -14
  57. package/dist/plus/integrations/models/gitHostIntegration.js +41 -19
  58. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  59. package/dist/plus/integrations/providers/azure/azure.d.ts +16 -16
  60. package/dist/plus/integrations/providers/azure/azure.js +45 -25
  61. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  62. package/dist/plus/integrations/providers/azure/azure.utils.d.ts +12 -0
  63. package/dist/plus/integrations/providers/azure/azure.utils.js +17 -1
  64. package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
  65. package/dist/plus/integrations/providers/azure/search.d.ts +4 -0
  66. package/dist/plus/integrations/providers/azure/search.js +46 -2
  67. package/dist/plus/integrations/providers/azure/search.js.map +1 -1
  68. package/dist/plus/integrations/providers/azure/workItemSearch.d.ts +11 -0
  69. package/dist/plus/integrations/providers/azure/workItemSearch.js +54 -0
  70. package/dist/plus/integrations/providers/azure/workItemSearch.js.map +1 -0
  71. package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +2 -2
  72. package/dist/plus/integrations/providers/azure/workItemStateCategories.js +19 -15
  73. package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
  74. package/dist/plus/integrations/providers/azureDevOps.d.ts +33 -20
  75. package/dist/plus/integrations/providers/azureDevOps.js +109 -48
  76. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  77. package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +16 -47
  78. package/dist/plus/integrations/providers/azureDevOpsServer.js +52 -91
  79. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  80. package/dist/plus/integrations/providers/issueSorts.d.ts +0 -1
  81. package/dist/plus/integrations/providers/models.d.ts +5 -0
  82. package/dist/plus/integrations/providers/models.js +14 -3
  83. package/dist/plus/integrations/providers/models.js.map +1 -1
  84. package/dist/plus/integrations/reads/drains.d.ts +2 -3
  85. package/dist/plus/integrations/reads/drains.js +4 -4
  86. package/dist/plus/integrations/reads/drains.js.map +1 -1
  87. package/dist/plus/integrations/reads/issues.js +7 -4
  88. package/dist/plus/integrations/reads/issues.js.map +1 -1
  89. package/dist/plus/integrations/reads/resolveRepository.js +5 -5
  90. package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
  91. 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"}
@@ -60,11 +60,13 @@ export interface PullRequestShape extends IssueOrPullRequest {
60
60
  readonly number?: number;
61
61
  readonly authoredByMe?: boolean;
62
62
  /**
63
- * The current account as this pull request's members identify it: the identity {@link authoredByMe} was matched
64
- * against, present exactly when that was. A consumer matching {@link reviewRequests} or {@link latestReviews}
65
- * entries to the current user compares against this, not the account `getCurrentAccount` returns: Azure DevOps
66
- * Server gives one person a different id in each collection than at the server level, and the members carry the
67
- * collection's. Core matches `username` too only on GitHub and GitHub Enterprise, whose rows key people by login.
63
+ * The current account as this pull request's members identify it: the identity its people were matched against,
64
+ * present whenever it was resolved. {@link authoredByMe} is matched against it too, and stays `undefined` when the
65
+ * row has no author, though its reviewers or assignees still get this. A consumer matching {@link reviewRequests}
66
+ * or {@link latestReviews} entries to the current user compares against this, not the account `getCurrentAccount`
67
+ * returns: Azure DevOps can know one person by another id in each organization (Azure DevOps Server: collection)
68
+ * than the account's, and the members carry the organization's. Core matches `username` too only on GitHub and
69
+ * GitHub Enterprise, whose rows key people by login.
68
70
  */
69
71
  readonly viewer?: {
70
72
  readonly id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"pullRequest.js","names":[],"sources":["../../../../git/src/models/pullRequest.ts"],"sourcesContent":["import { formatDate, fromNow } from '@gitlens/utils/date.js';\nimport { loggable } from '@gitlens/utils/decorators/log.js';\nimport { serializable } from '@gitlens/utils/decorators/serializable.js';\nimport type { RequireSome, RequireSomeWithProps } from '@gitlens/utils/types.js';\nimport type { IssueProject, IssueRepository } from './issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestState as PullRequestState } from './issueOrPullRequest.js';\nimport type { ProviderReference } from './remoteProvider.js';\nimport type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';\n\nexport type { PullRequestState };\n\n/**\n * The read that produced a pull request row, as a key for `getPullRequestFieldPresence` (`@gitlens/integrations`). It\n * names a READ, not a field set: the same tag can carry different fields on different providers.\n */\nexport type PullRequestProjection =\n\t/** A single-item read: by number, for a branch, or for a commit. */\n\t| 'point'\n\t/** The filtered search with the full projection, and the host's \"my pull requests\" search. */\n\t| 'search'\n\t/** The filtered search with `summary`. */\n\t| 'search-summary'\n\t/** The free-text search (`searchPullRequests`). */\n\t| 'text-search'\n\t/** An account-wide sweep with the full projection (`includeReviews`). */\n\t| 'account'\n\t/** An account-wide list or sweep with the lightweight projection. */\n\t| 'account-summary'\n\t/** A repository-scoped list or sweep. */\n\t| 'repos'\n\t/** A repository-scoped list with `summary`. */\n\t| 'repos-summary'\n\t/** The coordinate and branch batch reads. */\n\t| 'batch';\n\nexport interface PullRequestShape extends IssueOrPullRequest {\n\treadonly state: PullRequestState;\n\treadonly author: PullRequestMember;\n\t/**\n\t * Canonical base-repository identity returned by the provider. Some providers\n\t * scope pull-request ids to a repository, so consumers must not have to infer\n\t * identity from optional refs or URLs.\n\t */\n\treadonly repository?: IssueRepository;\n\treadonly mergedDate?: Date;\n\treadonly refs?: PullRequestRefs;\n\treadonly isDraft?: boolean;\n\treadonly additions?: number;\n\treadonly deletions?: number;\n\treadonly filesChanged?: number;\n\treadonly body?: string;\n\treadonly mergeableState?: PullRequestMergeableState;\n\treadonly reviewDecision?: PullRequestReviewDecision;\n\treadonly reviewRequests?: PullRequestReviewer[];\n\t/**\n\t * Reviews already submitted, as opposed to the still-pending {@link reviewRequests}. Absent means the read\n\t * didn't fetch reviews; empty means nobody has reviewed — but only where the read actually fetched them, which\n\t * is provider- and read-dependent: ask `getPullRequestFieldPresence` (`@gitlens/integrations`) for the row's\n\t * `reviews` group rather than trusting an empty array.\n\t * {@link PullRequestReviewer.commitOid} is the narrower promise: only the full GitHub/GHE projection has it.\n\t */\n\treadonly latestReviews?: PullRequestReviewer[];\n\treadonly assignees?: PullRequestMember[];\n\treadonly project?: IssueProject;\n\treadonly stack?: PullRequestStackInfo;\n\treadonly number?: number;\n\treadonly authoredByMe?: boolean;\n\t/**\n\t * The current account as this pull request's members identify it: the identity {@link authoredByMe} was matched\n\t * against, present exactly when that was. A consumer matching {@link reviewRequests} or {@link latestReviews}\n\t * entries to the current user compares against this, not the account `getCurrentAccount` returns: Azure DevOps\n\t * Server gives one person a different id in each collection than at the server level, and the members carry the\n\t * collection's. Core matches `username` too only on GitHub and GitHub Enterprise, whose rows key people by login.\n\t */\n\treadonly viewer?: { readonly id: string; readonly username?: string };\n\t/** The read that produced this row. Absent means unknown: a legacy row, or a provider or read not yet tagged. */\n\treadonly projection?: PullRequestProjection;\n}\n\n@loggable(i => i.id)\n@serializable\nexport class PullRequest implements PullRequestShape {\n\treadonly type = 'pullrequest';\n\n\tconstructor(\n\t\tpublic readonly provider: ProviderReference,\n\t\tpublic readonly author: PullRequestMember,\n\t\tpublic readonly id: string,\n\t\tpublic readonly nodeId: string | undefined,\n\t\tpublic readonly title: string,\n\t\tpublic readonly url: string,\n\t\tpublic readonly repository: IssueRepository,\n\t\tpublic readonly state: PullRequestState,\n\t\tpublic readonly createdDate: Date,\n\t\tpublic readonly updatedDate: Date,\n\t\tpublic readonly closedDate?: Date,\n\t\tpublic readonly mergedDate?: Date,\n\t\tpublic readonly mergeableState?: PullRequestMergeableState,\n\t\tpublic readonly viewerCanUpdate?: boolean,\n\t\tpublic readonly refs?: PullRequestRefs,\n\t\tpublic readonly isDraft?: boolean,\n\t\tpublic readonly additions?: number,\n\t\tpublic readonly deletions?: number,\n\t\tpublic readonly commentsCount?: number,\n\t\tpublic readonly thumbsUpCount?: number,\n\t\tpublic readonly reviewDecision?: PullRequestReviewDecision,\n\t\tpublic readonly reviewRequests?: PullRequestReviewer[],\n\t\tpublic readonly latestReviews?: PullRequestReviewer[],\n\t\tpublic readonly assignees?: PullRequestMember[],\n\t\tpublic readonly statusCheckRollupState?: PullRequestStatusCheckRollupState,\n\t\tpublic readonly project?: IssueProject,\n\t\tpublic readonly version?: number,\n\t\t/** Commits on the head that aren't on the base. Appended rather than slotted with the other counts:\n\t\t * this constructor is positional and every mapper passes through it. */\n\t\tpublic readonly commitCount?: number,\n\t\tpublic readonly stack?: PullRequestStackInfo,\n\t\tpublic readonly filesChanged?: number,\n\t\t/** The pull request's description, as markdown. */\n\t\tpublic readonly body?: string,\n\t\tpublic readonly number?: number,\n\t\tpublic readonly authoredByMe?: boolean,\n\t\tpublic readonly projection?: PullRequestProjection,\n\t\tpublic readonly viewer?: { readonly id: string; readonly username?: string },\n\t) {}\n\n\tget closed(): boolean {\n\t\treturn this.state === 'closed';\n\t}\n\n\tstatic is(pr: unknown): pr is PullRequest {\n\t\treturn pr instanceof PullRequest;\n\t}\n\n\tstatic formatDate(pr: PullRequestShape, format?: string | null): string {\n\t\treturn formatDate(pr.mergedDate ?? pr.closedDate ?? pr.updatedDate, format ?? 'MMMM Do, YYYY h:mma');\n\t}\n\n\tstatic formatDateFromNow(pr: PullRequestShape): string {\n\t\treturn fromNow(pr.mergedDate ?? pr.closedDate ?? pr.updatedDate);\n\t}\n\n\tstatic formatDateWithStyle(\n\t\tpr: PullRequestShape,\n\t\tformatting: { dateStyle: string; dateFormat: string | null },\n\t): string {\n\t\treturn formatting.dateStyle === 'absolute'\n\t\t\t? PullRequest.formatDate(pr, formatting.dateFormat)\n\t\t\t: PullRequest.formatDateFromNow(pr);\n\t}\n}\n\nexport const enum PullRequestReviewDecision {\n\tApproved = 'Approved',\n\tChangesRequested = 'ChangesRequested',\n\tReviewRequired = 'ReviewRequired',\n}\n\nexport const enum PullRequestMergeableState {\n\tUnknown = 'Unknown',\n\tMergeable = 'Mergeable',\n\tConflicting = 'Conflicting',\n\tFailingChecks = 'FailingChecks',\n\tBlockedByPolicy = 'BlockedByPolicy',\n}\n\nexport const enum PullRequestStatusCheckRollupState {\n\tSuccess = 'success',\n\tPending = 'pending',\n\tFailed = 'failed',\n}\n\nexport const enum PullRequestMergeMethod {\n\tMerge = 'merge',\n\tSquash = 'squash',\n\tRebase = 'rebase',\n}\n\nexport const enum PullRequestReviewState {\n\tApproved = 'APPROVED',\n\tChangesRequested = 'CHANGES_REQUESTED',\n\tCommented = 'COMMENTED',\n\tDismissed = 'DISMISSED',\n\tPending = 'PENDING',\n\tReviewRequested = 'REVIEW_REQUESTED',\n}\n\nexport interface PullRequestComparisonRefs {\n\trepoPath: string;\n\tbase: { ref: string; label: string };\n\thead: { ref: string; label: string };\n}\n\nexport interface PullRequestMember {\n\tid: string;\n\t/**\n\t * Absent when the provider exposes no display name for the member — same contract as `IssueMember.name`: a\n\t * fallback invented here can't be told apart from a real name downstream, so each consumer picks its own\n\t * presentation.\n\t */\n\tname?: string;\n\t/** The provider's handle for this person, when it has one — GitHub's login, Azure's `uniqueName` (a UPN,\n\t * so an email), Bitbucket's mutable `nickname`. Display/labelling only: it is neither guaranteed present\n\t * (GitLab's native mapper has none) nor a stable identity, so never key a match off it. */\n\tusername?: string;\n\tavatarUrl?: string;\n\turl?: string;\n}\n\n/** Provider-neutral relationship facets shared by PR listings and filtered PR search. */\nexport enum PullRequestFilter {\n\tAuthor = 'author',\n\tAssignee = 'assignee',\n\tReviewRequested = 'review-requested',\n\t/**\n\t * PRs the user has already reviewed (GitHub `reviewed-by:@me`), as opposed to `ReviewRequested` (a\n\t * still-pending request). Distinguishes \"waiting on the author after my review\" from \"waiting for my\n\t * review\". Not expressible on the repo-scoped listing — no provider SDK exposes a reviewed-by axis there —\n\t * so it is advertised for the account-wide read and for the filtered search (which can still bound itself\n\t * to repositories or an org), never under `supportedPullRequestFilters`.\n\t */\n\tReviewed = 'reviewed-by',\n\tMention = 'mention',\n}\n\n/** Selects which pull request states a read should include. `all` covers open + closed + merged. */\nexport type PullRequestStateFilter = 'open' | 'closed' | 'merged' | 'all';\n\n/**\n * A field a pull-request read can be ordered by, provider-neutral.\n *\n * Only two, and deliberately so — unlike {@link IssueSortField} this is the INTERSECTION of what GitHub's PR\n * search can order by server-side and what a {@link PullRequestShape} carries, because the filtered PR search\n * always merges its relationship × state facets in the facade and so must re-order the union itself (see\n * `getPullRequestComparator`). `created` and `updated` are both. A field GitHub cannot order PRs by, or one a\n * merged page can't reproduce, is left out rather than advertised and then approximated: relevance is unstable\n * and has no comparable value on a merge, and priority is not a pull-request concept on any provider that has PRs.\n */\nexport type PullRequestSortField =\n\t/** Creation date. */\n\t| 'created'\n\t/** Last activity. */\n\t| 'updated';\n\n/**\n * How a pull-request read is ordered, as `field:direction` — the same serializable shape {@link IssueSorting}\n * and `BranchSorting` use, so a consumer can persist it, compare it, or bind it straight to a setting or dropdown.\n */\nexport type PullRequestSorting = `${PullRequestSortField}:asc` | `${PullRequestSortField}:desc`;\n\n/**\n * The order the filtered pull-request search applies when the caller asks for none: most recently updated first,\n * which is what it served before ordering was an option. Named once rather than per layer — the criteria model\n * documents it, the GitHub query emits it, and the result-ceiling warning quotes it — so the three cannot\n * disagree about what \"the default\" is.\n */\nexport const defaultPullRequestSort: PullRequestSorting = 'updated:desc';\n\n/**\n * What the filtered pull-request search narrows on.\n *\n * Structured rather than a provider query string so callers can send free text without giving it a qualifier\n * channel, and can check {@link PullRequestSearchCapabilities} before issuing a read. Every requested criterion\n * is validated all-or-nothing before the provider runs; unsupported criteria never fall through to a wider\n * result set whose paging would no longer describe the returned items.\n *\n * Relationships are OR-ed, as are states. Omitting `relationships` removes the current-user constraint, which is\n * only safe when the manager call supplies a repository or organization scope. Omitting `states` reads open PRs.\n */\nexport interface PullRequestSearchCriteria {\n\t/**\n\t * Free text matched by the provider's own relevance rules. Tokens that look like provider qualifiers are\n\t * removed before the query is built, so text cannot change the search's scope or state.\n\t */\n\ttext?: string;\n\t/** Current-user relationship facets to union. Empty/omitted means no relationship constraint. */\n\trelationships?: PullRequestFilter[];\n\t/** Pull request states to union. Empty/omitted reads open PRs; `all` subsumes every other member. */\n\tstates?: PullRequestStateFilter[];\n\t/** ISO date (`YYYY-MM-DD`). The most effective narrowing criterion by far on a large scope. */\n\tupdatedAfter?: string;\n\t/** ISO date (`YYYY-MM-DD`). */\n\tcreatedAfter?: string;\n\t/** Includes PRs from archived repositories. They are excluded by default. */\n\tincludeArchived?: boolean;\n\t/**\n\t * Narrows on draft state: `true` returns only drafts, `false` only ready-for-review PRs. Omitted places no\n\t * constraint. A boolean rather than a truthy flag because `false` is a distinct request, not the absence of one.\n\t */\n\tdraft?: boolean;\n\t/**\n\t * How to order the results. Omitted means `updated:desc`, which is what this search has always served.\n\t *\n\t * Validated all-or-nothing against {@link PullRequestSearchCapabilities.sorts} like every other criterion: a\n\t * key the provider can't express server-side refuses the WHOLE read rather than falling back to the default,\n\t * because combined with the provider's result ceiling another order returns a different subset than was asked\n\t * for and the paging that comes with it describes that other subset. Do not change it mid-pagination — a\n\t * cursor carries the sort it was produced under; drop the cursor instead of threading it under a new key.\n\t */\n\tsort?: PullRequestSorting;\n}\n\n/**\n * Which {@link PullRequestSearchCriteria} fields and manager-level scopes a provider can express server-side.\n * An empty `relationships` means the provider has no filtered PR search at all.\n */\nexport interface PullRequestSearchCapabilities {\n\t/** Current-user relationship facets the provider can union. Empty means the search itself is unsupported. */\n\trelationships: PullRequestFilter[];\n\t/** Individual state values the provider can union in one logical search. */\n\tstates: PullRequestStateFilter[];\n\ttext: boolean;\n\tupdatedAfter: boolean;\n\tcreatedAfter: boolean;\n\tincludeArchived: boolean;\n\t/** Whether the provider can constrain the search by draft state server-side. */\n\tdraft: boolean;\n\t/** Whether the manager's repository-descriptor scope is supported. */\n\trepositoryScope: boolean;\n\t/** Whether the manager's organization scope is supported. */\n\torganizationScope: boolean;\n\t/**\n\t * Sort keys the search can express server-side. Always contains at least `updated:desc` — the historical\n\t * default — when the search exists at all, so it is never empty for a usable surface; a provider without a\n\t * filtered PR search reports an empty `relationships`, which is already the signal there is no surface to order.\n\t */\n\tsorts: PullRequestSorting[];\n}\n\nexport interface PullRequestRef {\n\t/**\n\t * `undefined` when the ref's repository was deleted (a removed fork): GitHub's rows say so, with `exists` false.\n\t * provider-apis' converters fill `''` instead, so test for a value rather than for `undefined`.\n\t */\n\towner: string | undefined;\n\t/** `undefined` (or `''`) when the ref's repository was deleted, as {@link owner}. */\n\trepo: string | undefined;\n\tbranch: string;\n\tsha: string;\n\texists: boolean;\n\t/**\n\t * Web URL of the ref's repository. `undefined` when the provider cannot resolve it — Azure DevOps embeds an\n\t * abbreviated repository reference in a pull request, so a fork's URL may need a lookup that can fail.\n\t */\n\turl: string | undefined;\n\t/** HTTPS clone URL of the ref's repository, when the provider exposes it. */\n\tcloneHttps?: string;\n\t/** SSH clone URL of the ref's repository, when the provider exposes it. */\n\tcloneSsh?: string;\n\t/** Best-effort flag: whether the ref's repository is a fork. `undefined` when the provider can't tell. */\n\tisFork?: boolean;\n}\n\nexport interface PullRequestRefs {\n\tbase: PullRequestRef & { url: string };\n\thead: PullRequestRef;\n\tisCrossRepository: boolean;\n}\n\n/**\n * A pull request's membership in a stack — an ordered chain of dependent pull requests where each\n * targets the branch of the one below it.\n *\n * `baseRef` is the stack's ultimate target (its trunk) and is NOT the same as `refs.base`, which for\n * anything above the bottom layer names the layer below. Use `refs.base` to diff a single layer; use\n * `baseRef` to answer where the work ultimately lands.\n */\nexport interface PullRequestStackInfo {\n\t/**\n\t * Opaque, and NOT comparable across read paths: GitHub's GraphQL selection returns a node id\n\t * (`PRS_kwDO…`) while its REST stacks endpoint returns a numeric id (`1347135`) that we stringify —\n\t * both verified against the same live stack. Use {@link number} to identify a stack; two views of one\n\t * stack agree there and will not agree here.\n\t */\n\tid: string;\n\t/** Identifies the stack within its repository. */\n\tnumber: number;\n\t/** Total pull requests in the stack. */\n\tsize: number;\n\t/** This pull request's layer, 1-based, where 1 is closest to `baseRef`. */\n\tposition: number;\n\t/** The branch the bottom of the stack targets. */\n\tbaseRef: string;\n}\n\n/**\n * One layer of a stack — the pull request occupying it, and the head ref a rebase or push needs to act\n * on. Unlike {@link PullRequestStackInfo}, which locates a single pull request within a stack, this is\n * the per-layer detail a caller walking the whole stack needs, e.g. to cascade a rebase bottom to top.\n */\nexport interface PullRequestStackLayer {\n\t/** The pull request's number. */\n\tnumber: number;\n\t/** The pull request's head branch name, without a remote prefix. */\n\theadRef: string;\n\t/** The head branch's tip when the stack was read. */\n\theadSha: string;\n\t/** `true` once this layer has landed. */\n\tmerged: boolean;\n\t/** `true` while this layer is still a draft. */\n\tdraft: boolean;\n}\n\n/**\n * A stack's layers, ordered bottom to top. `baseRef` is the stack's ultimate target (its trunk), the\n * same distinction drawn on {@link PullRequestStackInfo} — it is NOT the base of any individual layer\n * above the bottom, which targets the layer below it instead.\n */\nexport interface PullRequestStackLayers {\n\t/** Opaque and not comparable across read paths — see {@link PullRequestStackInfo.id}. */\n\tid: string;\n\tnumber: number;\n\t/** The branch the bottom of the stack targets. */\n\tbaseRef: string;\n\t/** The stack's layers, ordered bottom (closest to {@link baseRef}) to top. */\n\tlayers: PullRequestStackLayer[];\n}\n\nexport interface PullRequestReviewer {\n\tisCodeOwner?: boolean;\n\treviewer: PullRequestMember;\n\tstate: PullRequestReviewState;\n\t/**\n\t * The head commit oid this review was submitted against (GitHub). Lets a consumer tell \"the PR moved past\n\t * my review\" (oid !== the PR's current head) from a review still at the tip — without a per-review\n\t * timestamp. Only populated where the full GitHub/GHE projection is selected: the filtered search read, and\n\t * an account-wide sweep that opts in with `includeReviews`. Undefined elsewhere, including reviews the\n\t * repo-scoped read carries natively.\n\t */\n\tcommitOid?: string;\n\t/**\n\t * The reviewer is a group the current user is a member of (a team or another group, directly or through a group\n\t * nested in it) rather than a person: a request to it is one the user can answer as a member, not one addressed to\n\t * them by name. Only the Azure DevOps reads that resolve the user's groups set it; absent elsewhere, including for\n\t * groups a read could not tell the user belongs to.\n\t */\n\tisMyGroup?: boolean;\n}\n\nexport type PullRequestRepositoryIdentityDescriptor = RequireSomeWithProps<\n\tRequireSome<RepositoryIdentityDescriptor<string>, 'provider'>,\n\t'provider',\n\t'id' | 'domain' | 'repoDomain' | 'repoName'\n> &\n\tRequireSomeWithProps<RequireSome<RepositoryIdentityDescriptor<string>, 'remote'>, 'remote', 'domain'>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"pullRequest.js","names":[],"sources":["../../../../git/src/models/pullRequest.ts"],"sourcesContent":["import { formatDate, fromNow } from '@gitlens/utils/date.js';\nimport { loggable } from '@gitlens/utils/decorators/log.js';\nimport { serializable } from '@gitlens/utils/decorators/serializable.js';\nimport type { RequireSome, RequireSomeWithProps } from '@gitlens/utils/types.js';\nimport type { IssueProject, IssueRepository } from './issue.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestState as PullRequestState } from './issueOrPullRequest.js';\nimport type { ProviderReference } from './remoteProvider.js';\nimport type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';\n\nexport type { PullRequestState };\n\n/**\n * The read that produced a pull request row, as a key for `getPullRequestFieldPresence` (`@gitlens/integrations`). It\n * names a READ, not a field set: the same tag can carry different fields on different providers.\n */\nexport type PullRequestProjection =\n\t/** A single-item read: by number, for a branch, or for a commit. */\n\t| 'point'\n\t/** The filtered search with the full projection, and the host's \"my pull requests\" search. */\n\t| 'search'\n\t/** The filtered search with `summary`. */\n\t| 'search-summary'\n\t/** The free-text search (`searchPullRequests`). */\n\t| 'text-search'\n\t/** An account-wide sweep with the full projection (`includeReviews`). */\n\t| 'account'\n\t/** An account-wide list or sweep with the lightweight projection. */\n\t| 'account-summary'\n\t/** A repository-scoped list or sweep. */\n\t| 'repos'\n\t/** A repository-scoped list with `summary`. */\n\t| 'repos-summary'\n\t/** The coordinate and branch batch reads. */\n\t| 'batch';\n\nexport interface PullRequestShape extends IssueOrPullRequest {\n\treadonly state: PullRequestState;\n\treadonly author: PullRequestMember;\n\t/**\n\t * Canonical base-repository identity returned by the provider. Some providers\n\t * scope pull-request ids to a repository, so consumers must not have to infer\n\t * identity from optional refs or URLs.\n\t */\n\treadonly repository?: IssueRepository;\n\treadonly mergedDate?: Date;\n\treadonly refs?: PullRequestRefs;\n\treadonly isDraft?: boolean;\n\treadonly additions?: number;\n\treadonly deletions?: number;\n\treadonly filesChanged?: number;\n\treadonly body?: string;\n\treadonly mergeableState?: PullRequestMergeableState;\n\treadonly reviewDecision?: PullRequestReviewDecision;\n\treadonly reviewRequests?: PullRequestReviewer[];\n\t/**\n\t * Reviews already submitted, as opposed to the still-pending {@link reviewRequests}. Absent means the read\n\t * didn't fetch reviews; empty means nobody has reviewed — but only where the read actually fetched them, which\n\t * is provider- and read-dependent: ask `getPullRequestFieldPresence` (`@gitlens/integrations`) for the row's\n\t * `reviews` group rather than trusting an empty array.\n\t * {@link PullRequestReviewer.commitOid} is the narrower promise: only the full GitHub/GHE projection has it.\n\t */\n\treadonly latestReviews?: PullRequestReviewer[];\n\treadonly assignees?: PullRequestMember[];\n\treadonly project?: IssueProject;\n\treadonly stack?: PullRequestStackInfo;\n\treadonly number?: number;\n\treadonly authoredByMe?: boolean;\n\t/**\n\t * The current account as this pull request's members identify it: the identity its people were matched against,\n\t * present whenever it was resolved. {@link authoredByMe} is matched against it too, and stays `undefined` when the\n\t * row has no author, though its reviewers or assignees still get this. A consumer matching {@link reviewRequests}\n\t * or {@link latestReviews} entries to the current user compares against this, not the account `getCurrentAccount`\n\t * returns: Azure DevOps can know one person by another id in each organization (Azure DevOps Server: collection)\n\t * than the account's, and the members carry the organization's. Core matches `username` too only on GitHub and\n\t * GitHub Enterprise, whose rows key people by login.\n\t */\n\treadonly viewer?: { readonly id: string; readonly username?: string };\n\t/** The read that produced this row. Absent means unknown: a legacy row, or a provider or read not yet tagged. */\n\treadonly projection?: PullRequestProjection;\n}\n\n@loggable(i => i.id)\n@serializable\nexport class PullRequest implements PullRequestShape {\n\treadonly type = 'pullrequest';\n\n\tconstructor(\n\t\tpublic readonly provider: ProviderReference,\n\t\tpublic readonly author: PullRequestMember,\n\t\tpublic readonly id: string,\n\t\tpublic readonly nodeId: string | undefined,\n\t\tpublic readonly title: string,\n\t\tpublic readonly url: string,\n\t\tpublic readonly repository: IssueRepository,\n\t\tpublic readonly state: PullRequestState,\n\t\tpublic readonly createdDate: Date,\n\t\tpublic readonly updatedDate: Date,\n\t\tpublic readonly closedDate?: Date,\n\t\tpublic readonly mergedDate?: Date,\n\t\tpublic readonly mergeableState?: PullRequestMergeableState,\n\t\tpublic readonly viewerCanUpdate?: boolean,\n\t\tpublic readonly refs?: PullRequestRefs,\n\t\tpublic readonly isDraft?: boolean,\n\t\tpublic readonly additions?: number,\n\t\tpublic readonly deletions?: number,\n\t\tpublic readonly commentsCount?: number,\n\t\tpublic readonly thumbsUpCount?: number,\n\t\tpublic readonly reviewDecision?: PullRequestReviewDecision,\n\t\tpublic readonly reviewRequests?: PullRequestReviewer[],\n\t\tpublic readonly latestReviews?: PullRequestReviewer[],\n\t\tpublic readonly assignees?: PullRequestMember[],\n\t\tpublic readonly statusCheckRollupState?: PullRequestStatusCheckRollupState,\n\t\tpublic readonly project?: IssueProject,\n\t\tpublic readonly version?: number,\n\t\t/** Commits on the head that aren't on the base. Appended rather than slotted with the other counts:\n\t\t * this constructor is positional and every mapper passes through it. */\n\t\tpublic readonly commitCount?: number,\n\t\tpublic readonly stack?: PullRequestStackInfo,\n\t\tpublic readonly filesChanged?: number,\n\t\t/** The pull request's description, as markdown. */\n\t\tpublic readonly body?: string,\n\t\tpublic readonly number?: number,\n\t\tpublic readonly authoredByMe?: boolean,\n\t\tpublic readonly projection?: PullRequestProjection,\n\t\tpublic readonly viewer?: { readonly id: string; readonly username?: string },\n\t) {}\n\n\tget closed(): boolean {\n\t\treturn this.state === 'closed';\n\t}\n\n\tstatic is(pr: unknown): pr is PullRequest {\n\t\treturn pr instanceof PullRequest;\n\t}\n\n\tstatic formatDate(pr: PullRequestShape, format?: string | null): string {\n\t\treturn formatDate(pr.mergedDate ?? pr.closedDate ?? pr.updatedDate, format ?? 'MMMM Do, YYYY h:mma');\n\t}\n\n\tstatic formatDateFromNow(pr: PullRequestShape): string {\n\t\treturn fromNow(pr.mergedDate ?? pr.closedDate ?? pr.updatedDate);\n\t}\n\n\tstatic formatDateWithStyle(\n\t\tpr: PullRequestShape,\n\t\tformatting: { dateStyle: string; dateFormat: string | null },\n\t): string {\n\t\treturn formatting.dateStyle === 'absolute'\n\t\t\t? PullRequest.formatDate(pr, formatting.dateFormat)\n\t\t\t: PullRequest.formatDateFromNow(pr);\n\t}\n}\n\nexport const enum PullRequestReviewDecision {\n\tApproved = 'Approved',\n\tChangesRequested = 'ChangesRequested',\n\tReviewRequired = 'ReviewRequired',\n}\n\nexport const enum PullRequestMergeableState {\n\tUnknown = 'Unknown',\n\tMergeable = 'Mergeable',\n\tConflicting = 'Conflicting',\n\tFailingChecks = 'FailingChecks',\n\tBlockedByPolicy = 'BlockedByPolicy',\n}\n\nexport const enum PullRequestStatusCheckRollupState {\n\tSuccess = 'success',\n\tPending = 'pending',\n\tFailed = 'failed',\n}\n\nexport const enum PullRequestMergeMethod {\n\tMerge = 'merge',\n\tSquash = 'squash',\n\tRebase = 'rebase',\n}\n\nexport const enum PullRequestReviewState {\n\tApproved = 'APPROVED',\n\tChangesRequested = 'CHANGES_REQUESTED',\n\tCommented = 'COMMENTED',\n\tDismissed = 'DISMISSED',\n\tPending = 'PENDING',\n\tReviewRequested = 'REVIEW_REQUESTED',\n}\n\nexport interface PullRequestComparisonRefs {\n\trepoPath: string;\n\tbase: { ref: string; label: string };\n\thead: { ref: string; label: string };\n}\n\nexport interface PullRequestMember {\n\tid: string;\n\t/**\n\t * Absent when the provider exposes no display name for the member — same contract as `IssueMember.name`: a\n\t * fallback invented here can't be told apart from a real name downstream, so each consumer picks its own\n\t * presentation.\n\t */\n\tname?: string;\n\t/** The provider's handle for this person, when it has one — GitHub's login, Azure's `uniqueName` (a UPN,\n\t * so an email), Bitbucket's mutable `nickname`. Display/labelling only: it is neither guaranteed present\n\t * (GitLab's native mapper has none) nor a stable identity, so never key a match off it. */\n\tusername?: string;\n\tavatarUrl?: string;\n\turl?: string;\n}\n\n/** Provider-neutral relationship facets shared by PR listings and filtered PR search. */\nexport enum PullRequestFilter {\n\tAuthor = 'author',\n\tAssignee = 'assignee',\n\tReviewRequested = 'review-requested',\n\t/**\n\t * PRs the user has already reviewed (GitHub `reviewed-by:@me`), as opposed to `ReviewRequested` (a\n\t * still-pending request). Distinguishes \"waiting on the author after my review\" from \"waiting for my\n\t * review\". Not expressible on the repo-scoped listing — no provider SDK exposes a reviewed-by axis there —\n\t * so it is advertised for the account-wide read and for the filtered search (which can still bound itself\n\t * to repositories or an org), never under `supportedPullRequestFilters`.\n\t */\n\tReviewed = 'reviewed-by',\n\tMention = 'mention',\n}\n\n/** Selects which pull request states a read should include. `all` covers open + closed + merged. */\nexport type PullRequestStateFilter = 'open' | 'closed' | 'merged' | 'all';\n\n/**\n * A field a pull-request read can be ordered by, provider-neutral.\n *\n * Only two, and deliberately so — unlike {@link IssueSortField} this is the INTERSECTION of what GitHub's PR\n * search can order by server-side and what a {@link PullRequestShape} carries, because the filtered PR search\n * always merges its relationship × state facets in the facade and so must re-order the union itself (see\n * `getPullRequestComparator`). `created` and `updated` are both. A field GitHub cannot order PRs by, or one a\n * merged page can't reproduce, is left out rather than advertised and then approximated: relevance is unstable\n * and has no comparable value on a merge, and priority is not a pull-request concept on any provider that has PRs.\n */\nexport type PullRequestSortField =\n\t/** Creation date. */\n\t| 'created'\n\t/** Last activity. */\n\t| 'updated';\n\n/**\n * How a pull-request read is ordered, as `field:direction` — the same serializable shape {@link IssueSorting}\n * and `BranchSorting` use, so a consumer can persist it, compare it, or bind it straight to a setting or dropdown.\n */\nexport type PullRequestSorting = `${PullRequestSortField}:asc` | `${PullRequestSortField}:desc`;\n\n/**\n * The order the filtered pull-request search applies when the caller asks for none: most recently updated first,\n * which is what it served before ordering was an option. Named once rather than per layer — the criteria model\n * documents it, the GitHub query emits it, and the result-ceiling warning quotes it — so the three cannot\n * disagree about what \"the default\" is.\n */\nexport const defaultPullRequestSort: PullRequestSorting = 'updated:desc';\n\n/**\n * What the filtered pull-request search narrows on.\n *\n * Structured rather than a provider query string so callers can send free text without giving it a qualifier\n * channel, and can check {@link PullRequestSearchCapabilities} before issuing a read. Every requested criterion\n * is validated all-or-nothing before the provider runs; unsupported criteria never fall through to a wider\n * result set whose paging would no longer describe the returned items.\n *\n * Relationships are OR-ed, as are states. Omitting `relationships` removes the current-user constraint, which is\n * only safe when the manager call supplies a repository or organization scope. Omitting `states` reads open PRs.\n */\nexport interface PullRequestSearchCriteria {\n\t/**\n\t * Free text matched by the provider's own relevance rules. Tokens that look like provider qualifiers are\n\t * removed before the query is built, so text cannot change the search's scope or state.\n\t */\n\ttext?: string;\n\t/** Current-user relationship facets to union. Empty/omitted means no relationship constraint. */\n\trelationships?: PullRequestFilter[];\n\t/** Pull request states to union. Empty/omitted reads open PRs; `all` subsumes every other member. */\n\tstates?: PullRequestStateFilter[];\n\t/** ISO date (`YYYY-MM-DD`). The most effective narrowing criterion by far on a large scope. */\n\tupdatedAfter?: string;\n\t/** ISO date (`YYYY-MM-DD`). */\n\tcreatedAfter?: string;\n\t/** Includes PRs from archived repositories. They are excluded by default. */\n\tincludeArchived?: boolean;\n\t/**\n\t * Narrows on draft state: `true` returns only drafts, `false` only ready-for-review PRs. Omitted places no\n\t * constraint. A boolean rather than a truthy flag because `false` is a distinct request, not the absence of one.\n\t */\n\tdraft?: boolean;\n\t/**\n\t * How to order the results. Omitted means `updated:desc`, which is what this search has always served.\n\t *\n\t * Validated all-or-nothing against {@link PullRequestSearchCapabilities.sorts} like every other criterion: a\n\t * key the provider can't express server-side refuses the WHOLE read rather than falling back to the default,\n\t * because combined with the provider's result ceiling another order returns a different subset than was asked\n\t * for and the paging that comes with it describes that other subset. Do not change it mid-pagination — a\n\t * cursor carries the sort it was produced under; drop the cursor instead of threading it under a new key.\n\t */\n\tsort?: PullRequestSorting;\n}\n\n/**\n * Which {@link PullRequestSearchCriteria} fields and manager-level scopes a provider can express server-side.\n * An empty `relationships` means the provider has no filtered PR search at all.\n */\nexport interface PullRequestSearchCapabilities {\n\t/** Current-user relationship facets the provider can union. Empty means the search itself is unsupported. */\n\trelationships: PullRequestFilter[];\n\t/** Individual state values the provider can union in one logical search. */\n\tstates: PullRequestStateFilter[];\n\ttext: boolean;\n\tupdatedAfter: boolean;\n\tcreatedAfter: boolean;\n\tincludeArchived: boolean;\n\t/** Whether the provider can constrain the search by draft state server-side. */\n\tdraft: boolean;\n\t/** Whether the manager's repository-descriptor scope is supported. */\n\trepositoryScope: boolean;\n\t/** Whether the manager's organization scope is supported. */\n\torganizationScope: boolean;\n\t/**\n\t * Sort keys the search can express server-side. Always contains at least `updated:desc` — the historical\n\t * default — when the search exists at all, so it is never empty for a usable surface; a provider without a\n\t * filtered PR search reports an empty `relationships`, which is already the signal there is no surface to order.\n\t */\n\tsorts: PullRequestSorting[];\n}\n\nexport interface PullRequestRef {\n\t/**\n\t * `undefined` when the ref's repository was deleted (a removed fork): GitHub's rows say so, with `exists` false.\n\t * provider-apis' converters fill `''` instead, so test for a value rather than for `undefined`.\n\t */\n\towner: string | undefined;\n\t/** `undefined` (or `''`) when the ref's repository was deleted, as {@link owner}. */\n\trepo: string | undefined;\n\tbranch: string;\n\tsha: string;\n\texists: boolean;\n\t/**\n\t * Web URL of the ref's repository. `undefined` when the provider cannot resolve it — Azure DevOps embeds an\n\t * abbreviated repository reference in a pull request, so a fork's URL may need a lookup that can fail.\n\t */\n\turl: string | undefined;\n\t/** HTTPS clone URL of the ref's repository, when the provider exposes it. */\n\tcloneHttps?: string;\n\t/** SSH clone URL of the ref's repository, when the provider exposes it. */\n\tcloneSsh?: string;\n\t/** Best-effort flag: whether the ref's repository is a fork. `undefined` when the provider can't tell. */\n\tisFork?: boolean;\n}\n\nexport interface PullRequestRefs {\n\tbase: PullRequestRef & { url: string };\n\thead: PullRequestRef;\n\tisCrossRepository: boolean;\n}\n\n/**\n * A pull request's membership in a stack — an ordered chain of dependent pull requests where each\n * targets the branch of the one below it.\n *\n * `baseRef` is the stack's ultimate target (its trunk) and is NOT the same as `refs.base`, which for\n * anything above the bottom layer names the layer below. Use `refs.base` to diff a single layer; use\n * `baseRef` to answer where the work ultimately lands.\n */\nexport interface PullRequestStackInfo {\n\t/**\n\t * Opaque, and NOT comparable across read paths: GitHub's GraphQL selection returns a node id\n\t * (`PRS_kwDO…`) while its REST stacks endpoint returns a numeric id (`1347135`) that we stringify —\n\t * both verified against the same live stack. Use {@link number} to identify a stack; two views of one\n\t * stack agree there and will not agree here.\n\t */\n\tid: string;\n\t/** Identifies the stack within its repository. */\n\tnumber: number;\n\t/** Total pull requests in the stack. */\n\tsize: number;\n\t/** This pull request's layer, 1-based, where 1 is closest to `baseRef`. */\n\tposition: number;\n\t/** The branch the bottom of the stack targets. */\n\tbaseRef: string;\n}\n\n/**\n * One layer of a stack — the pull request occupying it, and the head ref a rebase or push needs to act\n * on. Unlike {@link PullRequestStackInfo}, which locates a single pull request within a stack, this is\n * the per-layer detail a caller walking the whole stack needs, e.g. to cascade a rebase bottom to top.\n */\nexport interface PullRequestStackLayer {\n\t/** The pull request's number. */\n\tnumber: number;\n\t/** The pull request's head branch name, without a remote prefix. */\n\theadRef: string;\n\t/** The head branch's tip when the stack was read. */\n\theadSha: string;\n\t/** `true` once this layer has landed. */\n\tmerged: boolean;\n\t/** `true` while this layer is still a draft. */\n\tdraft: boolean;\n}\n\n/**\n * A stack's layers, ordered bottom to top. `baseRef` is the stack's ultimate target (its trunk), the\n * same distinction drawn on {@link PullRequestStackInfo} — it is NOT the base of any individual layer\n * above the bottom, which targets the layer below it instead.\n */\nexport interface PullRequestStackLayers {\n\t/** Opaque and not comparable across read paths — see {@link PullRequestStackInfo.id}. */\n\tid: string;\n\tnumber: number;\n\t/** The branch the bottom of the stack targets. */\n\tbaseRef: string;\n\t/** The stack's layers, ordered bottom (closest to {@link baseRef}) to top. */\n\tlayers: PullRequestStackLayer[];\n}\n\nexport interface PullRequestReviewer {\n\tisCodeOwner?: boolean;\n\treviewer: PullRequestMember;\n\tstate: PullRequestReviewState;\n\t/**\n\t * The head commit oid this review was submitted against (GitHub). Lets a consumer tell \"the PR moved past\n\t * my review\" (oid !== the PR's current head) from a review still at the tip — without a per-review\n\t * timestamp. Only populated where the full GitHub/GHE projection is selected: the filtered search read, and\n\t * an account-wide sweep that opts in with `includeReviews`. Undefined elsewhere, including reviews the\n\t * repo-scoped read carries natively.\n\t */\n\tcommitOid?: string;\n\t/**\n\t * The reviewer is a group the current user is a member of (a team or another group, directly or through a group\n\t * nested in it) rather than a person: a request to it is one the user can answer as a member, not one addressed to\n\t * them by name. Only the Azure DevOps reads that resolve the user's groups set it; absent elsewhere, including for\n\t * groups a read could not tell the user belongs to.\n\t */\n\tisMyGroup?: boolean;\n}\n\nexport type PullRequestRepositoryIdentityDescriptor = RequireSomeWithProps<\n\tRequireSome<RepositoryIdentityDescriptor<string>, 'provider'>,\n\t'provider',\n\t'id' | 'domain' | 'repoDomain' | 'repoName'\n> &\n\tRequireSomeWithProps<RequireSome<RepositoryIdentityDescriptor<string>, 'remote'>, 'remote', 'domain'>;\n"],"mappings":"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"}
@@ -83,6 +83,14 @@ export interface RefRecord {
83
83
  upstreamRemote?: string;
84
84
  /** `%(upstream:remoteref)` — the ref a local branch tracks on its remote (`branch.<name>.merge`); only read where git supports it */
85
85
  upstreamRemoteRef?: string;
86
+ /**
87
+ * `%(push)` — the full name of a local branch's push destination as git resolves `<branch>@{push}` from
88
+ * `push.default`, the push remote and its refspecs. Named even when that ref doesn't exist yet; empty when git
89
+ * can't name one (no upstream under `simple`/`upstream`, `nothing`, `simple` with another push remote, or a
90
+ * destination with no local tracking ref). Not always where `git push` goes: a triangular `simple` push goes like
91
+ * `current`, though it is empty here
92
+ */
93
+ push: string;
86
94
  }
87
95
  //#endregion
88
96
  //# sourceMappingURL=reference.d.ts.map
@@ -42,6 +42,32 @@ export interface AheadOfBase {
42
42
  /** Full ref names (`refs/heads/main`, `refs/remotes/origin/main`, `refs/remotes/origin/HEAD`) whose movement can change this answer, given with or without a base so a watcher also learns when one appears: the branch, every candidate compared up to the chosen one, their tracking refs, the default-branch symrefs and the `<remote>/main`/`<remote>/master` refs when the default was consulted, the `upstream` remote's copy of the created-from or default branch when it was compared, and the branch's upstream when it is the default branch. Lets a watcher recompute only when one of them moves. */
43
43
  readonly dependsOn: readonly string[];
44
44
  }
45
+ /** A ref {@link GitPushTarget} names, with its commit from the cached ref list */
46
+ export interface GitPushTargetRef {
47
+ /** The full ref name: `refs/remotes/origin/main`, or `refs/heads/main` for an upstream that is a local branch */
48
+ readonly ref: string;
49
+ /** The commit {@link ref} points at; `undefined` when no such ref exists (never pushed or fetched, or gone) */
50
+ readonly sha: string | undefined;
51
+ }
52
+ /** Where a branch pushes to and what it tracks (see `getPushTarget`) */
53
+ export type GitPushTarget = {
54
+ readonly known: true;
55
+ /**
56
+ * The push destination as git names `<branch>@{push}`; `undefined` when git can't name one. Not always
57
+ * where `git push` goes: under `simple` with a push remote other than the upstream's, `git push` pushes
58
+ * like `current` while `@{push}` names nothing, and under `matching` it names a destination for a branch
59
+ * the remote doesn't have, which `git push` skips
60
+ */
61
+ readonly push: GitPushTargetRef | undefined;
62
+ /** The branch's upstream, as git names `<branch>@{upstream}`; `undefined` when it has none */
63
+ readonly upstream: GitPushTargetRef | undefined;
64
+ } | {
65
+ /**
66
+ * The ref list, or the checked-out branch, couldn't be read, so nothing can be said either way. A repository
67
+ * with no refs at all (no commit yet) reads as an empty list too, so it is unknown as well
68
+ */
69
+ readonly known: false;
70
+ };
45
71
  export type MergeDetectionConfidence = 'highest' | 'high' | 'medium';
46
72
  export type GitBranchMergedStatus = {
47
73
  merged: false;
@@ -100,6 +126,17 @@ export interface GitBranchesSubProvider {
100
126
  /** @internal not intended to be used outside of the sub-providers */
101
127
  getCurrentBranchReference?(repoPath: string, cancellation?: AbortSignal): Promise<GitBranchReference | undefined>;
102
128
  getLocalBranchByUpstream?(repoPath: string, remoteBranchName: string, cancellation?: AbortSignal): Promise<GitBranch | undefined>;
129
+ /**
130
+ * Where `branch` (a local branch's short or full name; the branch checked out at `repoPath` when omitted) pushes to,
131
+ * and its upstream, both read from the cached ref list, so a warm call runs no git. The destination is git's own
132
+ * answer (`%(push)`, which follows `push.default`, `remote.pushDefault`, `branch.<name>.pushRemote`, push refspecs
133
+ * and mirror remotes); it is `undefined` when git can't name a single destination: no upstream under `simple` or
134
+ * `upstream`, `push.default=nothing`, `simple` with a differently named upstream or a different push remote, or a
135
+ * destination with no local tracking ref. A destination or upstream is named even when its ref is missing, with an
136
+ * `undefined` `sha`. A detached HEAD, a remote-tracking ref and a branch not in the list have neither. `known` is
137
+ * `false` when the ref list, or the checked-out branch, couldn't be read.
138
+ */
139
+ getPushTarget?(repoPath: string, branch?: string, cancellation?: AbortSignal): Promise<GitPushTarget>;
103
140
  getPotentialApplyConflicts?(repoPath: string, targetBranch: string, shas: string[], options?: {
104
141
  stopOnFirstConflict?: boolean;
105
142
  }, cancellation?: AbortSignal): Promise<ConflictDetectionResult>;
@@ -136,6 +173,12 @@ export interface GitBranchesSubProvider {
136
173
  /** A target to try before every other source */
137
174
  preferredTarget?: string;
138
175
  }, cancellation?: AbortSignal): Promise<AheadOfBase>;
176
+ /**
177
+ * An opaque snapshot of the Git config inputs to `getAheadOfBase`, excluding the stored gk merge targets and
178
+ * base. Equal snapshots let a summary keep its base while its HEAD, stored values and dependent refs are unchanged.
179
+ * `undefined` means the inputs could not be read; providers without this operation require a full base read.
180
+ */
181
+ getAheadOfBaseConfigKey?(repoPath: string, cancellation?: AbortSignal): Promise<string | undefined>;
139
182
  /**
140
183
  * How far each branch has diverged from `base`, keyed by full ref name (`refs/heads/x`, plus `refs/remotes/<remote>/x`
141
184
  * when `options.remotes` is set) so a local and a remote name can't collide. Symbolic refs such as
@@ -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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"}
@@ -5,13 +5,20 @@ export type RefChangeKind = 'heads' | 'remotes' | 'tags' | 'stash';
5
5
  export interface WatcherRefChanges {
6
6
  /** Full ref names whose files changed (`refs/heads/main`), plus per-worktree pseudo-refs (`HEAD`, `ORIG_HEAD`). A name may be a ref directory and then covers every ref beneath it. */
7
7
  readonly names?: Iterable<string>;
8
- /** Ref kinds that changed without a name (packed-refs rewrite, config, shallow/replace, an injected change): any ref of that kind may have moved. */
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
  /**
13
18
  * The kind of ref a full ref name (or ref directory) belongs to. A pseudo-ref (no `refs/` prefix, e.g. `HEAD`,
14
- * `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind.
19
+ * `ORIG_HEAD`) counts as `heads`; a ref outside the watched namespaces (e.g. `refs/notes/x`) has no kind, and neither
20
+ * has a namespace directory itself (`refs/remotes`), so a change reported for one names no ref: any ref of the kind it
21
+ * holds may have moved.
15
22
  */
16
23
  export declare function getRefChangeKind(ref: string): RefChangeKind | undefined;
17
24
  /**
@@ -28,6 +35,7 @@ export declare class WatcherRepoChangeEvent {
28
35
  private readonly _refs;
29
36
  private readonly _unnamedRefKinds;
30
37
  private readonly _announced;
38
+ private readonly _configBranches;
31
39
  constructor(
32
40
  /** The repository path this event is for */
33
41
  repoPath: string, changes: RepositoryChange[],
@@ -48,6 +56,23 @@ export declare class WatcherRepoChangeEvent {
48
56
  get refs(): ReadonlySet<string>;
49
57
  /** Ref kinds that changed without a name — any ref of these kinds may have moved */
50
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;
51
76
  toString(changesOnly?: boolean): string;
52
77
  /**
53
78
  * Test whether the given ref may have moved in this event.
@@ -77,7 +102,8 @@ export declare class WatcherRepoChangeEvent {
77
102
  changedExclusive(...affected: RepositoryChange[]): boolean;
78
103
  /**
79
104
  * Coalesce: return a new event with all changes and refs from both this and the given set. When `refs` is omitted,
80
- * 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.
81
107
  */
82
108
  with(changes: RepositoryChange[], refs?: WatcherRefChanges): WatcherRepoChangeEvent;
83
109
  }