@gitkraken/core-gitlens 0.16.4 → 0.17.0

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 (90) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/dist/git/cache.d.ts +8 -2
  3. package/dist/git/cache.js +15 -6
  4. package/dist/git/cache.js.map +1 -1
  5. package/dist/git/models/issue.d.ts +20 -1
  6. package/dist/git/models/issue.js +3 -1
  7. package/dist/git/models/issue.js.map +1 -1
  8. package/dist/git/models/repository.d.ts +4 -2
  9. package/dist/git/models/repository.js +1 -0
  10. package/dist/git/models/repository.js.map +1 -1
  11. package/dist/git/models/worktree.d.ts +5 -1
  12. package/dist/git/models/worktree.js +5 -3
  13. package/dist/git/models/worktree.js.map +1 -1
  14. package/dist/git/providers/provider.d.ts +17 -0
  15. package/dist/git/providers/worktrees.d.ts +17 -2
  16. package/dist/git/run.types.d.ts +7 -0
  17. package/dist/git/service.d.ts +5 -2
  18. package/dist/git/service.js +11 -5
  19. package/dist/git/service.js.map +1 -1
  20. package/dist/git/utils/issue.utils.js +5 -1
  21. package/dist/git/utils/issue.utils.js.map +1 -1
  22. package/dist/git/watching/classifyChange.d.ts +4 -3
  23. package/dist/git/watching/classifyChange.js +7 -4
  24. package/dist/git/watching/classifyChange.js.map +1 -1
  25. package/dist/git/watching/provider.d.ts +6 -0
  26. package/dist/git/watching/watchGroup.d.ts +142 -3
  27. package/dist/git/watching/watchGroup.js +424 -19
  28. package/dist/git/watching/watchGroup.js.map +1 -1
  29. package/dist/git/watching/watchService.d.ts +25 -0
  30. package/dist/git/watching/watchService.js +27 -7
  31. package/dist/git/watching/watchService.js.map +1 -1
  32. package/dist/git/watching/watchSession.d.ts +13 -8
  33. package/dist/git/watching/watchSession.js +50 -10
  34. package/dist/git/watching/watchSession.js.map +1 -1
  35. package/dist/git/watching/watcherPatterns.d.ts +14 -0
  36. package/dist/git/watching/watcherPatterns.js +38 -1
  37. package/dist/git/watching/watcherPatterns.js.map +1 -1
  38. package/dist/git/watching/worktreeSummary.d.ts +64 -2
  39. package/dist/git/watching/worktreeSummary.js +326 -13
  40. package/dist/git/watching/worktreeSummary.js.map +1 -1
  41. package/dist/git-cli/cliGitProvider.d.ts +26 -0
  42. package/dist/git-cli/cliGitProvider.js +66 -5
  43. package/dist/git-cli/cliGitProvider.js.map +1 -1
  44. package/dist/git-cli/exec/commandClassifier.d.ts +9 -3
  45. package/dist/git-cli/exec/commandClassifier.js +98 -17
  46. package/dist/git-cli/exec/commandClassifier.js.map +1 -1
  47. package/dist/git-cli/exec/git.d.ts +20 -0
  48. package/dist/git-cli/exec/git.js +69 -7
  49. package/dist/git-cli/exec/git.js.map +1 -1
  50. package/dist/git-cli/exec/headState.d.ts +5 -0
  51. package/dist/git-cli/exec/headState.js +14 -1
  52. package/dist/git-cli/exec/headState.js.map +1 -1
  53. package/dist/git-cli/parsers/worktreeParser.js +2 -2
  54. package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
  55. package/dist/git-cli/providers/branches.js +8 -4
  56. package/dist/git-cli/providers/branches.js.map +1 -1
  57. package/dist/git-cli/providers/graph.js +1 -1
  58. package/dist/git-cli/providers/graph.js.map +1 -1
  59. package/dist/git-cli/providers/operations.d.ts +3 -0
  60. package/dist/git-cli/providers/operations.js +14 -5
  61. package/dist/git-cli/providers/operations.js.map +1 -1
  62. package/dist/git-cli/providers/worktrees.d.ts +25 -3
  63. package/dist/git-cli/providers/worktrees.js +125 -32
  64. package/dist/git-cli/providers/worktrees.js.map +1 -1
  65. package/dist/git-cli/service.d.ts +2 -0
  66. package/dist/git-cli/service.js +4 -2
  67. package/dist/git-cli/service.js.map +1 -1
  68. package/dist/plus/integrations/models/gitHostIntegration.d.ts +13 -0
  69. package/dist/plus/integrations/models/gitHostIntegration.js +20 -3
  70. package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
  71. package/dist/plus/integrations/models/integration.d.ts +12 -0
  72. package/dist/plus/integrations/models/integration.js +7 -1
  73. package/dist/plus/integrations/models/integration.js.map +1 -1
  74. package/dist/plus/integrations/providers/azure/azure.d.ts +13 -0
  75. package/dist/plus/integrations/providers/azure/azure.js +11 -3
  76. package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
  77. package/dist/plus/integrations/providers/azure/models.d.ts +3 -1
  78. package/dist/plus/integrations/providers/azure/models.js +11 -5
  79. package/dist/plus/integrations/providers/azure/models.js.map +1 -1
  80. package/dist/plus/integrations/providers/azureDevOps.d.ts +33 -5
  81. package/dist/plus/integrations/providers/azureDevOps.js +75 -11
  82. package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
  83. package/dist/plus/integrations/providers/azureDevOpsServer.js +1 -0
  84. package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
  85. package/dist/plus/integrations/providers/models.d.ts +19 -0
  86. package/dist/plus/integrations/providers/models.js +12 -3
  87. package/dist/plus/integrations/providers/models.js.map +1 -1
  88. package/dist/plus/integrations/reads/etag.js +6 -1
  89. package/dist/plus/integrations/reads/etag.js.map +1 -1
  90. package/package.json +1 -1
@@ -196,6 +196,18 @@ export interface IssueShape extends IssueOrPullRequest {
196
196
  iterations?: IssueIteration[];
197
197
  /** The read that produced this row. Absent means unknown: a legacy row, or a provider or read not yet tagged. */
198
198
  readonly projection?: IssueProjection;
199
+ /**
200
+ * The current account as this issue's members identify it, present only where that can differ from the account
201
+ * `getCurrentAccount` returns: Azure DevOps can know one person by another id in each organization (Azure DevOps
202
+ * Server: collection) than the account's, and a work item's {@link author} and {@link assignees} carry the
203
+ * organization's. A consumer matching them to the current user compares against this when present, and against
204
+ * the account otherwise. Absent on every other provider, and on an Azure DevOps row whose organization's identity
205
+ * couldn't be read.
206
+ */
207
+ readonly viewer?: {
208
+ readonly id: string;
209
+ readonly username?: string;
210
+ };
199
211
  }
200
212
  export declare class Issue implements IssueShape {
201
213
  readonly provider: ProviderReference;
@@ -223,8 +235,15 @@ export declare class Issue implements IssueShape {
223
235
  readonly iterations?: IssueIteration[] | undefined;
224
236
  readonly projection?: IssueProjection | undefined;
225
237
  readonly bodyField?: IssueBodyField | undefined;
238
+ readonly viewer?: {
239
+ readonly id: string;
240
+ readonly username?: string;
241
+ } | undefined;
226
242
  readonly type = "issue";
227
- constructor(provider: ProviderReference, id: string, nodeId: string | undefined, title: string, url: string, createdDate: Date, updatedDate: Date, closed: boolean, state: IssueOrPullRequestState | undefined, author: IssueMember | undefined, assignees: IssueMember[], repository?: IssueRepository | undefined, closedDate?: Date | undefined, labels?: IssueLabel[] | undefined, commentsCount?: number | undefined, thumbsUpCount?: number | undefined, body?: string | undefined, project?: IssueProject | undefined, number?: string | undefined, issueType?: string | undefined, providerState?: IssueProviderState | undefined, bodyFormat?: IssueBodyFormat | undefined, iterations?: IssueIteration[] | undefined, projection?: IssueProjection | undefined, bodyField?: IssueBodyField | undefined);
243
+ constructor(provider: ProviderReference, id: string, nodeId: string | undefined, title: string, url: string, createdDate: Date, updatedDate: Date, closed: boolean, state: IssueOrPullRequestState | undefined, author: IssueMember | undefined, assignees: IssueMember[], repository?: IssueRepository | undefined, closedDate?: Date | undefined, labels?: IssueLabel[] | undefined, commentsCount?: number | undefined, thumbsUpCount?: number | undefined, body?: string | undefined, project?: IssueProject | undefined, number?: string | undefined, issueType?: string | undefined, providerState?: IssueProviderState | undefined, bodyFormat?: IssueBodyFormat | undefined, iterations?: IssueIteration[] | undefined, projection?: IssueProjection | undefined, bodyField?: IssueBodyField | undefined, viewer?: {
244
+ readonly id: string;
245
+ readonly username?: string;
246
+ } | undefined);
228
247
  static is(issue: unknown): issue is Issue;
229
248
  }
230
249
  export type IssueBodyFormat = 'markdown' | 'jira-wiki';
@@ -45,8 +45,9 @@ let Issue = _Issue = class Issue {
45
45
  iterations;
46
46
  projection;
47
47
  bodyField;
48
+ viewer;
48
49
  type = "issue";
49
- constructor(provider, id, nodeId, title, url, createdDate, updatedDate, closed, state, author, assignees, repository, closedDate, labels, commentsCount, thumbsUpCount, body, project, number, issueType, providerState, bodyFormat, iterations, projection, bodyField) {
50
+ constructor(provider, id, nodeId, title, url, createdDate, updatedDate, closed, state, author, assignees, repository, closedDate, labels, commentsCount, thumbsUpCount, body, project, number, issueType, providerState, bodyFormat, iterations, projection, bodyField, viewer) {
50
51
  this.provider = provider;
51
52
  this.id = id;
52
53
  this.nodeId = nodeId;
@@ -72,6 +73,7 @@ let Issue = _Issue = class Issue {
72
73
  this.iterations = iterations;
73
74
  this.projection = projection;
74
75
  this.bodyField = bodyField;
76
+ this.viewer = viewer;
75
77
  }
76
78
  static is(issue) {
77
79
  return issue instanceof _Issue;
@@ -1 +1 @@
1
- {"version":3,"file":"issue.js","names":[],"sources":["../../../../git/src/models/issue.ts"],"sourcesContent":["import { loggable } from '@gitlens/utils/decorators/log.js';\nimport type { RequireSome, RequireSomeWithProps } from '@gitlens/utils/types.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestState } from './issueOrPullRequest.js';\nimport type { ProviderReference } from './remoteProvider.js';\nimport type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';\n\n/** Selects which issue states a read should include. `all` covers open + closed. */\nexport type IssueStateFilter = 'open' | 'closed' | 'all';\n\n/**\n * One relationship a filtered issue search can constrain on.\n *\n * The last two are USER-INDEPENDENT — they describe the issue, not the caller — and neither is a SCOPE: either\n * one alone matches every such issue on the host, so a search using one still needs a repository/org scope.\n */\nexport type IssueSearchRelationship =\n\t/** Authored by the current user. */\n\t| 'authored'\n\t/** Assigned to the current user. */\n\t| 'assigned'\n\t/** Mentions the current user. */\n\t| 'mentioned'\n\t/** Assigned to ANYONE. Excludes unassigned issues — see {@link IssueSearchCriteria.relationships}. */\n\t| 'any-assignee'\n\t/** Assigned to nobody. The complement of `any-assignee`, not a broadening of it. */\n\t| 'unassigned';\n\n/**\n * A field an issue read can be ordered by, provider-neutral. No provider expresses all of them: each read\n * validates the requested key against the capability its provider declares and refuses one it can't express\n * server-side, rather than serving a differently-ordered list under the name of the one that was asked for.\n *\n * `closed` and `resolved` are deliberately SEPARATE, not two names for the same date. Azure DevOps tracks both\n * and they differ (an issue can be closed without being resolved and resolved without being closed), and Jira\n * has no close date at all — it models the equivalent as `resolutiondate`. Collapsing them would force one of\n * those two providers to lie about which date it ordered by.\n */\nexport type IssueSortField =\n\t/** Creation date. */\n\t| 'created'\n\t/** Last activity. */\n\t| 'updated'\n\t/** Close date. Only where the provider orders by it server-side — notably NOT GitHub. */\n\t| 'closed'\n\t/** Resolution date (Jira `resolutiondate`, Azure `ResolvedDate`). Distinct from `closed`. */\n\t| 'resolved'\n\t/** Number of comments. */\n\t| 'comments'\n\t/** Reactions/upvotes. */\n\t| 'reactions'\n\t/** The provider's own priority field. */\n\t| 'priority'\n\t/** Due date. */\n\t| 'dueDate'\n\t/** Alphabetical by title. */\n\t| 'title';\n\n/**\n * How an issue read is ordered, as `field:direction` — the same shape `BranchSorting`/`TagSorting` already use,\n * so it is one serializable string a consumer can persist, compare, or bind straight to a setting or a dropdown.\n */\nexport type IssueSorting = `${IssueSortField}:asc` | `${IssueSortField}:desc`;\n\n/**\n * The order every issue read that HAS a default applies when the caller asks for none: most recently updated\n * first, which is what those reads served before ordering was an option.\n *\n * One definition rather than one per layer, because it is a promise made in several places at once — the criteria\n * model documents it, the GitHub query emits it, and the result-ceiling warning quotes it — and three copies would\n * be free to disagree about what \"the default\" is.\n *\n * Note it is not a default every read has, and the two layers differ. The API client's account-wide \"my issues\"\n * search (`GitHubApi.searchMyIssues`) never requested an order, so a direct caller omitting `sort` still gets\n * GitHub's relevance order — the default is opt-in there, to keep that read's already-shipped results unchanged.\n * The FACADE does apply it: GitHub declares `updated:desc` among its account-wide keys, so\n * `readAccountWideIssuesPage` resolves the omission to this default and forwards it, and that read's emitted query\n * gained a `sort:updated` qualifier it did not have before. A read whose surface cannot express even this key is\n * the only one the facade leaves in the provider's own order.\n */\nexport const defaultIssueSort: IssueSorting = 'updated:desc';\n\n/**\n * What a filtered issue search narrows on: a provider-neutral criteria set, translated to each provider's own\n * query language by its integration.\n *\n * Structured rather than a provider query string so a consumer can render filter chips and hide the ones a\n * provider can't express, and so the same criteria mean the same thing on a provider whose query language looks\n * nothing like GitHub's search syntax. Every field is validated against the provider's declared capability\n * BEFORE the read runs, all-or-nothing: a criterion the provider can't express server-side refuses the whole\n * read rather than serving a list that was never narrowed. Client-side narrowing is not an option — the dropped\n * items still counted toward the page and cursor the provider produced, so a post-filter would leave the items\n * describing a different result set than the paging that came with them.\n */\nexport interface IssueSearchCriteria {\n\t/**\n\t * Relationships to constrain on, OR-ed together (each becomes its own provider query, unioned and deduped).\n\t * Omitted means NO relationship constraint at all — every issue in scope, assigned or not — which requires a\n\t * repository/org scope.\n\t *\n\t * `any-assignee` and `unassigned` partition the scope between them and are mutually exclusive; passing both is\n\t * refused rather than silently dropping one. Note that \"all visible issues\" is the OMITTED case, NOT\n\t * `any-assignee`, which excludes unassigned issues.\n\t */\n\trelationships?: IssueSearchRelationship[];\n\t/**\n\t * Free text, matched by the provider's own relevance rules — NOT a substring match, and not a way to smuggle\n\t * qualifiers: a token that would read as one is dropped, as are quotes and control characters, so text can\n\t * never re-scope the search. The structured criteria are the qualifier channel.\n\t */\n\ttext?: string;\n\t/** Issue states to include. Omitted reads open issues only. */\n\tstate?: IssueStateFilter;\n\t/** Labels the issue must carry — AND-ed, matching GitHub's own `label:` semantics. */\n\tlabels?: string[];\n\tmilestone?: string;\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/** Issues with no linked pull request. */\n\twithoutLinkedPullRequest?: boolean;\n\t/** Includes issues in archived repositories, which are excluded by default. */\n\tincludeArchived?: 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 like every other criterion — all-or-nothing against {@link IssueSearchCapabilities.sorts}: a key\n\t * the provider can't express server-side refuses the WHOLE read rather than falling back to the default.\n\t * Serving another order is not a smaller error than serving a wider result: combined with the provider's\n\t * result ceiling it returns a different subset than was asked for, and the paging that comes with it describes\n\t * that other subset.\n\t *\n\t * Do not change it mid-pagination. A cursor carries the sort it was produced under, and threading it under a\n\t * different key is refused rather than serving a sequence with gaps and repeats: drop the cursor instead.\n\t */\n\tsort?: IssueSorting;\n}\n\n/**\n * Which {@link IssueSearchCriteria} fields a provider's filtered issue search can express server-side.\n *\n * An empty `relationships` with every flag false means the provider has NO filtered issue search — the read is\n * refused outright, so a consumer should offer neither the surface nor its chips.\n */\nexport interface IssueSearchCapabilities {\n\t/** Relationships the search can constrain on. Empty means the search itself is unsupported. */\n\trelationships: IssueSearchRelationship[];\n\ttext: boolean;\n\tlabels: boolean;\n\tmilestone: boolean;\n\tupdatedAfter: boolean;\n\tcreatedAfter: boolean;\n\twithoutLinkedPullRequest: boolean;\n\t/** Whether {@link IssueSearchCriteria.state} can select anything other than the provider's default (open). */\n\tstates: boolean;\n\t/**\n\t * Sort keys the search can express IN SERVER. Always contains at least `updated:desc` — the historical default\n\t * — when the search exists at all, so it is never empty for a usable surface; a provider without a filtered\n\t * search reports an empty `relationships`, which is already the signal that there is no surface to order.\n\t */\n\tsorts: IssueSorting[];\n}\n\n/**\n * The read that produced an issue row, as a key for `getIssueFieldPresence` (`@gitlens/integrations`). It names a\n * READ, not a field set: the same tag can carry different fields on different providers.\n */\nexport type IssueProjection =\n\t/** A single-item read. */\n\t| 'point'\n\t/** The filtered search. */\n\t| 'search'\n\t/** The account-wide list, and the trackers' \"my issues\". */\n\t| 'account'\n\t/** A repository-scoped list. */\n\t| 'repos'\n\t/** A tracker project's issues (`listIssueTrackerIssuesPage`, `getIssuesForProject`). */\n\t| 'project'\n\t/** The batch read. */\n\t| 'batch';\n\nexport interface IssueShape extends IssueOrPullRequest {\n\t/** `undefined` when the provider can't resolve the author, e.g. a deleted GitHub account */\n\tauthor: IssueMember | undefined;\n\tassignees: IssueMember[];\n\t/** The provider's own workflow state, when it exposes one beyond the normalized open/closed state. */\n\tproviderState?: IssueProviderState;\n\trepository?: IssueRepository;\n\tlabels?: IssueLabel[];\n\tbody?: string;\n\t/**\n\t * The syntax used by `body`. When omitted, consumers should treat `body` as Markdown — except that Azure DevOps\n\t * work items and Bitbucket Cloud issues carry HTML with no `bodyFormat` set.\n\t */\n\tbodyFormat?: IssueBodyFormat;\n\t/**\n\t * The provider field `body` was read from, for providers whose issues keep it in different fields: an Azure\n\t * DevOps work item's type decides it (Description, Repro Steps on a Bug, a custom field on an inherited process).\n\t * Absent when the provider has a single body field, or there is no body.\n\t */\n\tbodyField?: IssueBodyField;\n\tproject?: IssueProject;\n\tissueType?: string;\n\t/** An issue can belong to several Jira sprints; Azure reports one iteration. */\n\titerations?: IssueIteration[];\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?: IssueProjection;\n}\n\n@loggable(i => i.id)\nexport class Issue implements IssueShape {\n\treadonly type = 'issue';\n\n\tconstructor(\n\t\tpublic readonly provider: ProviderReference,\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 createdDate: Date,\n\t\tpublic readonly updatedDate: Date,\n\t\tpublic readonly closed: boolean,\n\t\tpublic readonly state: IssueOrPullRequestState | undefined,\n\t\tpublic readonly author: IssueMember | undefined,\n\t\tpublic readonly assignees: IssueMember[],\n\t\tpublic readonly repository?: IssueRepository,\n\t\tpublic readonly closedDate?: Date,\n\t\tpublic readonly labels?: IssueLabel[],\n\t\tpublic readonly commentsCount?: number,\n\t\tpublic readonly thumbsUpCount?: number,\n\t\tpublic readonly body?: string,\n\t\tpublic readonly project?: IssueProject,\n\t\tpublic readonly number?: string,\n\t\tpublic readonly issueType?: string,\n\t\tpublic readonly providerState?: IssueProviderState,\n\t\tpublic readonly bodyFormat?: IssueBodyFormat,\n\t\tpublic readonly iterations?: IssueIteration[],\n\t\tpublic readonly projection?: IssueProjection,\n\t\tpublic readonly bodyField?: IssueBodyField,\n\t) {}\n\n\tstatic is(issue: unknown): issue is Issue {\n\t\treturn issue instanceof Issue;\n\t}\n}\n\nexport type IssueBodyFormat = 'markdown' | 'jira-wiki';\n\nexport interface IssueBodyField {\n\t/** The provider's id for the field, e.g. `Microsoft.VSTS.TCM.ReproSteps`. */\n\treadonly id: string;\n\t/** The field's display name, e.g. `Repro Steps`. */\n\treadonly name: string;\n}\n\nexport const enum RepositoryAccessLevel {\n\tAdmin = 100,\n\tMaintain = 40,\n\tWrite = 30,\n\tTriage = 20,\n\tRead = 10,\n\tNone = 0,\n}\n\nexport interface IssueLabel {\n\tcolor?: string;\n\tname: string;\n}\n\n/**\n * Sprint metadata is optional because some providers only report an iteration path. An absent `isActive`\n * means unknown, not inactive, so consumers must not use it to exclude those iterations by default.\n */\nexport interface IssueIteration {\n\t/**\n\t * A sprint id on Jira, the verbatim iteration path on Azure — so it is only meaningful within the provider's\n\t * project, never across providers. Unique at read time but NOT durable: Azure rewrites the path when an\n\t * iteration node is renamed or re-parented, so don't persist it as a long-lived id.\n\t */\n\tid: string;\n\tname: string;\n\tisActive?: boolean;\n\tstartDate?: Date;\n\tendDate?: Date;\n}\n\nexport interface IssueMember {\n\tid: string;\n\t/**\n\t * Absent when the provider exposes no display name for the member. Optional on purpose: a fallback string\n\t * invented here (`'unknown'`, `''`) can't be told apart from a real name downstream, so it can't be undone by a\n\t * consumer that needs to render something else — or nothing at all, as when the member feeds an AI prompt.\n\t * Each consumer picks its own 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\nexport type IssueProviderState = {\n\tid?: string;\n\tname: string;\n\tcolor?: string;\n\t/** The provider's workflow category. `CANCELED` is abandoned work (Linear's canceled and duplicate states, Azure's\n\t * `Removed` category): closed like `DONE`, kept apart so a consumer can tell the two outcomes apart. */\n\tcategory?: 'TO_DO' | 'IN_PROGRESS' | 'DONE' | 'CANCELED';\n};\n\nexport interface IssueProject {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface IssueRepository {\n\towner: string;\n\trepo: string;\n\taccessLevel?: RepositoryAccessLevel;\n\turl?: string;\n\tid?: string;\n}\n\nexport type IssueRepositoryIdentityDescriptor = 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":";;;;;;;;;;;;;;;;;;;;AA+EA,MAAa,mBAAiC;AAmIvC,IAAM,QAAA,SAAN,MAAM,MAA4B;CAIvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CA3BjB,OAAgB;CAEhB,YACC,UACA,IACA,QACA,OACA,KACA,aACA,aACA,QACA,OACA,QACA,WACA,YACA,YACA,QACA,eACA,eACA,MACA,SACA,QACA,WACA,eACA,YACA,YACA,YACA,WACC;EAzBe,KAAA,WAAA;EACA,KAAA,KAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EACA,KAAA,cAAA;EACA,KAAA,cAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,SAAA;EACA,KAAA,gBAAA;EACA,KAAA,gBAAA;EACA,KAAA,OAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,gBAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,YAAA;CACd;CAEH,OAAO,GAAG,OAAgC;EACzC,OAAO,iBAAA;CACR;AACD;AAnCC,QAAA,SAAA,WAAA,CAAA,UAAS,MAAK,EAAE,EAAE,CAAA,GAAA,KAAA;AA8CnB,IAAkB,wBAAX,yBAAA,uBAAA;CACN,sBAAA,sBAAA,WAAA,OAAA;CACA,sBAAA,sBAAA,cAAA,MAAA;CACA,sBAAA,sBAAA,WAAA,MAAA;CACA,sBAAA,sBAAA,YAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,KAAA;;AACD,EAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"issue.js","names":[],"sources":["../../../../git/src/models/issue.ts"],"sourcesContent":["import { loggable } from '@gitlens/utils/decorators/log.js';\nimport type { RequireSome, RequireSomeWithProps } from '@gitlens/utils/types.js';\nimport type { IssueOrPullRequest, IssueOrPullRequestState } from './issueOrPullRequest.js';\nimport type { ProviderReference } from './remoteProvider.js';\nimport type { RepositoryIdentityDescriptor } from './repositoryIdentities.js';\n\n/** Selects which issue states a read should include. `all` covers open + closed. */\nexport type IssueStateFilter = 'open' | 'closed' | 'all';\n\n/**\n * One relationship a filtered issue search can constrain on.\n *\n * The last two are USER-INDEPENDENT — they describe the issue, not the caller — and neither is a SCOPE: either\n * one alone matches every such issue on the host, so a search using one still needs a repository/org scope.\n */\nexport type IssueSearchRelationship =\n\t/** Authored by the current user. */\n\t| 'authored'\n\t/** Assigned to the current user. */\n\t| 'assigned'\n\t/** Mentions the current user. */\n\t| 'mentioned'\n\t/** Assigned to ANYONE. Excludes unassigned issues — see {@link IssueSearchCriteria.relationships}. */\n\t| 'any-assignee'\n\t/** Assigned to nobody. The complement of `any-assignee`, not a broadening of it. */\n\t| 'unassigned';\n\n/**\n * A field an issue read can be ordered by, provider-neutral. No provider expresses all of them: each read\n * validates the requested key against the capability its provider declares and refuses one it can't express\n * server-side, rather than serving a differently-ordered list under the name of the one that was asked for.\n *\n * `closed` and `resolved` are deliberately SEPARATE, not two names for the same date. Azure DevOps tracks both\n * and they differ (an issue can be closed without being resolved and resolved without being closed), and Jira\n * has no close date at all — it models the equivalent as `resolutiondate`. Collapsing them would force one of\n * those two providers to lie about which date it ordered by.\n */\nexport type IssueSortField =\n\t/** Creation date. */\n\t| 'created'\n\t/** Last activity. */\n\t| 'updated'\n\t/** Close date. Only where the provider orders by it server-side — notably NOT GitHub. */\n\t| 'closed'\n\t/** Resolution date (Jira `resolutiondate`, Azure `ResolvedDate`). Distinct from `closed`. */\n\t| 'resolved'\n\t/** Number of comments. */\n\t| 'comments'\n\t/** Reactions/upvotes. */\n\t| 'reactions'\n\t/** The provider's own priority field. */\n\t| 'priority'\n\t/** Due date. */\n\t| 'dueDate'\n\t/** Alphabetical by title. */\n\t| 'title';\n\n/**\n * How an issue read is ordered, as `field:direction` — the same shape `BranchSorting`/`TagSorting` already use,\n * so it is one serializable string a consumer can persist, compare, or bind straight to a setting or a dropdown.\n */\nexport type IssueSorting = `${IssueSortField}:asc` | `${IssueSortField}:desc`;\n\n/**\n * The order every issue read that HAS a default applies when the caller asks for none: most recently updated\n * first, which is what those reads served before ordering was an option.\n *\n * One definition rather than one per layer, because it is a promise made in several places at once — the criteria\n * model documents it, the GitHub query emits it, and the result-ceiling warning quotes it — and three copies would\n * be free to disagree about what \"the default\" is.\n *\n * Note it is not a default every read has, and the two layers differ. The API client's account-wide \"my issues\"\n * search (`GitHubApi.searchMyIssues`) never requested an order, so a direct caller omitting `sort` still gets\n * GitHub's relevance order — the default is opt-in there, to keep that read's already-shipped results unchanged.\n * The FACADE does apply it: GitHub declares `updated:desc` among its account-wide keys, so\n * `readAccountWideIssuesPage` resolves the omission to this default and forwards it, and that read's emitted query\n * gained a `sort:updated` qualifier it did not have before. A read whose surface cannot express even this key is\n * the only one the facade leaves in the provider's own order.\n */\nexport const defaultIssueSort: IssueSorting = 'updated:desc';\n\n/**\n * What a filtered issue search narrows on: a provider-neutral criteria set, translated to each provider's own\n * query language by its integration.\n *\n * Structured rather than a provider query string so a consumer can render filter chips and hide the ones a\n * provider can't express, and so the same criteria mean the same thing on a provider whose query language looks\n * nothing like GitHub's search syntax. Every field is validated against the provider's declared capability\n * BEFORE the read runs, all-or-nothing: a criterion the provider can't express server-side refuses the whole\n * read rather than serving a list that was never narrowed. Client-side narrowing is not an option — the dropped\n * items still counted toward the page and cursor the provider produced, so a post-filter would leave the items\n * describing a different result set than the paging that came with them.\n */\nexport interface IssueSearchCriteria {\n\t/**\n\t * Relationships to constrain on, OR-ed together (each becomes its own provider query, unioned and deduped).\n\t * Omitted means NO relationship constraint at all — every issue in scope, assigned or not — which requires a\n\t * repository/org scope.\n\t *\n\t * `any-assignee` and `unassigned` partition the scope between them and are mutually exclusive; passing both is\n\t * refused rather than silently dropping one. Note that \"all visible issues\" is the OMITTED case, NOT\n\t * `any-assignee`, which excludes unassigned issues.\n\t */\n\trelationships?: IssueSearchRelationship[];\n\t/**\n\t * Free text, matched by the provider's own relevance rules — NOT a substring match, and not a way to smuggle\n\t * qualifiers: a token that would read as one is dropped, as are quotes and control characters, so text can\n\t * never re-scope the search. The structured criteria are the qualifier channel.\n\t */\n\ttext?: string;\n\t/** Issue states to include. Omitted reads open issues only. */\n\tstate?: IssueStateFilter;\n\t/** Labels the issue must carry — AND-ed, matching GitHub's own `label:` semantics. */\n\tlabels?: string[];\n\tmilestone?: string;\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/** Issues with no linked pull request. */\n\twithoutLinkedPullRequest?: boolean;\n\t/** Includes issues in archived repositories, which are excluded by default. */\n\tincludeArchived?: 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 like every other criterion — all-or-nothing against {@link IssueSearchCapabilities.sorts}: a key\n\t * the provider can't express server-side refuses the WHOLE read rather than falling back to the default.\n\t * Serving another order is not a smaller error than serving a wider result: combined with the provider's\n\t * result ceiling it returns a different subset than was asked for, and the paging that comes with it describes\n\t * that other subset.\n\t *\n\t * Do not change it mid-pagination. A cursor carries the sort it was produced under, and threading it under a\n\t * different key is refused rather than serving a sequence with gaps and repeats: drop the cursor instead.\n\t */\n\tsort?: IssueSorting;\n}\n\n/**\n * Which {@link IssueSearchCriteria} fields a provider's filtered issue search can express server-side.\n *\n * An empty `relationships` with every flag false means the provider has NO filtered issue search — the read is\n * refused outright, so a consumer should offer neither the surface nor its chips.\n */\nexport interface IssueSearchCapabilities {\n\t/** Relationships the search can constrain on. Empty means the search itself is unsupported. */\n\trelationships: IssueSearchRelationship[];\n\ttext: boolean;\n\tlabels: boolean;\n\tmilestone: boolean;\n\tupdatedAfter: boolean;\n\tcreatedAfter: boolean;\n\twithoutLinkedPullRequest: boolean;\n\t/** Whether {@link IssueSearchCriteria.state} can select anything other than the provider's default (open). */\n\tstates: boolean;\n\t/**\n\t * Sort keys the search can express IN SERVER. Always contains at least `updated:desc` — the historical default\n\t * — when the search exists at all, so it is never empty for a usable surface; a provider without a filtered\n\t * search reports an empty `relationships`, which is already the signal that there is no surface to order.\n\t */\n\tsorts: IssueSorting[];\n}\n\n/**\n * The read that produced an issue row, as a key for `getIssueFieldPresence` (`@gitlens/integrations`). It names a\n * READ, not a field set: the same tag can carry different fields on different providers.\n */\nexport type IssueProjection =\n\t/** A single-item read. */\n\t| 'point'\n\t/** The filtered search. */\n\t| 'search'\n\t/** The account-wide list, and the trackers' \"my issues\". */\n\t| 'account'\n\t/** A repository-scoped list. */\n\t| 'repos'\n\t/** A tracker project's issues (`listIssueTrackerIssuesPage`, `getIssuesForProject`). */\n\t| 'project'\n\t/** The batch read. */\n\t| 'batch';\n\nexport interface IssueShape extends IssueOrPullRequest {\n\t/** `undefined` when the provider can't resolve the author, e.g. a deleted GitHub account */\n\tauthor: IssueMember | undefined;\n\tassignees: IssueMember[];\n\t/** The provider's own workflow state, when it exposes one beyond the normalized open/closed state. */\n\tproviderState?: IssueProviderState;\n\trepository?: IssueRepository;\n\tlabels?: IssueLabel[];\n\tbody?: string;\n\t/**\n\t * The syntax used by `body`. When omitted, consumers should treat `body` as Markdown — except that Azure DevOps\n\t * work items and Bitbucket Cloud issues carry HTML with no `bodyFormat` set.\n\t */\n\tbodyFormat?: IssueBodyFormat;\n\t/**\n\t * The provider field `body` was read from, for providers whose issues keep it in different fields: an Azure\n\t * DevOps work item's type decides it (Description, Repro Steps on a Bug, a custom field on an inherited process).\n\t * Absent when the provider has a single body field, or there is no body.\n\t */\n\tbodyField?: IssueBodyField;\n\tproject?: IssueProject;\n\tissueType?: string;\n\t/** An issue can belong to several Jira sprints; Azure reports one iteration. */\n\titerations?: IssueIteration[];\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?: IssueProjection;\n\t/**\n\t * The current account as this issue's members identify it, present only where that can differ from the account\n\t * `getCurrentAccount` returns: Azure DevOps can know one person by another id in each organization (Azure DevOps\n\t * Server: collection) than the account's, and a work item's {@link author} and {@link assignees} carry the\n\t * organization's. A consumer matching them to the current user compares against this when present, and against\n\t * the account otherwise. Absent on every other provider, and on an Azure DevOps row whose organization's identity\n\t * couldn't be read.\n\t */\n\treadonly viewer?: { readonly id: string; readonly username?: string };\n}\n\n@loggable(i => i.id)\nexport class Issue implements IssueShape {\n\treadonly type = 'issue';\n\n\tconstructor(\n\t\tpublic readonly provider: ProviderReference,\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 createdDate: Date,\n\t\tpublic readonly updatedDate: Date,\n\t\tpublic readonly closed: boolean,\n\t\tpublic readonly state: IssueOrPullRequestState | undefined,\n\t\tpublic readonly author: IssueMember | undefined,\n\t\tpublic readonly assignees: IssueMember[],\n\t\tpublic readonly repository?: IssueRepository,\n\t\tpublic readonly closedDate?: Date,\n\t\tpublic readonly labels?: IssueLabel[],\n\t\tpublic readonly commentsCount?: number,\n\t\tpublic readonly thumbsUpCount?: number,\n\t\tpublic readonly body?: string,\n\t\tpublic readonly project?: IssueProject,\n\t\tpublic readonly number?: string,\n\t\tpublic readonly issueType?: string,\n\t\tpublic readonly providerState?: IssueProviderState,\n\t\tpublic readonly bodyFormat?: IssueBodyFormat,\n\t\tpublic readonly iterations?: IssueIteration[],\n\t\tpublic readonly projection?: IssueProjection,\n\t\tpublic readonly bodyField?: IssueBodyField,\n\t\tpublic readonly viewer?: { readonly id: string; readonly username?: string },\n\t) {}\n\n\tstatic is(issue: unknown): issue is Issue {\n\t\treturn issue instanceof Issue;\n\t}\n}\n\nexport type IssueBodyFormat = 'markdown' | 'jira-wiki';\n\nexport interface IssueBodyField {\n\t/** The provider's id for the field, e.g. `Microsoft.VSTS.TCM.ReproSteps`. */\n\treadonly id: string;\n\t/** The field's display name, e.g. `Repro Steps`. */\n\treadonly name: string;\n}\n\nexport const enum RepositoryAccessLevel {\n\tAdmin = 100,\n\tMaintain = 40,\n\tWrite = 30,\n\tTriage = 20,\n\tRead = 10,\n\tNone = 0,\n}\n\nexport interface IssueLabel {\n\tcolor?: string;\n\tname: string;\n}\n\n/**\n * Sprint metadata is optional because some providers only report an iteration path. An absent `isActive`\n * means unknown, not inactive, so consumers must not use it to exclude those iterations by default.\n */\nexport interface IssueIteration {\n\t/**\n\t * A sprint id on Jira, the verbatim iteration path on Azure — so it is only meaningful within the provider's\n\t * project, never across providers. Unique at read time but NOT durable: Azure rewrites the path when an\n\t * iteration node is renamed or re-parented, so don't persist it as a long-lived id.\n\t */\n\tid: string;\n\tname: string;\n\tisActive?: boolean;\n\tstartDate?: Date;\n\tendDate?: Date;\n}\n\nexport interface IssueMember {\n\tid: string;\n\t/**\n\t * Absent when the provider exposes no display name for the member. Optional on purpose: a fallback string\n\t * invented here (`'unknown'`, `''`) can't be told apart from a real name downstream, so it can't be undone by a\n\t * consumer that needs to render something else — or nothing at all, as when the member feeds an AI prompt.\n\t * Each consumer picks its own 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\nexport type IssueProviderState = {\n\tid?: string;\n\tname: string;\n\tcolor?: string;\n\t/** The provider's workflow category. `CANCELED` is abandoned work (Linear's canceled and duplicate states, Azure's\n\t * `Removed` category): closed like `DONE`, kept apart so a consumer can tell the two outcomes apart. */\n\tcategory?: 'TO_DO' | 'IN_PROGRESS' | 'DONE' | 'CANCELED';\n};\n\nexport interface IssueProject {\n\tid: string;\n\tname: string;\n\tresourceId: string;\n\tresourceName: string;\n}\n\nexport interface IssueRepository {\n\towner: string;\n\trepo: string;\n\taccessLevel?: RepositoryAccessLevel;\n\turl?: string;\n\tid?: string;\n}\n\nexport type IssueRepositoryIdentityDescriptor = 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":";;;;;;;;;;;;;;;;;;;;AA+EA,MAAa,mBAAiC;AA4IvC,IAAM,QAAA,SAAN,MAAM,MAA4B;CAIvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CA5BjB,OAAgB;CAEhB,YACC,UACA,IACA,QACA,OACA,KACA,aACA,aACA,QACA,OACA,QACA,WACA,YACA,YACA,QACA,eACA,eACA,MACA,SACA,QACA,WACA,eACA,YACA,YACA,YACA,WACA,QACC;EA1Be,KAAA,WAAA;EACA,KAAA,KAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,MAAA;EACA,KAAA,cAAA;EACA,KAAA,cAAA;EACA,KAAA,SAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,SAAA;EACA,KAAA,gBAAA;EACA,KAAA,gBAAA;EACA,KAAA,OAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;EACA,KAAA,YAAA;EACA,KAAA,gBAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,aAAA;EACA,KAAA,YAAA;EACA,KAAA,SAAA;CACd;CAEH,OAAO,GAAG,OAAgC;EACzC,OAAO,iBAAA;CACR;AACD;AApCC,QAAA,SAAA,WAAA,CAAA,UAAS,MAAK,EAAE,EAAE,CAAA,GAAA,KAAA;AA+CnB,IAAkB,wBAAX,yBAAA,uBAAA;CACN,sBAAA,sBAAA,WAAA,OAAA;CACA,sBAAA,sBAAA,cAAA,MAAA;CACA,sBAAA,sBAAA,WAAA,MAAA;CACA,sBAAA,sBAAA,YAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,MAAA;CACA,sBAAA,sBAAA,UAAA,KAAA;;AACD,EAAA,CAAA,CAAA"}
@@ -14,8 +14,10 @@ export interface GitDir {
14
14
  /** The parent (superproject) directory for submodules */
15
15
  readonly parentUri?: Uri;
16
16
  }
17
- export type RepositoryChange = 'unknown' | 'index' | 'head' | 'heads' | 'tags' | 'stash' | 'remotes' | 'worktrees' | 'config' | 'pausedOp' | 'cherryPick' | 'merge' | 'rebase' | 'revert' | 'closed' | 'ignores' | 'remoteProviders' | 'starred' | 'opened' | 'gkConfig' | 'lastFetched';
18
- export declare const repositoryChanges: ("cherryPick" | "closed" | "config" | "gkConfig" | "head" | "heads" | "ignores" | "index" | "lastFetched" | "merge" | "opened" | "pausedOp" | "rebase" | "remoteProviders" | "remotes" | "revert" | "starred" | "stash" | "tags" | "unknown" | "worktrees")[];
17
+ export type RepositoryChange = 'unknown' | 'index' | 'head' | 'heads' | 'tags' | 'stash' | 'remotes' | 'worktrees' |
18
+ /** A linked worktree's HEAD moved: a checkout in a worktree whose own `.git` this session doesn't watch */
19
+ 'worktreeHead' | 'config' | 'pausedOp' | 'cherryPick' | 'merge' | 'rebase' | 'revert' | 'closed' | 'ignores' | 'remoteProviders' | 'starred' | 'opened' | 'gkConfig' | 'lastFetched';
20
+ export declare const repositoryChanges: ("cherryPick" | "closed" | "config" | "gkConfig" | "head" | "heads" | "ignores" | "index" | "lastFetched" | "merge" | "opened" | "pausedOp" | "rebase" | "remoteProviders" | "remotes" | "revert" | "starred" | "stash" | "tags" | "unknown" | "worktreeHead" | "worktrees")[];
19
21
  /**
20
22
  * The only changes {@link Repository.fireChange} accepts with `force` — delivered straight to the repository's
21
23
  * emitter, bypassing the watch session, so they still arrive while it's suspended or when there's no watch handle
@@ -21,6 +21,7 @@ const repositoryChanges = exhaustiveArray()([
21
21
  "stash",
22
22
  "remotes",
23
23
  "worktrees",
24
+ "worktreeHead",
24
25
  "config",
25
26
  "pausedOp",
26
27
  "cherryPick",
@@ -1 +1 @@
1
- {"version":3,"file":"repository.js","names":[],"sources":["../../../../git/src/models/repository.ts"],"sourcesContent":["import { exhaustiveArray } from '@gitlens/utils/array.js';\nimport { getScopedCounter } from '@gitlens/utils/counter.js';\nimport { md5 } from '@gitlens/utils/crypto.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { basename, isDescendant, normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { GitProviderDescriptor } from '../providers/types.js';\nimport { getCommonRepositoryUri } from '../utils/repository.utils.js';\nimport type { WatcherRepoChangeEvent, WorkingTreeChangeEvent } from '../watching/changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle, WatchHooks } from '../watching/watchService.js';\nimport type { RepositorySubscription } from '../watching/watchSession.js';\nimport { RepositoryChangeEvent } from './repositoryChangeEvent.js';\n\nconst instanceCounter = getScopedCounter();\n\nexport interface GitDir {\n\treadonly uri: Uri;\n\t/** The common git directory for worktrees */\n\treadonly commonUri?: Uri;\n\t/** The parent (superproject) directory for submodules */\n\treadonly parentUri?: Uri;\n}\n\nexport type RepositoryChange =\n\t| 'unknown'\n\t| 'index'\n\t| 'head'\n\t| 'heads'\n\t| 'tags'\n\t| 'stash'\n\t| 'remotes'\n\t| 'worktrees'\n\t| 'config'\n\t| 'pausedOp'\n\t| 'cherryPick'\n\t| 'merge'\n\t| 'rebase'\n\t| 'revert'\n\t| 'closed'\n\t| 'ignores'\n\t| 'remoteProviders'\n\t| 'starred'\n\t| 'opened'\n\t| 'gkConfig'\n\t| 'lastFetched';\n\nexport const repositoryChanges = exhaustiveArray<RepositoryChange>()([\n\t'unknown',\n\t'index',\n\t'head',\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'config',\n\t'pausedOp',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'closed',\n\t'ignores',\n\t'remoteProviders',\n\t'starred',\n\t'opened',\n\t'gkConfig',\n\t'lastFetched',\n]);\n\n/**\n * The only changes {@link Repository.fireChange} accepts with `force` — delivered straight to the repository's\n * emitter, bypassing the watch session, so they still arrive while it's suspended or when there's no watch handle\n * at all (a repo being opened, or closed, or a virtual repo that never had one).\n *\n * These have nothing intrinsic in common — being forced IS the shared property, which is why this is keyed off\n * `force` rather than some category of change.\n *\n * It's the partition consumers need: everything NOT listed here goes through the session, which does its\n * pre-notification work (e.g. advancing a cache's status clock) exactly once, before it notifies anyone. Nothing\n * runs upstream of a forced change, so a consumer bridging repository changes into a cache must handle these\n * itself. Forcing a change that isn't listed is a compile error — and consumers gate on this same array, so\n * listing one wires it up for free.\n */\nexport const forcedRepositoryChanges = [\n\t'opened',\n\t'closed',\n\t'lastFetched',\n] as const satisfies readonly RepositoryChange[];\nexport type ForcedRepositoryChange = (typeof forcedRepositoryChanges)[number];\n\nexport interface RepositoryInit {\n\treadonly id: string;\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly name: string;\n\treadonly provider: GitProviderDescriptor;\n\treadonly gitDir: GitDir | undefined;\n\treadonly index: number;\n\treadonly root: boolean;\n\treadonly watchService: RepositoryWatchService;\n}\n\n/**\n * An event describing working tree file changes.\n *\n * Deliberately exposes no path collection. Consumers react by refreshing, and a refresh re-reads current\n * state — so the paths only ever gate whether to bother, never what the answer is. A partial set would\n * read as usable while quietly licensing the one harmful outcome (concluding a file did NOT change), so\n * {@link changed} is the only way to ask.\n */\nexport class RepositoryWorkingTreeChangeEvent {\n\tconstructor(\n\t\treadonly repository: Repository,\n\t\t/** Absolute paths changed in this batch; partial when {@link incomplete}. */\n\t\tprivate readonly paths: ReadonlySet<string>,\n\t\t/** Whether paths were dropped, so {@link paths} can't be used to rule anything out. */\n\t\tprivate readonly incomplete: boolean,\n\t) {}\n\n\t/**\n\t * Whether `uri` — or anything beneath it, when it's a folder, or a folder containing it — changed in this batch.\n\t * A changed folder stands for everything beneath it (a watcher can miss the files of a just-created folder).\n\t *\n\t * Conservative: `true` whenever the batch is incomplete, since a miss would then prove nothing. Matches\n\t * on paths rather than URIs so nothing has to be allocated to answer.\n\t */\n\tchanged(uri: Uri): boolean {\n\t\tif (this.incomplete) return true;\n\n\t\tconst path = uri.fsPath;\n\t\treturn some(this.paths, p => p === path || isDescendant(p, path) || isDescendant(path, p));\n\t}\n\n\ttoString(): string {\n\t\tif (this.incomplete) return `{ repository: ${this.repository.name ?? ''}, paths: incomplete }`;\n\n\t\tconst [first] = this.paths;\n\t\treturn `{ repository: ${this.repository.name ?? ''}, paths(${this.paths.size}): [${first ?? ''}${\n\t\t\tthis.paths.size > 1 ? ', ...' : ''\n\t\t}] }`;\n\t}\n}\n\nexport class Repository {\n\tprivate readonly _onDidChange = new Emitter<RepositoryChangeEvent>();\n\tget onDidChange(): Event<RepositoryChangeEvent> {\n\t\treturn this._onDidChange.event;\n\t}\n\n\tprivate readonly _onDidChangeWorkingTree = new Emitter<RepositoryWorkingTreeChangeEvent>();\n\tget onDidChangeWorkingTree(): Event<RepositoryWorkingTreeChangeEvent> {\n\t\treturn this._onDidChangeWorkingTree.event;\n\t}\n\n\treadonly commonPath: string | undefined;\n\treadonly commonRepositoryName: string | undefined;\n\treadonly commonUri: Uri | undefined;\n\treadonly id: string;\n\treadonly index: number;\n\treadonly instance = instanceCounter.next();\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly provider: GitProviderDescriptor;\n\treadonly root: boolean;\n\n\tprotected readonly _gitDir: GitDir | undefined;\n\tprotected _repoSubscription: RepositorySubscription | undefined;\n\tprotected _watchHandle: WatchHandle | undefined;\n\tprotected readonly _watchService: RepositoryWatchService;\n\n\tprivate _pendingWorkingTreePaths?: Set<string>;\n\tprivate _pendingWorkingTreeIncomplete = false;\n\tprivate _pendingWorkingTreeFlush = false;\n\tprivate _repoChangeListener: UnifiedDisposable | undefined;\n\t/** Outstanding leases on the shared {@link _watchHandle} across `watch()` + `watchWorkingTree()`. */\n\tprivate _watchHandleRefs = 0;\n\t/** Outstanding `watch()` (repo-change) leases; drives the single shared bridge subscription. */\n\tprivate _watchRefs = 0;\n\tprivate _disposed = false;\n\n\tconstructor(init: RepositoryInit) {\n\t\t({\n\t\t\tid: this.id,\n\t\t\tindex: this.index,\n\t\t\tgitDir: this._gitDir,\n\t\t\tname: this._name = init.name ?? basename(init.uri.path),\n\t\t\tpath: this.path,\n\t\t\tprovider: this.provider,\n\t\t\troot: this.root,\n\t\t\turi: this.uri,\n\t\t\twatchService: this._watchService,\n\t\t} = init);\n\n\t\t// Pre-compute common* properties from gitDir\n\t\tconst commonGitDirUri = this._gitDir?.commonUri;\n\t\tthis.commonUri = commonGitDirUri != null ? getCommonRepositoryUri(commonGitDirUri) : undefined;\n\t\tthis.commonPath = this.commonUri != null ? normalizePath(this.commonUri.path) : undefined;\n\t\tthis.commonRepositoryName = this.commonPath != null ? basename(this.commonPath) : undefined;\n\n\t\t// Apply commonRepositoryName prefix\n\t\tif (this.commonRepositoryName) {\n\t\t\tconst prefix = `${this.commonRepositoryName}: `;\n\t\t\tif (!this._name.startsWith(prefix)) {\n\t\t\t\tthis._name = `${prefix}${this._name}`;\n\t\t\t}\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._disposed = true;\n\t\tthis._repoChangeListener?.dispose();\n\t\tthis._repoChangeListener = undefined;\n\t\tthis._repoSubscription?.dispose();\n\t\tthis._repoSubscription = undefined;\n\t\tthis._watchHandle?.dispose();\n\t\tthis._watchHandle = undefined;\n\t\tthis._watchHandleRefs = 0;\n\t\tthis._watchRefs = 0;\n\t\tthis._onDidChange.dispose();\n\t\tthis._onDidChangeWorkingTree.dispose();\n\t}\n\n\t/** Indicates whether this repository currently has an active `watch()` (repo-change) lease. */\n\tget watching(): boolean {\n\t\treturn this._watchRefs > 0;\n\t}\n\n\tget etag(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget etagWorkingTree(): number | undefined {\n\t\tconst etag = this._watchHandle?.session.etagWorkingTree;\n\t\treturn etag != null && etag > 0 ? etag : undefined;\n\t}\n\n\tget hasPendingChanges(): boolean {\n\t\treturn this._watchHandle?.session.hasPendingChanges ?? false;\n\t}\n\n\t@memoize()\n\tget idHash(): string {\n\t\treturn md5(this.id);\n\t}\n\n\t/** Indicates whether this repository is a submodule */\n\tget isSubmodule(): boolean {\n\t\treturn this._gitDir?.parentUri != null;\n\t}\n\n\t/** Indicates whether this repository is a worktree */\n\tget isWorktree(): boolean {\n\t\treturn this._gitDir?.commonUri != null;\n\t}\n\n\tprotected _lastFetched: number | undefined;\n\tget lastFetchedCached(): number | undefined {\n\t\treturn this._lastFetched;\n\t}\n\n\tprotected readonly _name: string;\n\tget name(): string {\n\t\treturn this._name;\n\t}\n\n\t/** The parent repository URI (for submodules) */\n\tget parentUri(): Uri | undefined {\n\t\treturn this._gitDir?.parentUri;\n\t}\n\n\tprotected _updatedAt: number = 0;\n\tget updatedAt(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget virtual(): boolean {\n\t\treturn this.provider.virtual;\n\t}\n\n\twaitForRepoChange(timeoutMs: number): Promise<boolean> {\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\tlet listener: UnifiedDisposable | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutId != null) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\ttimeoutId = undefined;\n\t\t\t}\n\t\t\tlistener?.dispose();\n\t\t\tlistener = undefined;\n\t\t};\n\n\t\treturn Promise.race([\n\t\t\tnew Promise<false>(r => {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(false);\n\t\t\t\t}, timeoutMs);\n\t\t\t}),\n\t\t\tnew Promise<true>(r => {\n\t\t\t\tlistener = this.onDidChange(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(true);\n\t\t\t\t});\n\t\t\t}),\n\t\t]);\n\t}\n\n\t/**\n\t * Starts watching this repository's `.git` directory for repo-change events (delivered via\n\t * {@link onDidChange}) and returns a lease. Ref-counted: the underlying watch handle is acquired\n\t * on the first lease (across `watch()` and {@link watchWorkingTree}) and released when the last\n\t * is disposed. The model is inert until watched. No-ops (returns a disposable that does nothing)\n\t * for repositories without a git directory (e.g. virtual repos).\n\t */\n\t@trace({ onlyExit: true })\n\twatch(): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tif (this._watchRefs++ === 0) {\n\t\t\tconst sub = acquired.handle.session.subscribe();\n\t\t\tthis._repoSubscription = sub;\n\t\t\tthis._repoChangeListener = sub.onDidChange(e => this.onSessionRepoChange(e));\n\t\t}\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — its handle/subscriptions are gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchRefs === 0) {\n\t\t\t\t\tthis._repoChangeListener?.dispose();\n\t\t\t\t\tthis._repoChangeListener = undefined;\n\t\t\t\t\tthis._repoSubscription?.dispose();\n\t\t\t\t\tthis._repoSubscription = undefined;\n\t\t\t\t}\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t@trace({ onlyExit: true })\n\twatchWorkingTree(delay: number = 2500): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tconst sub = acquired.handle.session.subscribeToWorkingTree({ delayMs: delay });\n\t\tconst listener = sub.onDidChangeWorkingTree(e => this.onWorkingTreeChanged(e));\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\tlistener.dispose();\n\t\t\t\tsub.dispose();\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t/**\n\t * Creates a RepositoryChangeEvent for this repository.\n\t * Override in subclass to return a subclass-specific event type.\n\t */\n\tprotected createChangeEvent(changes: RepositoryChange[]): RepositoryChangeEvent {\n\t\treturn new RepositoryChangeEvent(this, changes);\n\t}\n\n\tprotected fireChange(...changes: RepositoryChange[]): void;\n\t/** Force-fire: delivered straight to the emitter, bypassing the watch session — see\n\t * {@link forcedRepositoryChanges}, which is the only set this accepts. */\n\tprotected fireChange(change: ForcedRepositoryChange, force: true): void;\n\t@trace()\n\tprotected fireChange(...args: RepositoryChange[] | [RepositoryChange, boolean]): void {\n\t\tconst lastArg = args.at(-1);\n\t\tconst force = typeof lastArg === 'boolean' ? lastArg : false;\n\t\tconst changes = (force ? args.slice(0, -1) : args) as RepositoryChange[];\n\n\t\tthis._updatedAt = Date.now();\n\n\t\tif (force) {\n\t\t\tconst e = this.createChangeEvent(changes);\n\t\t\tthis._onDidChange.fire(e);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._watchHandle?.session.fireChange(...changes);\n\t}\n\n\t/** Called when the watch service notifies of FETCH_HEAD changes. */\n\tprotected onFetchHeadChanged(): void {\n\t\t// Don't reset `_lastFetched` here: the FS watcher can fire even when the on-disk mtime\n\t\t// hasn't advanced (atomic-rename / lock-file activity emits `change` events without an\n\t\t// mtime bump), and wiping would clobber a fresher value set by `markFetched()`.\n\t\t// `getLastFetched()` reconciles in-memory vs FS via `Math.max`. Force-fire so consumers\n\t\t// invalidate their caches and re-read.\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/**\n\t * Marks the repository as having just been fetched. Use after a GitLens-initiated fetch/pull\n\t * completes so the \"last fetched\" UI tracks the attempt even when git skipped rewriting\n\t * `.git/FETCH_HEAD` (modern git omits the rewrite when all refs are up-to-date). The\n\t * in-memory timestamp is reconciled with the on-disk mtime by {@link getLastFetched} via\n\t * `Math.max`, so this is monotonic.\n\t */\n\tmarkFetched(timestamp: number = Date.now()): void {\n\t\tconst next = Math.max(this._lastFetched ?? 0, timestamp);\n\t\tif (next === this._lastFetched) return;\n\n\t\tthis._lastFetched = next;\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/** Called when .gitignore changes in the working tree. */\n\tprotected onGitIgnoreChanged(): void {\n\t\tthis.fireChange('ignores');\n\t}\n\n\t/** Called by the watch service when info/exclude changes. */\n\tprotected onIgnoresChanged(): void {}\n\n\t/** Called by the session when a debounced repo change event fires */\n\tprivate onSessionRepoChange(e: WatcherRepoChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tconst extEvent = this.createChangeEvent([...e.changes]);\n\t\tthis._onDidChange.fire(extEvent);\n\t}\n\n\t/**\n\t * Called by every {@link watchWorkingTree} subscription when its debounced batch arrives. Each\n\t * call to {@link watchWorkingTree} adds a separate bridge listener on the underlying watch\n\t * session, so a single fs change can invoke this method multiple times in the same microtask —\n\t * once per active subscription. We accumulate paths into a single pending event and defer the\n\t * actual fire to a microtask so back-to-back synchronous bridge calls collapse into ONE\n\t * `_onDidChangeWorkingTree` emission. Without this coalescing, every WIP-dependent listener\n\t * (graph, file history, compare branch, etc.) would fire N times for N active subscriptions.\n\t */\n\t/** Takes the session event whole rather than spreading it across parameters — a subclass override that\n\t * dropped a trailing positional argument silently defeated this pipeline once already. */\n\tprotected onWorkingTreeChanged(e: WorkingTreeChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tthis._pendingWorkingTreePaths ??= new Set<string>();\n\t\tfor (const p of e.paths) {\n\t\t\tthis._pendingWorkingTreePaths.add(p);\n\t\t}\n\t\tthis._pendingWorkingTreeIncomplete ||= e.incomplete;\n\n\t\tif (this._pendingWorkingTreeFlush) return;\n\n\t\tthis._pendingWorkingTreeFlush = true;\n\t\tqueueMicrotask(() => {\n\t\t\tthis._pendingWorkingTreeFlush = false;\n\t\t\tconst paths = this._pendingWorkingTreePaths;\n\t\t\tif (paths == null) return;\n\n\t\t\tconst incomplete = this._pendingWorkingTreeIncomplete;\n\t\t\tthis._pendingWorkingTreePaths = undefined;\n\t\t\tthis._pendingWorkingTreeIncomplete = false;\n\n\t\t\tthis._onDidChangeWorkingTree.fire(new RepositoryWorkingTreeChangeEvent(this, paths, incomplete));\n\t\t});\n\t}\n\n\t/**\n\t * Lazily acquires (and ref-counts) the shared {@link _watchHandle} for this repository, returning\n\t * the handle plus a one-shot lease whose disposal decrements the ref count (releasing the handle\n\t * when the last lease is gone). Returns `undefined` for repositories without a git directory.\n\t */\n\tprivate acquireWatchHandle(): { handle: WatchHandle; lease: UnifiedDisposable } | undefined {\n\t\tif (this._gitDir == null || this._disposed) return undefined;\n\n\t\tthis._watchHandle ??= this._watchService.watch(this.path, this._gitDir, {\n\t\t\tonFetchHeadChanged: () => this.onFetchHeadChanged(),\n\t\t\tonGitIgnoreChanged: () => this.onGitIgnoreChanged(),\n\t\t\tonIgnoresChanged: () => this.onIgnoresChanged(),\n\t\t} satisfies WatchHooks);\n\t\tconst handle = this._watchHandle;\n\t\tif (handle == null) return undefined;\n\n\t\tthis._watchHandleRefs++;\n\t\tconst lease = createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — the handle is gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchHandleRefs === 0) {\n\t\t\t\t\tthis._watchHandle?.dispose();\n\t\t\t\t\tthis._watchHandle = undefined;\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\treturn { handle: handle, lease: lease };\n\t}\n\n\tstatic is(repository: unknown): repository is Repository {\n\t\treturn repository instanceof Repository;\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;AAmBA,MAAM,kBAAkB,iBAAiB;AAiCzC,MAAa,oBAAoB,gBAAkC,CAAC,CAAC;CACpE;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACD,CAAC;;;;;;;;;;;;;;;AAgBD,MAAa,0BAA0B;CACtC;CACA;CACA;AACD;;;;;;;;;AAuBA,IAAa,mCAAb,MAA8C;CAEnC;CAEQ;CAEA;CALlB,YACC,YAEA,OAEA,YACC;EALQ,KAAA,aAAA;EAEQ,KAAA,QAAA;EAEA,KAAA,aAAA;CACf;;;;;;;;CASH,QAAQ,KAAmB;EAC1B,IAAI,KAAK,YAAY,OAAO;EAE5B,MAAM,OAAO,IAAI;EACjB,OAAO,KAAK,KAAK,QAAO,MAAK,MAAM,QAAQ,aAAa,GAAG,IAAI,KAAK,aAAa,MAAM,CAAC,CAAC;CAC1F;CAEA,WAAmB;EAClB,IAAI,KAAK,YAAY,OAAO,iBAAiB,KAAK,WAAW,QAAQ,GAAG;EAExE,MAAM,CAAC,SAAS,KAAK;EACrB,OAAO,iBAAiB,KAAK,WAAW,QAAQ,GAAG,UAAU,KAAK,MAAM,KAAK,MAAM,SAAS,KAC3F,KAAK,MAAM,OAAO,IAAI,UAAU,GAChC;CACF;AACD;AAEA,IAAa,aAAb,MAAa,WAAW;CACvB,eAAgC,IAAI,QAA+B;CACnE,IAAI,cAA4C;EAC/C,OAAO,KAAK,aAAa;CAC1B;CAEA,0BAA2C,IAAI,QAA0C;CACzF,IAAI,yBAAkE;EACrE,OAAO,KAAK,wBAAwB;CACrC;CAEA;CACA;CACA;CACA;CACA;CACA,WAAoB,gBAAgB,KAAK;CACzC;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CAEA;CACA,gCAAwC;CACxC,2BAAmC;CACnC;;CAEA,mBAA2B;;CAE3B,aAAqB;CACrB,YAAoB;CAEpB,YAAY,MAAsB;EACjC,CAAC,CACA,IAAI,KAAK,IACT,OAAO,KAAK,OACZ,QAAQ,KAAK,SACb,MAAM,KAAK,QAAQ,KAAK,QAAQ,SAAS,KAAK,IAAI,IAAI,GACtD,MAAM,KAAK,MACX,UAAU,KAAK,UACf,MAAM,KAAK,MACX,KAAK,KAAK,KACV,cAAc,KAAK,iBAChB;EAGJ,MAAM,kBAAkB,KAAK,SAAS;EACtC,KAAK,YAAY,mBAAmB,OAAO,uBAAuB,eAAe,IAAI,KAAA;EACrF,KAAK,aAAa,KAAK,aAAa,OAAO,cAAc,KAAK,UAAU,IAAI,IAAI,KAAA;EAChF,KAAK,uBAAuB,KAAK,cAAc,OAAO,SAAS,KAAK,UAAU,IAAI,KAAA;EAGlF,IAAI,KAAK,sBAAsB;GAC9B,MAAM,SAAS,GAAG,KAAK,qBAAqB;GAC5C,IAAI,CAAC,KAAK,MAAM,WAAW,MAAM,GAChC,KAAK,QAAQ,GAAG,SAAS,KAAK;EAEhC;CACD;CAEA,UAAgB;EACf,KAAK,YAAY;EACjB,KAAK,qBAAqB,QAAQ;EAClC,KAAK,sBAAsB,KAAA;EAC3B,KAAK,mBAAmB,QAAQ;EAChC,KAAK,oBAAoB,KAAA;EACzB,KAAK,cAAc,QAAQ;EAC3B,KAAK,eAAe,KAAA;EACpB,KAAK,mBAAmB;EACxB,KAAK,aAAa;EAClB,KAAK,aAAa,QAAQ;EAC1B,KAAK,wBAAwB,QAAQ;CACtC;;CAGA,IAAI,WAAoB;EACvB,OAAO,KAAK,aAAa;CAC1B;CAEA,IAAI,OAAe;EAClB,OAAO,KAAK;CACb;CAEA,IAAI,kBAAsC;EACzC,MAAM,OAAO,KAAK,cAAc,QAAQ;EACxC,OAAO,QAAQ,QAAQ,OAAO,IAAI,OAAO,KAAA;CAC1C;CAEA,IAAI,oBAA6B;EAChC,OAAO,KAAK,cAAc,QAAQ,qBAAqB;CACxD;CAEA,IACI,SAAiB;EACpB,OAAO,IAAI,KAAK,EAAE;CACnB;;CAGA,IAAI,cAAuB;EAC1B,OAAO,KAAK,SAAS,aAAa;CACnC;;CAGA,IAAI,aAAsB;EACzB,OAAO,KAAK,SAAS,aAAa;CACnC;CAEA;CACA,IAAI,oBAAwC;EAC3C,OAAO,KAAK;CACb;CAEA;CACA,IAAI,OAAe;EAClB,OAAO,KAAK;CACb;;CAGA,IAAI,YAA6B;EAChC,OAAO,KAAK,SAAS;CACtB;CAEA,aAA+B;CAC/B,IAAI,YAAoB;EACvB,OAAO,KAAK;CACb;CAEA,IAAI,UAAmB;EACtB,OAAO,KAAK,SAAS;CACtB;CAEA,kBAAkB,WAAqC;EACtD,IAAI;EACJ,IAAI;EAEJ,MAAM,gBAAgB;GACrB,IAAI,aAAa,MAAM;IACtB,aAAa,SAAS;IACtB,YAAY,KAAA;GACb;GACA,UAAU,QAAQ;GAClB,WAAW,KAAA;EACZ;EAEA,OAAO,QAAQ,KAAK,CACnB,IAAI,SAAe,MAAK;GACvB,YAAY,iBAAiB;IAC5B,QAAQ;IACR,EAAE,KAAK;GACR,GAAG,SAAS;EACb,CAAC,GACD,IAAI,SAAc,MAAK;GACtB,WAAW,KAAK,kBAAkB;IACjC,QAAQ;IACR,EAAE,IAAI;GACP,CAAC;EACF,CAAC,CACF,CAAC;CACF;;;;;;;;CASA,QAC2B;EAC1B,MAAM,WAAW,KAAK,mBAAmB;EACzC,IAAI,YAAY,MAAM,OAAO,uBAAuB,CAAC,CAAC;EAEtD,IAAI,KAAK,iBAAiB,GAAG;GAC5B,MAAM,MAAM,SAAS,OAAO,QAAQ,UAAU;GAC9C,KAAK,oBAAoB;GACzB,KAAK,sBAAsB,IAAI,aAAY,MAAK,KAAK,oBAAoB,CAAC,CAAC;EAC5E;EAEA,OAAO,uBACA;GAEL,IAAI,KAAK,WAAW;GAEpB,IAAI,EAAE,KAAK,eAAe,GAAG;IAC5B,KAAK,qBAAqB,QAAQ;IAClC,KAAK,sBAAsB,KAAA;IAC3B,KAAK,mBAAmB,QAAQ;IAChC,KAAK,oBAAoB,KAAA;GAC1B;GACA,SAAS,MAAM,QAAQ;EACxB,GACA,EAAE,MAAM,KAAK,CACd;CACD;CAEA,iBACiB,QAAgB,MAAyB;EACzD,MAAM,WAAW,KAAK,mBAAmB;EACzC,IAAI,YAAY,MAAM,OAAO,uBAAuB,CAAC,CAAC;EAEtD,MAAM,MAAM,SAAS,OAAO,QAAQ,uBAAuB,EAAE,SAAS,MAAM,CAAC;EAC7E,MAAM,WAAW,IAAI,wBAAuB,MAAK,KAAK,qBAAqB,CAAC,CAAC;EAE7E,OAAO,uBACA;GACL,SAAS,QAAQ;GACjB,IAAI,QAAQ;GACZ,SAAS,MAAM,QAAQ;EACxB,GACA,EAAE,MAAM,KAAK,CACd;CACD;;;;;CAMA,kBAA4B,SAAoD;EAC/E,OAAO,IAAI,sBAAsB,MAAM,OAAO;CAC/C;CAMA,WACqB,GAAG,MAA8D;EACrF,MAAM,UAAU,KAAK,GAAG,EAAE;EAC1B,MAAM,QAAQ,OAAO,YAAY,YAAY,UAAU;EACvD,MAAM,UAAW,QAAQ,KAAK,MAAM,GAAG,EAAE,IAAI;EAE7C,KAAK,aAAa,KAAK,IAAI;EAE3B,IAAI,OAAO;GACV,MAAM,IAAI,KAAK,kBAAkB,OAAO;GACxC,KAAK,aAAa,KAAK,CAAC;GACxB;EACD;EAEA,KAAK,cAAc,QAAQ,WAAW,GAAG,OAAO;CACjD;;CAGA,qBAAqC;EAMpC,KAAK,WAAW,eAAe,IAAI;CACpC;;;;;;;;CASA,YAAY,YAAoB,KAAK,IAAI,GAAS;EACjD,MAAM,OAAO,KAAK,IAAI,KAAK,gBAAgB,GAAG,SAAS;EACvD,IAAI,SAAS,KAAK,cAAc;EAEhC,KAAK,eAAe;EACpB,KAAK,WAAW,eAAe,IAAI;CACpC;;CAGA,qBAAqC;EACpC,KAAK,WAAW,SAAS;CAC1B;;CAGA,mBAAmC,CAAC;;CAGpC,oBAA4B,GAAiC;EAC5D,KAAK,aAAa,KAAK,IAAI;EAE3B,MAAM,WAAW,KAAK,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC;EACtD,KAAK,aAAa,KAAK,QAAQ;CAChC;;;;;;;;;;;;CAaA,qBAA+B,GAAiC;EAC/D,KAAK,aAAa,KAAK,IAAI;EAE3B,KAAK,6CAA6B,IAAI,IAAY;EAClD,KAAK,MAAM,KAAK,EAAE,OACjB,KAAK,yBAAyB,IAAI,CAAC;EAEpC,KAAK,kCAAkC,EAAE;EAEzC,IAAI,KAAK,0BAA0B;EAEnC,KAAK,2BAA2B;EAChC,qBAAqB;GACpB,KAAK,2BAA2B;GAChC,MAAM,QAAQ,KAAK;GACnB,IAAI,SAAS,MAAM;GAEnB,MAAM,aAAa,KAAK;GACxB,KAAK,2BAA2B,KAAA;GAChC,KAAK,gCAAgC;GAErC,KAAK,wBAAwB,KAAK,IAAI,iCAAiC,MAAM,OAAO,UAAU,CAAC;EAChG,CAAC;CACF;;;;;;CAOA,qBAA4F;EAC3F,IAAI,KAAK,WAAW,QAAQ,KAAK,WAAW,OAAO,KAAA;EAEnD,KAAK,iBAAiB,KAAK,cAAc,MAAM,KAAK,MAAM,KAAK,SAAS;GACvE,0BAA0B,KAAK,mBAAmB;GAClD,0BAA0B,KAAK,mBAAmB;GAClD,wBAAwB,KAAK,iBAAiB;EAC/C,CAAsB;EACtB,MAAM,SAAS,KAAK;EACpB,IAAI,UAAU,MAAM,OAAO,KAAA;EAE3B,KAAK;EAcL,OAAO;GAAU;GAAQ,OAbX,uBACP;IAEL,IAAI,KAAK,WAAW;IAEpB,IAAI,EAAE,KAAK,qBAAqB,GAAG;KAClC,KAAK,cAAc,QAAQ;KAC3B,KAAK,eAAe,KAAA;IACrB;GACD,GACA,EAAE,MAAM,KAAK,CAGsB;EAAE;CACvC;CAEA,OAAO,GAAG,YAA+C;EACxD,OAAO,sBAAsB;CAC9B;AACD;AAvQE,WAAA,CAAA,QAAQ,CAAA,GAAA,WAAA,WAAA,UAAA,IAAA;AA2ER,WAAA,CAAA,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAAA,WAAA,WAAA,SAAA,IAAA;AA4BxB,WAAA,CAAA,MAAM,EAAE,UAAU,KAAK,CAAC,CAAA,GAAA,WAAA,WAAA,oBAAA,IAAA;AA8BxB,WAAA,CAAA,MAAM,CAAA,GAAA,WAAA,WAAA,cAAA,IAAA"}
1
+ {"version":3,"file":"repository.js","names":[],"sources":["../../../../git/src/models/repository.ts"],"sourcesContent":["import { exhaustiveArray } from '@gitlens/utils/array.js';\nimport { getScopedCounter } from '@gitlens/utils/counter.js';\nimport { md5 } from '@gitlens/utils/crypto.js';\nimport { trace } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport type { UnifiedDisposable } from '@gitlens/utils/disposable.js';\nimport { createDisposable } from '@gitlens/utils/disposable.js';\nimport type { Event } from '@gitlens/utils/event.js';\nimport { Emitter } from '@gitlens/utils/event.js';\nimport { some } from '@gitlens/utils/iterable.js';\nimport { basename, isDescendant, normalizePath } from '@gitlens/utils/path.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport type { GitProviderDescriptor } from '../providers/types.js';\nimport { getCommonRepositoryUri } from '../utils/repository.utils.js';\nimport type { WatcherRepoChangeEvent, WorkingTreeChangeEvent } from '../watching/changeEvent.js';\nimport type { RepositoryWatchService, WatchHandle, WatchHooks } from '../watching/watchService.js';\nimport type { RepositorySubscription } from '../watching/watchSession.js';\nimport { RepositoryChangeEvent } from './repositoryChangeEvent.js';\n\nconst instanceCounter = getScopedCounter();\n\nexport interface GitDir {\n\treadonly uri: Uri;\n\t/** The common git directory for worktrees */\n\treadonly commonUri?: Uri;\n\t/** The parent (superproject) directory for submodules */\n\treadonly parentUri?: Uri;\n}\n\nexport type RepositoryChange =\n\t| 'unknown'\n\t| 'index'\n\t| 'head'\n\t| 'heads'\n\t| 'tags'\n\t| 'stash'\n\t| 'remotes'\n\t| 'worktrees'\n\t/** A linked worktree's HEAD moved: a checkout in a worktree whose own `.git` this session doesn't watch */\n\t| 'worktreeHead'\n\t| 'config'\n\t| 'pausedOp'\n\t| 'cherryPick'\n\t| 'merge'\n\t| 'rebase'\n\t| 'revert'\n\t| 'closed'\n\t| 'ignores'\n\t| 'remoteProviders'\n\t| 'starred'\n\t| 'opened'\n\t| 'gkConfig'\n\t| 'lastFetched';\n\nexport const repositoryChanges = exhaustiveArray<RepositoryChange>()([\n\t'unknown',\n\t'index',\n\t'head',\n\t'heads',\n\t'tags',\n\t'stash',\n\t'remotes',\n\t'worktrees',\n\t'worktreeHead',\n\t'config',\n\t'pausedOp',\n\t'cherryPick',\n\t'merge',\n\t'rebase',\n\t'revert',\n\t'closed',\n\t'ignores',\n\t'remoteProviders',\n\t'starred',\n\t'opened',\n\t'gkConfig',\n\t'lastFetched',\n]);\n\n/**\n * The only changes {@link Repository.fireChange} accepts with `force` — delivered straight to the repository's\n * emitter, bypassing the watch session, so they still arrive while it's suspended or when there's no watch handle\n * at all (a repo being opened, or closed, or a virtual repo that never had one).\n *\n * These have nothing intrinsic in common — being forced IS the shared property, which is why this is keyed off\n * `force` rather than some category of change.\n *\n * It's the partition consumers need: everything NOT listed here goes through the session, which does its\n * pre-notification work (e.g. advancing a cache's status clock) exactly once, before it notifies anyone. Nothing\n * runs upstream of a forced change, so a consumer bridging repository changes into a cache must handle these\n * itself. Forcing a change that isn't listed is a compile error — and consumers gate on this same array, so\n * listing one wires it up for free.\n */\nexport const forcedRepositoryChanges = [\n\t'opened',\n\t'closed',\n\t'lastFetched',\n] as const satisfies readonly RepositoryChange[];\nexport type ForcedRepositoryChange = (typeof forcedRepositoryChanges)[number];\n\nexport interface RepositoryInit {\n\treadonly id: string;\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly name: string;\n\treadonly provider: GitProviderDescriptor;\n\treadonly gitDir: GitDir | undefined;\n\treadonly index: number;\n\treadonly root: boolean;\n\treadonly watchService: RepositoryWatchService;\n}\n\n/**\n * An event describing working tree file changes.\n *\n * Deliberately exposes no path collection. Consumers react by refreshing, and a refresh re-reads current\n * state — so the paths only ever gate whether to bother, never what the answer is. A partial set would\n * read as usable while quietly licensing the one harmful outcome (concluding a file did NOT change), so\n * {@link changed} is the only way to ask.\n */\nexport class RepositoryWorkingTreeChangeEvent {\n\tconstructor(\n\t\treadonly repository: Repository,\n\t\t/** Absolute paths changed in this batch; partial when {@link incomplete}. */\n\t\tprivate readonly paths: ReadonlySet<string>,\n\t\t/** Whether paths were dropped, so {@link paths} can't be used to rule anything out. */\n\t\tprivate readonly incomplete: boolean,\n\t) {}\n\n\t/**\n\t * Whether `uri` — or anything beneath it, when it's a folder, or a folder containing it — changed in this batch.\n\t * A changed folder stands for everything beneath it (a watcher can miss the files of a just-created folder).\n\t *\n\t * Conservative: `true` whenever the batch is incomplete, since a miss would then prove nothing. Matches\n\t * on paths rather than URIs so nothing has to be allocated to answer.\n\t */\n\tchanged(uri: Uri): boolean {\n\t\tif (this.incomplete) return true;\n\n\t\tconst path = uri.fsPath;\n\t\treturn some(this.paths, p => p === path || isDescendant(p, path) || isDescendant(path, p));\n\t}\n\n\ttoString(): string {\n\t\tif (this.incomplete) return `{ repository: ${this.repository.name ?? ''}, paths: incomplete }`;\n\n\t\tconst [first] = this.paths;\n\t\treturn `{ repository: ${this.repository.name ?? ''}, paths(${this.paths.size}): [${first ?? ''}${\n\t\t\tthis.paths.size > 1 ? ', ...' : ''\n\t\t}] }`;\n\t}\n}\n\nexport class Repository {\n\tprivate readonly _onDidChange = new Emitter<RepositoryChangeEvent>();\n\tget onDidChange(): Event<RepositoryChangeEvent> {\n\t\treturn this._onDidChange.event;\n\t}\n\n\tprivate readonly _onDidChangeWorkingTree = new Emitter<RepositoryWorkingTreeChangeEvent>();\n\tget onDidChangeWorkingTree(): Event<RepositoryWorkingTreeChangeEvent> {\n\t\treturn this._onDidChangeWorkingTree.event;\n\t}\n\n\treadonly commonPath: string | undefined;\n\treadonly commonRepositoryName: string | undefined;\n\treadonly commonUri: Uri | undefined;\n\treadonly id: string;\n\treadonly index: number;\n\treadonly instance = instanceCounter.next();\n\treadonly path: string;\n\treadonly uri: Uri;\n\treadonly provider: GitProviderDescriptor;\n\treadonly root: boolean;\n\n\tprotected readonly _gitDir: GitDir | undefined;\n\tprotected _repoSubscription: RepositorySubscription | undefined;\n\tprotected _watchHandle: WatchHandle | undefined;\n\tprotected readonly _watchService: RepositoryWatchService;\n\n\tprivate _pendingWorkingTreePaths?: Set<string>;\n\tprivate _pendingWorkingTreeIncomplete = false;\n\tprivate _pendingWorkingTreeFlush = false;\n\tprivate _repoChangeListener: UnifiedDisposable | undefined;\n\t/** Outstanding leases on the shared {@link _watchHandle} across `watch()` + `watchWorkingTree()`. */\n\tprivate _watchHandleRefs = 0;\n\t/** Outstanding `watch()` (repo-change) leases; drives the single shared bridge subscription. */\n\tprivate _watchRefs = 0;\n\tprivate _disposed = false;\n\n\tconstructor(init: RepositoryInit) {\n\t\t({\n\t\t\tid: this.id,\n\t\t\tindex: this.index,\n\t\t\tgitDir: this._gitDir,\n\t\t\tname: this._name = init.name ?? basename(init.uri.path),\n\t\t\tpath: this.path,\n\t\t\tprovider: this.provider,\n\t\t\troot: this.root,\n\t\t\turi: this.uri,\n\t\t\twatchService: this._watchService,\n\t\t} = init);\n\n\t\t// Pre-compute common* properties from gitDir\n\t\tconst commonGitDirUri = this._gitDir?.commonUri;\n\t\tthis.commonUri = commonGitDirUri != null ? getCommonRepositoryUri(commonGitDirUri) : undefined;\n\t\tthis.commonPath = this.commonUri != null ? normalizePath(this.commonUri.path) : undefined;\n\t\tthis.commonRepositoryName = this.commonPath != null ? basename(this.commonPath) : undefined;\n\n\t\t// Apply commonRepositoryName prefix\n\t\tif (this.commonRepositoryName) {\n\t\t\tconst prefix = `${this.commonRepositoryName}: `;\n\t\t\tif (!this._name.startsWith(prefix)) {\n\t\t\t\tthis._name = `${prefix}${this._name}`;\n\t\t\t}\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis._disposed = true;\n\t\tthis._repoChangeListener?.dispose();\n\t\tthis._repoChangeListener = undefined;\n\t\tthis._repoSubscription?.dispose();\n\t\tthis._repoSubscription = undefined;\n\t\tthis._watchHandle?.dispose();\n\t\tthis._watchHandle = undefined;\n\t\tthis._watchHandleRefs = 0;\n\t\tthis._watchRefs = 0;\n\t\tthis._onDidChange.dispose();\n\t\tthis._onDidChangeWorkingTree.dispose();\n\t}\n\n\t/** Indicates whether this repository currently has an active `watch()` (repo-change) lease. */\n\tget watching(): boolean {\n\t\treturn this._watchRefs > 0;\n\t}\n\n\tget etag(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget etagWorkingTree(): number | undefined {\n\t\tconst etag = this._watchHandle?.session.etagWorkingTree;\n\t\treturn etag != null && etag > 0 ? etag : undefined;\n\t}\n\n\tget hasPendingChanges(): boolean {\n\t\treturn this._watchHandle?.session.hasPendingChanges ?? false;\n\t}\n\n\t@memoize()\n\tget idHash(): string {\n\t\treturn md5(this.id);\n\t}\n\n\t/** Indicates whether this repository is a submodule */\n\tget isSubmodule(): boolean {\n\t\treturn this._gitDir?.parentUri != null;\n\t}\n\n\t/** Indicates whether this repository is a worktree */\n\tget isWorktree(): boolean {\n\t\treturn this._gitDir?.commonUri != null;\n\t}\n\n\tprotected _lastFetched: number | undefined;\n\tget lastFetchedCached(): number | undefined {\n\t\treturn this._lastFetched;\n\t}\n\n\tprotected readonly _name: string;\n\tget name(): string {\n\t\treturn this._name;\n\t}\n\n\t/** The parent repository URI (for submodules) */\n\tget parentUri(): Uri | undefined {\n\t\treturn this._gitDir?.parentUri;\n\t}\n\n\tprotected _updatedAt: number = 0;\n\tget updatedAt(): number {\n\t\treturn this._updatedAt;\n\t}\n\n\tget virtual(): boolean {\n\t\treturn this.provider.virtual;\n\t}\n\n\twaitForRepoChange(timeoutMs: number): Promise<boolean> {\n\t\tlet timeoutId: ReturnType<typeof setTimeout> | undefined;\n\t\tlet listener: UnifiedDisposable | undefined;\n\n\t\tconst cleanup = () => {\n\t\t\tif (timeoutId != null) {\n\t\t\t\tclearTimeout(timeoutId);\n\t\t\t\ttimeoutId = undefined;\n\t\t\t}\n\t\t\tlistener?.dispose();\n\t\t\tlistener = undefined;\n\t\t};\n\n\t\treturn Promise.race([\n\t\t\tnew Promise<false>(r => {\n\t\t\t\ttimeoutId = setTimeout(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(false);\n\t\t\t\t}, timeoutMs);\n\t\t\t}),\n\t\t\tnew Promise<true>(r => {\n\t\t\t\tlistener = this.onDidChange(() => {\n\t\t\t\t\tcleanup();\n\t\t\t\t\tr(true);\n\t\t\t\t});\n\t\t\t}),\n\t\t]);\n\t}\n\n\t/**\n\t * Starts watching this repository's `.git` directory for repo-change events (delivered via\n\t * {@link onDidChange}) and returns a lease. Ref-counted: the underlying watch handle is acquired\n\t * on the first lease (across `watch()` and {@link watchWorkingTree}) and released when the last\n\t * is disposed. The model is inert until watched. No-ops (returns a disposable that does nothing)\n\t * for repositories without a git directory (e.g. virtual repos).\n\t */\n\t@trace({ onlyExit: true })\n\twatch(): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tif (this._watchRefs++ === 0) {\n\t\t\tconst sub = acquired.handle.session.subscribe();\n\t\t\tthis._repoSubscription = sub;\n\t\t\tthis._repoChangeListener = sub.onDidChange(e => this.onSessionRepoChange(e));\n\t\t}\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — its handle/subscriptions are gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchRefs === 0) {\n\t\t\t\t\tthis._repoChangeListener?.dispose();\n\t\t\t\t\tthis._repoChangeListener = undefined;\n\t\t\t\t\tthis._repoSubscription?.dispose();\n\t\t\t\t\tthis._repoSubscription = undefined;\n\t\t\t\t}\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t@trace({ onlyExit: true })\n\twatchWorkingTree(delay: number = 2500): UnifiedDisposable {\n\t\tconst acquired = this.acquireWatchHandle();\n\t\tif (acquired == null) return createDisposable(() => {});\n\n\t\tconst sub = acquired.handle.session.subscribeToWorkingTree({ delayMs: delay });\n\t\tconst listener = sub.onDidChangeWorkingTree(e => this.onWorkingTreeChanged(e));\n\n\t\treturn createDisposable(\n\t\t\t() => {\n\t\t\t\tlistener.dispose();\n\t\t\t\tsub.dispose();\n\t\t\t\tacquired.lease.dispose();\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\t}\n\n\t/**\n\t * Creates a RepositoryChangeEvent for this repository.\n\t * Override in subclass to return a subclass-specific event type.\n\t */\n\tprotected createChangeEvent(changes: RepositoryChange[]): RepositoryChangeEvent {\n\t\treturn new RepositoryChangeEvent(this, changes);\n\t}\n\n\tprotected fireChange(...changes: RepositoryChange[]): void;\n\t/** Force-fire: delivered straight to the emitter, bypassing the watch session — see\n\t * {@link forcedRepositoryChanges}, which is the only set this accepts. */\n\tprotected fireChange(change: ForcedRepositoryChange, force: true): void;\n\t@trace()\n\tprotected fireChange(...args: RepositoryChange[] | [RepositoryChange, boolean]): void {\n\t\tconst lastArg = args.at(-1);\n\t\tconst force = typeof lastArg === 'boolean' ? lastArg : false;\n\t\tconst changes = (force ? args.slice(0, -1) : args) as RepositoryChange[];\n\n\t\tthis._updatedAt = Date.now();\n\n\t\tif (force) {\n\t\t\tconst e = this.createChangeEvent(changes);\n\t\t\tthis._onDidChange.fire(e);\n\t\t\treturn;\n\t\t}\n\n\t\tthis._watchHandle?.session.fireChange(...changes);\n\t}\n\n\t/** Called when the watch service notifies of FETCH_HEAD changes. */\n\tprotected onFetchHeadChanged(): void {\n\t\t// Don't reset `_lastFetched` here: the FS watcher can fire even when the on-disk mtime\n\t\t// hasn't advanced (atomic-rename / lock-file activity emits `change` events without an\n\t\t// mtime bump), and wiping would clobber a fresher value set by `markFetched()`.\n\t\t// `getLastFetched()` reconciles in-memory vs FS via `Math.max`. Force-fire so consumers\n\t\t// invalidate their caches and re-read.\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/**\n\t * Marks the repository as having just been fetched. Use after a GitLens-initiated fetch/pull\n\t * completes so the \"last fetched\" UI tracks the attempt even when git skipped rewriting\n\t * `.git/FETCH_HEAD` (modern git omits the rewrite when all refs are up-to-date). The\n\t * in-memory timestamp is reconciled with the on-disk mtime by {@link getLastFetched} via\n\t * `Math.max`, so this is monotonic.\n\t */\n\tmarkFetched(timestamp: number = Date.now()): void {\n\t\tconst next = Math.max(this._lastFetched ?? 0, timestamp);\n\t\tif (next === this._lastFetched) return;\n\n\t\tthis._lastFetched = next;\n\t\tthis.fireChange('lastFetched', true);\n\t}\n\n\t/** Called when .gitignore changes in the working tree. */\n\tprotected onGitIgnoreChanged(): void {\n\t\tthis.fireChange('ignores');\n\t}\n\n\t/** Called by the watch service when info/exclude changes. */\n\tprotected onIgnoresChanged(): void {}\n\n\t/** Called by the session when a debounced repo change event fires */\n\tprivate onSessionRepoChange(e: WatcherRepoChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tconst extEvent = this.createChangeEvent([...e.changes]);\n\t\tthis._onDidChange.fire(extEvent);\n\t}\n\n\t/**\n\t * Called by every {@link watchWorkingTree} subscription when its debounced batch arrives. Each\n\t * call to {@link watchWorkingTree} adds a separate bridge listener on the underlying watch\n\t * session, so a single fs change can invoke this method multiple times in the same microtask —\n\t * once per active subscription. We accumulate paths into a single pending event and defer the\n\t * actual fire to a microtask so back-to-back synchronous bridge calls collapse into ONE\n\t * `_onDidChangeWorkingTree` emission. Without this coalescing, every WIP-dependent listener\n\t * (graph, file history, compare branch, etc.) would fire N times for N active subscriptions.\n\t */\n\t/** Takes the session event whole rather than spreading it across parameters — a subclass override that\n\t * dropped a trailing positional argument silently defeated this pipeline once already. */\n\tprotected onWorkingTreeChanged(e: WorkingTreeChangeEvent): void {\n\t\tthis._updatedAt = Date.now();\n\n\t\tthis._pendingWorkingTreePaths ??= new Set<string>();\n\t\tfor (const p of e.paths) {\n\t\t\tthis._pendingWorkingTreePaths.add(p);\n\t\t}\n\t\tthis._pendingWorkingTreeIncomplete ||= e.incomplete;\n\n\t\tif (this._pendingWorkingTreeFlush) return;\n\n\t\tthis._pendingWorkingTreeFlush = true;\n\t\tqueueMicrotask(() => {\n\t\t\tthis._pendingWorkingTreeFlush = false;\n\t\t\tconst paths = this._pendingWorkingTreePaths;\n\t\t\tif (paths == null) return;\n\n\t\t\tconst incomplete = this._pendingWorkingTreeIncomplete;\n\t\t\tthis._pendingWorkingTreePaths = undefined;\n\t\t\tthis._pendingWorkingTreeIncomplete = false;\n\n\t\t\tthis._onDidChangeWorkingTree.fire(new RepositoryWorkingTreeChangeEvent(this, paths, incomplete));\n\t\t});\n\t}\n\n\t/**\n\t * Lazily acquires (and ref-counts) the shared {@link _watchHandle} for this repository, returning\n\t * the handle plus a one-shot lease whose disposal decrements the ref count (releasing the handle\n\t * when the last lease is gone). Returns `undefined` for repositories without a git directory.\n\t */\n\tprivate acquireWatchHandle(): { handle: WatchHandle; lease: UnifiedDisposable } | undefined {\n\t\tif (this._gitDir == null || this._disposed) return undefined;\n\n\t\tthis._watchHandle ??= this._watchService.watch(this.path, this._gitDir, {\n\t\t\tonFetchHeadChanged: () => this.onFetchHeadChanged(),\n\t\t\tonGitIgnoreChanged: () => this.onGitIgnoreChanged(),\n\t\t\tonIgnoresChanged: () => this.onIgnoresChanged(),\n\t\t} satisfies WatchHooks);\n\t\tconst handle = this._watchHandle;\n\t\tif (handle == null) return undefined;\n\n\t\tthis._watchHandleRefs++;\n\t\tconst lease = createDisposable(\n\t\t\t() => {\n\t\t\t\t// Repo already torn down — the handle is gone; skip to avoid a negative refcount\n\t\t\t\tif (this._disposed) return;\n\n\t\t\t\tif (--this._watchHandleRefs === 0) {\n\t\t\t\t\tthis._watchHandle?.dispose();\n\t\t\t\t\tthis._watchHandle = undefined;\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ once: true },\n\t\t);\n\n\t\treturn { handle: handle, lease: lease };\n\t}\n\n\tstatic is(repository: unknown): repository is Repository {\n\t\treturn repository instanceof Repository;\n\t}\n}\n"],"mappings":"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"}
@@ -19,10 +19,14 @@ export declare class GitWorktree {
19
19
  readonly uri: Uri;
20
20
  readonly name: string;
21
21
  } | undefined;
22
+ /** The branch name from `git worktree list` (`branch refs/heads/<name>`), present even when no `GitBranch` was matched into `branch` */
23
+ readonly branchName?: string | undefined;
22
24
  constructor(isDefault: boolean, type: 'bare' | 'branch' | 'detached', repoPath: string, uri: Uri, locked: boolean | string, prunable: boolean | string, sha?: string | undefined, branch?: GitBranch | undefined, workspaceFolder?: {
23
25
  readonly uri: Uri;
24
26
  readonly name: string;
25
- } | undefined);
27
+ } | undefined,
28
+ /** The branch name from `git worktree list` (`branch refs/heads/<name>`), present even when no `GitBranch` was matched into `branch` */
29
+ branchName?: string | undefined);
26
30
  get date(): Date | undefined;
27
31
  /**
28
32
  * @returns The most recent date among lastModifiedDate, lastAccessedDate, and branch.date. Not memoized: the stamps
@@ -19,7 +19,8 @@ let GitWorktree = _GitWorktree = class GitWorktree {
19
19
  sha;
20
20
  branch;
21
21
  workspaceFolder;
22
- constructor(isDefault, type, repoPath, uri, locked, prunable, sha, branch, workspaceFolder) {
22
+ branchName;
23
+ constructor(isDefault, type, repoPath, uri, locked, prunable, sha, branch, workspaceFolder, branchName) {
23
24
  this.isDefault = isDefault;
24
25
  this.type = type;
25
26
  this.repoPath = repoPath;
@@ -29,6 +30,7 @@ let GitWorktree = _GitWorktree = class GitWorktree {
29
30
  this.sha = sha;
30
31
  this.branch = branch;
31
32
  this.workspaceFolder = workspaceFolder;
33
+ this.branchName = branchName;
32
34
  }
33
35
  get date() {
34
36
  return this.branch?.date;
@@ -63,7 +65,7 @@ let GitWorktree = _GitWorktree = class GitWorktree {
63
65
  switch (this.type) {
64
66
  case "bare": return "(bare)";
65
67
  case "detached": return `${basename(this.path)} (${shortenRevision(this.sha)})`;
66
- default: return this.branch?.name || this.friendlyPath;
68
+ default: return this.branch?.name || this.branchName || this.friendlyPath;
67
69
  }
68
70
  }
69
71
  get opened() {
@@ -75,7 +77,7 @@ let GitWorktree = _GitWorktree = class GitWorktree {
75
77
  /** Creates a copy of this worktree with a different repoPath and updated branch — ONLY used for worktree-aware caching */
76
78
  withRepoPath(repoPath) {
77
79
  if (repoPath === this.repoPath) return this;
78
- return new _GitWorktree(this.isDefault, this.type, repoPath, this.uri, this.locked, this.prunable, this.sha, this.branch?.withRepoPath(repoPath, true), this.workspaceFolder);
80
+ return new _GitWorktree(this.isDefault, this.type, repoPath, this.uri, this.locked, this.prunable, this.sha, this.branch?.withRepoPath(repoPath, true), this.workspaceFolder, this.branchName);
79
81
  }
80
82
  static is(worktree) {
81
83
  return worktree instanceof _GitWorktree;
@@ -1 +1 @@
1
- {"version":3,"file":"worktree.js","names":[],"sources":["../../../../git/src/models/worktree.ts"],"sourcesContent":["import { formatDate, fromNow } from '@gitlens/utils/date.js';\nimport { loggable } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport { serializable } from '@gitlens/utils/decorators/serializable.js';\nimport { basename, normalizePath, relative } from '@gitlens/utils/path.js';\nimport type { Shape } from '@gitlens/utils/types.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryService } from '../repositoryService.js';\nimport type { GitCommandPriority } from '../run.types.js';\nimport { getRepositoryOrWorktreePath } from '../utils/repository.utils.js';\nimport { shortenRevision } from '../utils/revision.utils.js';\nimport type { GitBranch } from './branch.js';\nimport type { GitStatus } from './status.js';\n\nexport type GitWorktreeShape = Shape<GitWorktree>;\n\nexport type WorkspaceFolderResolver = (uri: Uri) => GitWorktree['workspaceFolder'] | undefined;\n\n@loggable(i => i.uri.toString())\n@serializable\nexport class GitWorktree {\n\tconstructor(\n\t\tpublic readonly isDefault: boolean,\n\t\tpublic readonly type: 'bare' | 'branch' | 'detached',\n\t\tpublic readonly repoPath: string,\n\t\tpublic readonly uri: Uri,\n\t\tpublic readonly locked: boolean | string,\n\t\tpublic readonly prunable: boolean | string,\n\t\tpublic readonly sha?: string,\n\t\tpublic readonly branch?: GitBranch,\n\t\tpublic readonly workspaceFolder?: { readonly uri: Uri; readonly name: string },\n\t) {}\n\n\tget date(): Date | undefined {\n\t\treturn this.branch?.date;\n\t}\n\n\t/**\n\t * @returns The most recent date among lastModifiedDate, lastAccessedDate, and branch.date. Not memoized: the stamps\n\t * change in place (see `BranchMetadata`)\n\t */\n\tget effectiveDate(): Date | undefined {\n\t\tlet maxTime: number | undefined;\n\n\t\tconst accessed = this.lastAccessedDate?.getTime();\n\t\tif (accessed != null && (maxTime == null || accessed > maxTime)) {\n\t\t\tmaxTime = accessed;\n\t\t}\n\n\t\tconst modified = this.lastModifiedDate?.getTime();\n\t\tif (modified != null && (maxTime == null || modified > maxTime)) {\n\t\t\tmaxTime = modified;\n\t\t}\n\n\t\tconst date = this.branch?.date?.getTime();\n\t\tif (date != null && (maxTime == null || date > maxTime)) {\n\t\t\tmaxTime = date;\n\t\t}\n\n\t\treturn maxTime != null ? new Date(maxTime) : undefined;\n\t}\n\n\t/** Timestamp when the worktree branch was last accessed or modified */\n\tget lastAccessedDate(): Date | undefined {\n\t\treturn this.branch?.lastAccessedDate;\n\t}\n\n\t/** Timestamp when the worktree branch was last modified (working changes / index) */\n\tget lastModifiedDate(): Date | undefined {\n\t\treturn this.branch?.lastModifiedDate;\n\t}\n\n\t@memoize()\n\tget friendlyPath(): string {\n\t\tif (this.workspaceFolder != null) {\n\t\t\tconst relativePath = normalizePath(relative(this.workspaceFolder.uri.fsPath, this.uri.fsPath));\n\t\t\treturn relativePath || this.workspaceFolder.name;\n\t\t}\n\n\t\tconst relativePath = normalizePath(relative(this.repoPath, this.uri.fsPath));\n\t\treturn relativePath || normalizePath(this.uri.fsPath);\n\t}\n\n\t@memoize()\n\tget name(): string {\n\t\tswitch (this.type) {\n\t\t\tcase 'bare':\n\t\t\t\treturn '(bare)';\n\t\t\tcase 'detached':\n\t\t\t\treturn `${basename(this.path)} (${shortenRevision(this.sha)})`;\n\t\t\tdefault:\n\t\t\t\treturn this.branch?.name || this.friendlyPath;\n\t\t}\n\t}\n\n\tget opened(): boolean {\n\t\treturn this.workspaceFolder?.uri.toString() === this.uri.toString();\n\t}\n\n\tget path(): string {\n\t\treturn getRepositoryOrWorktreePath(this.uri);\n\t}\n\n\t/** Creates a copy of this worktree with a different repoPath and updated branch — ONLY used for worktree-aware caching */\n\twithRepoPath(repoPath: string): GitWorktree {\n\t\tif (repoPath === this.repoPath) return this;\n\n\t\treturn new GitWorktree(\n\t\t\tthis.isDefault,\n\t\t\tthis.type,\n\t\t\trepoPath,\n\t\t\tthis.uri,\n\t\t\tthis.locked,\n\t\t\tthis.prunable,\n\t\t\tthis.sha,\n\t\t\tthis.branch?.withRepoPath(repoPath, true),\n\t\t\tthis.workspaceFolder,\n\t\t);\n\t}\n\n\tstatic is(worktree: unknown): worktree is GitWorktree {\n\t\treturn worktree instanceof GitWorktree;\n\t}\n\n\tstatic formatDate(worktree: GitWorktreeShape, format?: string | null): string {\n\t\treturn worktree.date != null ? formatDate(worktree.date, format ?? 'MMMM Do, YYYY h:mma') : '';\n\t}\n\n\tstatic formatDateFromNow(worktree: GitWorktreeShape): string {\n\t\treturn worktree.date != null ? fromNow(worktree.date) : '';\n\t}\n\n\tstatic formatDateWithStyle(\n\t\tworktree: GitWorktreeShape,\n\t\tformatting: { dateStyle: string; dateFormat: string | null },\n\t): string {\n\t\treturn formatting.dateStyle === 'absolute'\n\t\t\t? GitWorktree.formatDate(worktree, formatting.dateFormat)\n\t\t\t: GitWorktree.formatDateFromNow(worktree);\n\t}\n\n\tstatic async getStatus(\n\t\tworktree: GitWorktree,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitStatus | undefined> {\n\t\tif (worktree.type === 'bare') return undefined;\n\n\t\tconst repo = getRepositoryService(worktree.path);\n\t\treturn repo?.status.getStatus(options, cancellation);\n\t}\n\n\tstatic async hasWorkingChanges(\n\t\tworktree: GitWorktree,\n\t\toptions?: { staged?: boolean; unstaged?: boolean; untracked?: boolean },\n\t): Promise<boolean | undefined> {\n\t\tif (worktree.type === 'bare') return undefined;\n\n\t\tconst repo = getRepositoryService(worktree.path);\n\t\treturn repo?.status.hasWorkingChanges(options);\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"worktree.js","names":[],"sources":["../../../../git/src/models/worktree.ts"],"sourcesContent":["import { formatDate, fromNow } from '@gitlens/utils/date.js';\nimport { loggable } from '@gitlens/utils/decorators/log.js';\nimport { memoize } from '@gitlens/utils/decorators/memoize.js';\nimport { serializable } from '@gitlens/utils/decorators/serializable.js';\nimport { basename, normalizePath, relative } from '@gitlens/utils/path.js';\nimport type { Shape } from '@gitlens/utils/types.js';\nimport type { Uri } from '@gitlens/utils/uri.js';\nimport { getRepositoryService } from '../repositoryService.js';\nimport type { GitCommandPriority } from '../run.types.js';\nimport { getRepositoryOrWorktreePath } from '../utils/repository.utils.js';\nimport { shortenRevision } from '../utils/revision.utils.js';\nimport type { GitBranch } from './branch.js';\nimport type { GitStatus } from './status.js';\n\nexport type GitWorktreeShape = Shape<GitWorktree>;\n\nexport type WorkspaceFolderResolver = (uri: Uri) => GitWorktree['workspaceFolder'] | undefined;\n\n@loggable(i => i.uri.toString())\n@serializable\nexport class GitWorktree {\n\tconstructor(\n\t\tpublic readonly isDefault: boolean,\n\t\tpublic readonly type: 'bare' | 'branch' | 'detached',\n\t\tpublic readonly repoPath: string,\n\t\tpublic readonly uri: Uri,\n\t\tpublic readonly locked: boolean | string,\n\t\tpublic readonly prunable: boolean | string,\n\t\tpublic readonly sha?: string,\n\t\tpublic readonly branch?: GitBranch,\n\t\tpublic readonly workspaceFolder?: { readonly uri: Uri; readonly name: string },\n\t\t/** The branch name from `git worktree list` (`branch refs/heads/<name>`), present even when no `GitBranch` was matched into `branch` */\n\t\tpublic readonly branchName?: string,\n\t) {}\n\n\tget date(): Date | undefined {\n\t\treturn this.branch?.date;\n\t}\n\n\t/**\n\t * @returns The most recent date among lastModifiedDate, lastAccessedDate, and branch.date. Not memoized: the stamps\n\t * change in place (see `BranchMetadata`)\n\t */\n\tget effectiveDate(): Date | undefined {\n\t\tlet maxTime: number | undefined;\n\n\t\tconst accessed = this.lastAccessedDate?.getTime();\n\t\tif (accessed != null && (maxTime == null || accessed > maxTime)) {\n\t\t\tmaxTime = accessed;\n\t\t}\n\n\t\tconst modified = this.lastModifiedDate?.getTime();\n\t\tif (modified != null && (maxTime == null || modified > maxTime)) {\n\t\t\tmaxTime = modified;\n\t\t}\n\n\t\tconst date = this.branch?.date?.getTime();\n\t\tif (date != null && (maxTime == null || date > maxTime)) {\n\t\t\tmaxTime = date;\n\t\t}\n\n\t\treturn maxTime != null ? new Date(maxTime) : undefined;\n\t}\n\n\t/** Timestamp when the worktree branch was last accessed or modified */\n\tget lastAccessedDate(): Date | undefined {\n\t\treturn this.branch?.lastAccessedDate;\n\t}\n\n\t/** Timestamp when the worktree branch was last modified (working changes / index) */\n\tget lastModifiedDate(): Date | undefined {\n\t\treturn this.branch?.lastModifiedDate;\n\t}\n\n\t@memoize()\n\tget friendlyPath(): string {\n\t\tif (this.workspaceFolder != null) {\n\t\t\tconst relativePath = normalizePath(relative(this.workspaceFolder.uri.fsPath, this.uri.fsPath));\n\t\t\treturn relativePath || this.workspaceFolder.name;\n\t\t}\n\n\t\tconst relativePath = normalizePath(relative(this.repoPath, this.uri.fsPath));\n\t\treturn relativePath || normalizePath(this.uri.fsPath);\n\t}\n\n\t@memoize()\n\tget name(): string {\n\t\tswitch (this.type) {\n\t\t\tcase 'bare':\n\t\t\t\treturn '(bare)';\n\t\t\tcase 'detached':\n\t\t\t\treturn `${basename(this.path)} (${shortenRevision(this.sha)})`;\n\t\t\tdefault:\n\t\t\t\treturn this.branch?.name || this.branchName || this.friendlyPath;\n\t\t}\n\t}\n\n\tget opened(): boolean {\n\t\treturn this.workspaceFolder?.uri.toString() === this.uri.toString();\n\t}\n\n\tget path(): string {\n\t\treturn getRepositoryOrWorktreePath(this.uri);\n\t}\n\n\t/** Creates a copy of this worktree with a different repoPath and updated branch — ONLY used for worktree-aware caching */\n\twithRepoPath(repoPath: string): GitWorktree {\n\t\tif (repoPath === this.repoPath) return this;\n\n\t\treturn new GitWorktree(\n\t\t\tthis.isDefault,\n\t\t\tthis.type,\n\t\t\trepoPath,\n\t\t\tthis.uri,\n\t\t\tthis.locked,\n\t\t\tthis.prunable,\n\t\t\tthis.sha,\n\t\t\tthis.branch?.withRepoPath(repoPath, true),\n\t\t\tthis.workspaceFolder,\n\t\t\tthis.branchName,\n\t\t);\n\t}\n\n\tstatic is(worktree: unknown): worktree is GitWorktree {\n\t\treturn worktree instanceof GitWorktree;\n\t}\n\n\tstatic formatDate(worktree: GitWorktreeShape, format?: string | null): string {\n\t\treturn worktree.date != null ? formatDate(worktree.date, format ?? 'MMMM Do, YYYY h:mma') : '';\n\t}\n\n\tstatic formatDateFromNow(worktree: GitWorktreeShape): string {\n\t\treturn worktree.date != null ? fromNow(worktree.date) : '';\n\t}\n\n\tstatic formatDateWithStyle(\n\t\tworktree: GitWorktreeShape,\n\t\tformatting: { dateStyle: string; dateFormat: string | null },\n\t): string {\n\t\treturn formatting.dateStyle === 'absolute'\n\t\t\t? GitWorktree.formatDate(worktree, formatting.dateFormat)\n\t\t\t: GitWorktree.formatDateFromNow(worktree);\n\t}\n\n\tstatic async getStatus(\n\t\tworktree: GitWorktree,\n\t\toptions?: { priority?: GitCommandPriority },\n\t\tcancellation?: AbortSignal,\n\t): Promise<GitStatus | undefined> {\n\t\tif (worktree.type === 'bare') return undefined;\n\n\t\tconst repo = getRepositoryService(worktree.path);\n\t\treturn repo?.status.getStatus(options, cancellation);\n\t}\n\n\tstatic async hasWorkingChanges(\n\t\tworktree: GitWorktree,\n\t\toptions?: { staged?: boolean; unstaged?: boolean; untracked?: boolean },\n\t): Promise<boolean | undefined> {\n\t\tif (worktree.type === 'bare') return undefined;\n\n\t\tconst repo = getRepositoryService(worktree.path);\n\t\treturn repo?.status.hasWorkingChanges(options);\n\t}\n}\n"],"mappings":"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"}
@@ -101,6 +101,23 @@ export interface GitProvider {
101
101
  * provider can't tell, because `repoPath` was never registered with it
102
102
  */
103
103
  getCommonPath?(repoPath: string): string | undefined;
104
+ /**
105
+ * The absolute, normalized folders under `repoPath`'s common git dir whose worktrees core acts as if didn't exist:
106
+ * they are left out of worktree lists, and their add, remove, lock, unlock or move run through `notify: 'infer'`,
107
+ * and the watcher's reports of their admin entries, raise no `worktrees` change (the typed worktree writes still
108
+ * announce one). A host registers them, relative to the common git dir, for a tool's throwaway worktrees, which no
109
+ * host shows (sync-tools' `sync-tools-worktrees`, for one). Each comes both as the common git dir is spelled and as
110
+ * it resolves through symlinks, since git and such tools write resolved paths. `[]` when none is registered; rejects
111
+ * when they can't be read, or not in time.
112
+ */
113
+ getIgnoredWorktreeRoots?(repoPath: string): Promise<readonly string[]>;
114
+ /**
115
+ * The folders of {@link getIgnoredWorktreeRoots}, relative to `repoPath`'s common git dir, as the host registered
116
+ * them less any that isn't a literal path strictly inside it (see `sanitizeIgnoredWorktreeFolders`), read without
117
+ * touching the disk: the watcher leaves them unwatched, unless one shares a directory the `.git` watch reads. `[]`
118
+ * when none is registered.
119
+ */
120
+ getIgnoredWorktreeFolders?(repoPath: string): readonly string[];
104
121
  /**
105
122
  * Fires as the provider announces one of its own writes to the host (`hooks.repository.onChanged`), once for each
106
123
  * path it announces it for, sibling worktrees included — after the write finished and its caches were evicted.
@@ -17,8 +17,23 @@ export interface GitWorktreesSubProvider {
17
17
  force?: boolean;
18
18
  noTracking?: boolean;
19
19
  }, runOptions?: GitOperationRunOptions): Promise<GitWorktree | undefined>;
20
- getWorktree(repoPath: string, predicate: (w: GitWorktree) => boolean, cancellation?: AbortSignal): Promise<GitWorktree | undefined>;
21
- getWorktrees(repoPath: string, cancellation?: AbortSignal): Promise<GitWorktree[]>;
20
+ getWorktree(repoPath: string, predicate: (w: GitWorktree) => boolean,
21
+ /** `force`: searches a fresh list, as {@link getWorktrees} reads it */
22
+ options?: {
23
+ force?: boolean;
24
+ }, cancellation?: AbortSignal): Promise<GitWorktree | undefined>;
25
+ getWorktrees(repoPath: string,
26
+ /**
27
+ * `force`: re-reads `git worktree list` rather than answer from the cached list or a read whose git process
28
+ * started before this call (a pending read that hasn't started one yet is joined), and stores the fresh
29
+ * answer. When the list disagrees with the cached branch list, the branch caches are refreshed once (and
30
+ * a worktree's own when its branch changed); otherwise they are left alone. Not a substitute for a
31
+ * `'worktrees'` reset: with the branch list current it keeps the cached git results and merge targets, which
32
+ * a reset after a sibling worktree's checkout evicts.
33
+ */
34
+ options?: {
35
+ force?: boolean;
36
+ }, cancellation?: AbortSignal): Promise<GitWorktree[]>;
22
37
  getWorktreesDefaultUri(repoPath: string): Uri | undefined;
23
38
  /**
24
39
  * Deletes a worktree. Pass `force: 'locked'` to also override a locked worktree.
@@ -109,6 +109,13 @@ export interface GitRunOptions {
109
109
  * a listed code resolves as `exited` with that `exitCode` in every `errors` mode.
110
110
  */
111
111
  expectedExitCodes?: readonly number[];
112
+ /**
113
+ * Called once, when the git process starts: after any wait in core's queue, and not again for a lock retry. A call
114
+ * that joins an identical run already in flight is told when that run starts, or at once if it already has. Not
115
+ * called for a run that never starts (cancelled while queued), nor for one `caching` answers, even from a run
116
+ * still in flight.
117
+ */
118
+ onSpawn?: () => void;
112
119
  /** Priority level for queue ordering. If not specified, will be inferred from the command type. */
113
120
  priority?: GitCommandPriority;
114
121
  /** Specifies that this command should always be executed locally if possible (for live share sessions) */
@@ -61,13 +61,16 @@ export type ValidateRepoResult = {
61
61
  export declare class GitService implements UnifiedDisposable {
62
62
  private static _instance;
63
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`).
64
+ * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch,
65
+ * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`),
66
+ * and with it `options.readDirectory` lets it drop the reports of ignored worktrees (see
67
+ * `WatchServiceOptions.readDirectory`).
66
68
  * `options.verifyBaseRemeasure` is for tests only (see `WorktreeSummaryServiceOptions.verifyBaseRemeasure`)
67
69
  */
68
70
  static createSingleton(watchingProvider?: FileWatchingProvider, options?: {
69
71
  watchPolling?: WatchPollingOptions;
70
72
  readFile?: (path: string) => Promise<Uint8Array>;
73
+ readDirectory?: (path: string) => Promise<readonly (readonly [string, number])[]>;
71
74
  verifyBaseRemeasure?: WorktreeSummaryServiceOptions['verifyBaseRemeasure'];
72
75
  }): GitService;
73
76
  private readonly _onDidChangeProviders;
@@ -34,13 +34,15 @@ import { WorktreeSummaryService } from "./watching/worktreeSummary.js";
34
34
  var GitService = class GitService {
35
35
  static _instance;
36
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`).
37
+ * `options.watchPolling` opts the watch service into polling what `watchingProvider` reports it can't watch,
38
+ * `options.readFile` lets it tell what a reported `.git/config` change changed (see `WatchServiceOptions.readFile`),
39
+ * and with it `options.readDirectory` lets it drop the reports of ignored worktrees (see
40
+ * `WatchServiceOptions.readDirectory`).
39
41
  * `options.verifyBaseRemeasure` is for tests only (see `WorktreeSummaryServiceOptions.verifyBaseRemeasure`)
40
42
  */
41
43
  static createSingleton(watchingProvider, options) {
42
44
  if (GitService._instance != null) throw new Error("GitService already exists — only one instance is allowed");
43
- const instance = new GitService(watchingProvider, options?.watchPolling, options?.readFile, options?.verifyBaseRemeasure);
45
+ const instance = new GitService(watchingProvider, options?.watchPolling, options?.readFile, options?.readDirectory, options?.verifyBaseRemeasure);
44
46
  GitService._instance = instance;
45
47
  return instance;
46
48
  }
@@ -64,22 +66,26 @@ var GitService = class GitService {
64
66
  _serviceCache = /* @__PURE__ */ new Map();
65
67
  _watchService;
66
68
  _summaries;
67
- constructor(watchingProvider, watchPolling, readFile, verifyBaseRemeasure) {
69
+ constructor(watchingProvider, watchPolling, readFile, readDirectory, verifyBaseRemeasure) {
68
70
  if (watchingProvider != null) {
69
71
  this._watchService = new RepositoryWatchService({
70
72
  watchingProvider,
71
73
  polling: watchPolling,
72
74
  readFile,
75
+ readDirectory,
73
76
  getIgnoreFilter: (repoPath, gitDirPath) => {
74
77
  return this.getProvider(repoPath)?.provider.getIgnoreFilter?.(repoPath, gitDirPath);
75
78
  },
76
- getIgnoredDirectories: (repoPath) => this.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([])
79
+ getIgnoredDirectories: (repoPath) => this.getProvider(repoPath)?.provider.getIgnoredDirectories?.(repoPath) ?? Promise.resolve([]),
80
+ getIgnoredWorktreeRoots: (repoPath) => this.getProvider(repoPath)?.provider.getIgnoredWorktreeRoots?.(repoPath) ?? Promise.resolve([]),
81
+ getIgnoredWorktreeFolders: (repoPath) => this.getProvider(repoPath)?.provider.getIgnoredWorktreeFolders?.(repoPath) ?? []
77
82
  });
78
83
  this._summaries = new WorktreeSummaryService({
79
84
  watchService: this._watchService,
80
85
  getRepository: (repoPath) => this.forRepo(repoPath),
81
86
  onDidChangeMergeTargets: this.onDidChangeMergeTargets,
82
87
  getCommonPath: (repoPath) => this.getProvider(repoPath)?.provider.getCommonPath?.(repoPath),
88
+ stat: watchPolling?.stat,
83
89
  verifyBaseRemeasure
84
90
  });
85
91
  }