@gitkraken/core-gitlens 0.12.0 → 0.13.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.
- package/CHANGELOG.md +57 -1
- package/dist/git/cache.d.ts +84 -29
- package/dist/git/cache.js +188 -54
- package/dist/git/cache.js.map +1 -1
- package/dist/git/features.d.ts +1 -1
- package/dist/git/features.js +4 -0
- package/dist/git/features.js.map +1 -1
- package/dist/git/models/branch.d.ts +15 -2
- package/dist/git/models/branch.js +27 -5
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/status.js +1 -1
- package/dist/git/models/worktree.d.ts +4 -1
- package/dist/git/models/worktree.js +4 -2
- package/dist/git/models/worktree.js.map +1 -1
- package/dist/git/providers/branches.d.ts +73 -8
- package/dist/git/providers/pausedOperations.d.ts +6 -0
- package/dist/git/providers/provider.d.ts +34 -2
- package/dist/git/providers/status.d.ts +8 -2
- package/dist/git/remotes/azure-devops.js +61 -53
- package/dist/git/remotes/azure-devops.js.map +1 -1
- package/dist/git/service.d.ts +8 -5
- package/dist/git/service.js +16 -8
- package/dist/git/service.js.map +1 -1
- package/dist/git/utils/branch.utils.d.ts +8 -1
- package/dist/git/utils/branch.utils.js +30 -1
- package/dist/git/utils/branch.utils.js.map +1 -1
- package/dist/git/watching/changeEvent.d.ts +13 -2
- package/dist/git/watching/changeEvent.js +17 -3
- package/dist/git/watching/changeEvent.js.map +1 -1
- package/dist/git/watching/gitDirPoller.d.ts +111 -0
- package/dist/git/watching/gitDirPoller.js +266 -0
- package/dist/git/watching/gitDirPoller.js.map +1 -0
- package/dist/git/watching/provider.d.ts +16 -0
- package/dist/git/watching/watchGroup.d.ts +40 -4
- package/dist/git/watching/watchGroup.js +143 -17
- package/dist/git/watching/watchGroup.js.map +1 -1
- package/dist/git/watching/watchService.d.ts +70 -1
- package/dist/git/watching/watchService.js +158 -13
- package/dist/git/watching/watchService.js.map +1 -1
- package/dist/git/watching/watchSession.d.ts +44 -0
- package/dist/git/watching/watchSession.js +91 -6
- package/dist/git/watching/watchSession.js.map +1 -1
- package/dist/git/watching/watcherPatterns.d.ts +21 -0
- package/dist/git/watching/watcherPatterns.js +67 -1
- package/dist/git/watching/watcherPatterns.js.map +1 -1
- package/dist/git/watching/worktreeSummary.d.ts +34 -7
- package/dist/git/watching/worktreeSummary.js +39 -12
- package/dist/git/watching/worktreeSummary.js.map +1 -1
- package/dist/git-cli/cliGitProvider.d.ts +34 -2
- package/dist/git-cli/cliGitProvider.js +119 -17
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/commandClassifier.d.ts +15 -0
- package/dist/git-cli/exec/commandClassifier.js +48 -2
- package/dist/git-cli/exec/commandClassifier.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +15 -3
- package/dist/git-cli/exec/git.js +38 -17
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitQueue.d.ts +2 -0
- package/dist/git-cli/exec/gitQueue.js +2 -1
- package/dist/git-cli/exec/gitQueue.js.map +1 -1
- package/dist/git-cli/exec/indexLock.d.ts +38 -0
- package/dist/git-cli/exec/indexLock.js +150 -0
- package/dist/git-cli/exec/indexLock.js.map +1 -0
- package/dist/git-cli/parsers/worktreeParser.js +42 -2
- package/dist/git-cli/parsers/worktreeParser.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +109 -10
- package/dist/git-cli/providers/branches.js +624 -131
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts +9 -5
- package/dist/git-cli/providers/config.js +60 -27
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +17 -0
- package/dist/git-cli/providers/operations.js +153 -96
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.d.ts +8 -0
- package/dist/git-cli/providers/pausedOperations.js +60 -40
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.js +11 -8
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/remotes.js +19 -10
- package/dist/git-cli/providers/remotes.js.map +1 -1
- package/dist/git-cli/providers/staging.js +40 -15
- package/dist/git-cli/providers/staging.js.map +1 -1
- package/dist/git-cli/providers/stash.d.ts +2 -0
- package/dist/git-cli/providers/stash.js +33 -18
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/status.d.ts +3 -1
- package/dist/git-cli/providers/status.js +38 -20
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/tags.js +6 -4
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/git-cli/providers/worktrees.d.ts +1 -0
- package/dist/git-cli/providers/worktrees.js +60 -31
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/git-cli/service.d.ts +6 -0
- package/dist/git-cli/service.js +5 -2
- package/dist/git-cli/service.js.map +1 -1
- package/dist/ipc/discovery.js +1 -1
- package/dist/plus/agents/providers/claudeCodeTranscript.js +6 -6
- package/dist/plus/agents/providers/claudeCodeTranscript.js.map +1 -1
- package/dist/plus/ai/providers/openAICompatibleProviderBase.js +1 -1
- package/dist/plus/git-github/providers/github/status.d.ts +3 -1
- package/dist/plus/git-github/providers/github/status.js +1 -1
- package/dist/plus/git-github/providers/github/status.js.map +1 -1
- package/dist/plus/integrations/collectionMetadata.d.ts +13 -2
- package/dist/plus/integrations/collectionMetadata.js +14 -2
- package/dist/plus/integrations/collectionMetadata.js.map +1 -1
- package/dist/plus/integrations/index.d.ts +2 -2
- package/dist/plus/integrations/index.js.map +1 -1
- package/dist/plus/integrations/integrationService.d.ts +1 -0
- package/dist/plus/integrations/integrationService.js +1 -1
- package/dist/plus/integrations/integrationService.js.map +1 -1
- package/dist/plus/integrations/manager.d.ts +1 -0
- package/dist/plus/integrations/models/gitHostIntegration.d.ts +41 -5
- package/dist/plus/integrations/models/gitHostIntegration.js +106 -29
- package/dist/plus/integrations/models/gitHostIntegration.js.map +1 -1
- package/dist/plus/integrations/models/issueReads.d.ts +17 -4
- package/dist/plus/integrations/providers/apiClients.d.ts +2 -0
- package/dist/plus/integrations/providers/apiClients.js +7 -1
- package/dist/plus/integrations/providers/apiClients.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.d.ts +5 -4
- package/dist/plus/integrations/providers/azure/azure.js +37 -77
- package/dist/plus/integrations/providers/azure/azure.js.map +1 -1
- package/dist/plus/integrations/providers/azure/azure.utils.js +20 -17
- package/dist/plus/integrations/providers/azure/azure.utils.js.map +1 -1
- package/dist/plus/integrations/providers/azure/models.d.ts +2 -2
- package/dist/plus/integrations/providers/azure/models.js +4 -4
- package/dist/plus/integrations/providers/azure/models.js.map +1 -1
- package/dist/plus/integrations/providers/azure/workItemStateCategories.d.ts +18 -14
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js +69 -17
- package/dist/plus/integrations/providers/azure/workItemStateCategories.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOps.d.ts +44 -0
- package/dist/plus/integrations/providers/azureDevOps.js +108 -17
- package/dist/plus/integrations/providers/azureDevOps.js.map +1 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.d.ts +45 -1
- package/dist/plus/integrations/providers/azureDevOpsServer.js +121 -6
- package/dist/plus/integrations/providers/azureDevOpsServer.js.map +1 -1
- package/dist/plus/integrations/providers/jira-server.js +18 -14
- package/dist/plus/integrations/providers/jira-server.js.map +1 -1
- package/dist/plus/integrations/providers/jira.js +18 -14
- package/dist/plus/integrations/providers/jira.js.map +1 -1
- package/dist/plus/integrations/providers/models.d.ts +1 -2
- package/dist/plus/integrations/providers/models.js +9 -2
- package/dist/plus/integrations/providers/models.js.map +1 -1
- package/dist/plus/integrations/providers/providersApi.d.ts +7 -17
- package/dist/plus/integrations/providers/providersApi.js +85 -78
- package/dist/plus/integrations/providers/providersApi.js.map +1 -1
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.d.ts +22 -0
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js +69 -9
- package/dist/plus/integrations/providers/utils/jiraIssueDrains.js.map +1 -1
- package/dist/plus/integrations/providers/utils/providerPaging.js +8 -2
- package/dist/plus/integrations/providers/utils/providerPaging.js.map +1 -1
- package/dist/plus/integrations/reads/filters.d.ts +3 -3
- package/dist/plus/integrations/reads/filters.js.map +1 -1
- package/dist/plus/integrations/reads/issueBatch.d.ts +2 -1
- package/dist/plus/integrations/reads/issueBatch.js +1 -1
- package/dist/plus/integrations/reads/issueBatch.js.map +1 -1
- package/dist/plus/integrations/reads/issueTracker.js +23 -14
- package/dist/plus/integrations/reads/issueTracker.js.map +1 -1
- package/dist/plus/integrations/reads/issues.d.ts +2 -1
- package/dist/plus/integrations/reads/issues.js +18 -3
- package/dist/plus/integrations/reads/issues.js.map +1 -1
- package/dist/plus/integrations/reads/pullRequestBatch.js +1 -1
- package/dist/plus/integrations/reads/resolveRepository.js +12 -3
- package/dist/plus/integrations/reads/resolveRepository.js.map +1 -1
- package/dist/plus/integrations/reads/warnings.d.ts +11 -4
- package/dist/plus/integrations/reads/warnings.js +15 -5
- package/dist/plus/integrations/reads/warnings.js.map +1 -1
- package/dist/plus/integrations/results.d.ts +36 -2
- package/dist/plus/integrations/results.js +33 -13
- package/dist/plus/integrations/results.js.map +1 -1
- package/dist/utils/env/node/exec.js +2 -2
- package/dist/utils/promiseCache.d.ts +10 -0
- package/dist/utils/promiseCache.js +17 -2
- package/dist/utils/promiseCache.js.map +1 -1
- package/docs/integrations.md +52 -20
- package/package.json +2 -2
package/dist/git/cache.js
CHANGED
|
@@ -6,6 +6,7 @@ import { Emitter } from "../utils/event.js";
|
|
|
6
6
|
import { getBranchId } from "../utils/gitRefs.js";
|
|
7
7
|
import { normalizePath } from "../utils/path.js";
|
|
8
8
|
import { PromiseCache, PromiseMap, RepoPromiseCacheMap } from "../utils/promiseCache.js";
|
|
9
|
+
import { parseBranchMetadataGkConfigKey, setBranchMetadataValue } from "./utils/branch.utils.js";
|
|
9
10
|
import { createReference } from "./utils/reference.utils.js";
|
|
10
11
|
import { getCommonRepositoryPath, getRepositoryOrWorktreePath } from "./utils/repository.utils.js";
|
|
11
12
|
import __decorate from "../_virtual/@oxc-project+runtime/helpers/esm/decorate.js";
|
|
@@ -42,6 +43,15 @@ async function shouldEvictBlameCacheEntry(progressive) {
|
|
|
42
43
|
* Capture group 1 is the ref name (which may itself contain `.`/`/`).
|
|
43
44
|
*/
|
|
44
45
|
const branchOverviewGkConfigKeysRegex = /^branch\.(.+)\.(?:gk-merge-(?:base|target(?:-user)?)|gk-target-base)$/;
|
|
46
|
+
/** The ref of a gkConfig key the reconcile baseline tracks, or `undefined` for any other key */
|
|
47
|
+
function getReconciledGkConfigRef(key) {
|
|
48
|
+
return branchOverviewGkConfigKeysRegex.exec(key)?.[1] ?? parseBranchMetadataGkConfigKey(key)?.branch;
|
|
49
|
+
}
|
|
50
|
+
/** The value `cache` holds for `key`, `'pending'` while it is still being read, or `undefined` when nothing is cached */
|
|
51
|
+
function getSettledOrPending(cache, key) {
|
|
52
|
+
if (!cache?.has(key)) return void 0;
|
|
53
|
+
return cache.getSettled(key) ?? "pending";
|
|
54
|
+
}
|
|
45
55
|
/** Compile-time enforced: adding a key to SharedCaches without listing it here is a type error */
|
|
46
56
|
const sharedCacheKeys = new Set(exhaustiveArray()([
|
|
47
57
|
"baseBranchName",
|
|
@@ -128,18 +138,22 @@ var Cache = class Cache {
|
|
|
128
138
|
/** Monotonic per-path "close clock" — see {@link getCloseGeneration}. */
|
|
129
139
|
_closeGenerations = /* @__PURE__ */ new Map();
|
|
130
140
|
/**
|
|
131
|
-
* Per-commonPath snapshot of the
|
|
132
|
-
* {@link branchOverviewGkConfigKeysRegex})
|
|
133
|
-
* `'gkConfig'` event down to only the keys that actually
|
|
134
|
-
* `baseBranchName`/`branchOverviews` on every gk write.
|
|
141
|
+
* Per-commonPath snapshot of the subset of `.git/gk/config` that derived caches depend on — merge-relevant keys
|
|
142
|
+
* ({@link branchOverviewGkConfigKeysRegex}) and branch metadata keys ({@link parseBranchMetadataGkConfigKey}) —
|
|
143
|
+
* used by {@link reconcileGkConfigMap} to diff a watcher `'gkConfig'` event down to only the keys that actually
|
|
144
|
+
* changed, instead of coarsely re-deriving `baseBranchName`/`branchOverviews` and the branches on every gk write.
|
|
145
|
+
* Absent means "never observed".
|
|
135
146
|
*/
|
|
136
|
-
|
|
147
|
+
_gkConfigSnapshots = /* @__PURE__ */ new Map();
|
|
137
148
|
_onDidChangeMergeTargets = new Emitter();
|
|
138
149
|
/**
|
|
139
|
-
* Fires when
|
|
140
|
-
*
|
|
141
|
-
* {@link recordGkConfigWrite}
|
|
142
|
-
*
|
|
150
|
+
* Fires when a `.git/gk/config` write changed a branch's merge-relevant keys — never for a write that only touched
|
|
151
|
+
* other keys (activity timestamps). Core's own key store fires it once, as the write completes (see
|
|
152
|
+
* {@link recordGkConfigWrite}), and the watcher's later report of that write doesn't fire it again; core's own
|
|
153
|
+
* section remove and rename (a branch delete or rename) never fire it, nor do its internal saves while answering
|
|
154
|
+
* a read (nobody awaits those, and only another process's watcher reports them). Fires after the derived caches were
|
|
155
|
+
* cleared, so a listener's re-read sees the new values. A listener must not write merge keys back in response,
|
|
156
|
+
* or it loops.
|
|
143
157
|
*/
|
|
144
158
|
get onDidChangeMergeTargets() {
|
|
145
159
|
return this._onDidChangeMergeTargets.event;
|
|
@@ -166,7 +180,7 @@ var Cache = class Cache {
|
|
|
166
180
|
usage["cache.worktreesByCommonPath.entries.count"] = this._worktreesByCommonPath.size;
|
|
167
181
|
usage["cache.statusGenerations.entries.count"] = this._statusGenerations.size;
|
|
168
182
|
usage["cache.closeGenerations.entries.count"] = this._closeGenerations.size;
|
|
169
|
-
usage["cache.
|
|
183
|
+
usage["cache.gkConfigSnapshots.entries.count"] = this._gkConfigSnapshots.size;
|
|
170
184
|
return usage;
|
|
171
185
|
}
|
|
172
186
|
get bestRemotes() {
|
|
@@ -365,7 +379,7 @@ var Cache = class Cache {
|
|
|
365
379
|
return this._caches.worktrees ??= new PromiseMap();
|
|
366
380
|
}
|
|
367
381
|
clearCaches(repoPath, ...types) {
|
|
368
|
-
this.clearCachesCore("share", repoPath,
|
|
382
|
+
this.clearCachesCore("share", repoPath, types);
|
|
369
383
|
}
|
|
370
384
|
/**
|
|
371
385
|
* Hard-evicts the same cache keys {@link clearCaches} would clear for `repoPath`, but a shared
|
|
@@ -375,9 +389,10 @@ var Cache = class Cache {
|
|
|
375
389
|
* letting an in-flight read complete and share its answer with new callers is fine.
|
|
376
390
|
*/
|
|
377
391
|
evictCaches(repoPath, ...types) {
|
|
378
|
-
this.clearCachesCore("evict", repoPath,
|
|
392
|
+
this.clearCachesCore("evict", repoPath, types);
|
|
379
393
|
}
|
|
380
|
-
|
|
394
|
+
/** `worktreeOnly` leaves the shared (commonPath-keyed) entries alone (see {@link applyRepositoryChanges}) */
|
|
395
|
+
clearCachesCore(inFlight, repoPath, types, worktreeOnly = false) {
|
|
381
396
|
const keysToClear = /* @__PURE__ */ new Set();
|
|
382
397
|
if (!types.length) {
|
|
383
398
|
for (const key of Object.keys(this._caches)) keysToClear.add(key);
|
|
@@ -467,8 +482,9 @@ var Cache = class Cache {
|
|
|
467
482
|
const cache = this._caches[key];
|
|
468
483
|
if (cache == null) continue;
|
|
469
484
|
if (repoPath == null) cache.clear();
|
|
470
|
-
else if (sharedCacheKeys.has(key))
|
|
471
|
-
|
|
485
|
+
else if (sharedCacheKeys.has(key)) {
|
|
486
|
+
if (!worktreeOnly) this.evictShared(key, repoPath, inFlight);
|
|
487
|
+
} else cache.delete(repoPath);
|
|
472
488
|
}
|
|
473
489
|
}
|
|
474
490
|
/**
|
|
@@ -617,7 +633,7 @@ var Cache = class Cache {
|
|
|
617
633
|
if (isLastWorktree) evict(commonPath);
|
|
618
634
|
} else evict(repoPath);
|
|
619
635
|
}
|
|
620
|
-
if (isLastWorktree) this.
|
|
636
|
+
if (isLastWorktree) this._gkConfigSnapshots.delete(commonPath);
|
|
621
637
|
this._commonPathRegistry.delete(repoPath);
|
|
622
638
|
if (worktrees != null) {
|
|
623
639
|
worktrees.delete(repoPath);
|
|
@@ -629,7 +645,7 @@ var Cache = class Cache {
|
|
|
629
645
|
this._commonPathRegistry.clear();
|
|
630
646
|
this._worktreesByCommonPath.clear();
|
|
631
647
|
this._statusGenerations.clear();
|
|
632
|
-
this.
|
|
648
|
+
this._gkConfigSnapshots.clear();
|
|
633
649
|
this._gkConfigReconciler = void 0;
|
|
634
650
|
this._caches = createEmptyCaches();
|
|
635
651
|
}
|
|
@@ -647,8 +663,13 @@ var Cache = class Cache {
|
|
|
647
663
|
* `'gkConfig'` change, returning the cache types it cleared. `inFlight` is as {@link clearCaches}
|
|
648
664
|
* (`'share'`) vs {@link evictCaches} (`'evict'`). `'unknown'` and `'closed'` map to nothing here: the
|
|
649
665
|
* watcher unregisters the path on them, and an announced write resets everything instead.
|
|
666
|
+
*
|
|
667
|
+
* `options.worktreeOnly` clears only `repoPath`'s own entries and status clock, leaving the shared
|
|
668
|
+
* (commonPath-keyed) ones alone — for a sibling worktree once the change was applied to the worktree it ran in,
|
|
669
|
+
* which already cleared them for every worktree of the repository.
|
|
650
670
|
*/
|
|
651
|
-
applyRepositoryChanges(repoPath, changes, inFlight) {
|
|
671
|
+
applyRepositoryChanges(repoPath, changes, inFlight, options) {
|
|
672
|
+
const worktreeOnly = options?.worktreeOnly ?? false;
|
|
652
673
|
const changesSet = new Set(changes);
|
|
653
674
|
const hasAny = (...c) => c.some((ch) => changesSet.has(ch));
|
|
654
675
|
if (hasAny("index", "head", "heads", "remotes", "ignores", "config")) this.incrementStatusGeneration(repoPath);
|
|
@@ -673,7 +694,7 @@ var Cache = class Cache {
|
|
|
673
694
|
types.add("worktrees");
|
|
674
695
|
}
|
|
675
696
|
if (hasAny("ignores")) types.add("gitignore");
|
|
676
|
-
if (hasAny("gkConfig")) this.handleGkConfigChanged(repoPath);
|
|
697
|
+
if (hasAny("gkConfig") && !worktreeOnly) this.handleGkConfigChanged(repoPath);
|
|
677
698
|
if (hasAny("lastFetched")) types.add("lastFetched");
|
|
678
699
|
if (hasAny("remoteProviders")) types.add("providers");
|
|
679
700
|
if (hasAny("cherryPick", "merge", "rebase", "revert", "pausedOp")) {
|
|
@@ -683,7 +704,7 @@ var Cache = class Cache {
|
|
|
683
704
|
if (hasAny("stash")) types.add("stashes");
|
|
684
705
|
if (hasAny("tags")) types.add("tags");
|
|
685
706
|
if (hasAny("worktrees")) types.add("worktrees");
|
|
686
|
-
if (types.size) this.clearCachesCore(inFlight, repoPath, ...types);
|
|
707
|
+
if (types.size) this.clearCachesCore(inFlight, repoPath, [...types], worktreeOnly);
|
|
687
708
|
return [...types];
|
|
688
709
|
}
|
|
689
710
|
/**
|
|
@@ -701,14 +722,18 @@ var Cache = class Cache {
|
|
|
701
722
|
this.clearGkConfigCoarsely(repoPath);
|
|
702
723
|
return;
|
|
703
724
|
}
|
|
704
|
-
const priorSnapshot = this.
|
|
725
|
+
const priorSnapshot = this._gkConfigSnapshots.get(commonPath);
|
|
705
726
|
Promise.resolve(this._gkConfigReconciler(repoPath, priorSnapshot)).then((outcome) => {
|
|
706
727
|
if (outcome === "failed") this.clearGkConfigCoarsely(repoPath);
|
|
707
728
|
}, () => this.clearGkConfigCoarsely(repoPath));
|
|
708
729
|
}
|
|
709
|
-
/**
|
|
730
|
+
/**
|
|
731
|
+
* The coarse fallback: every derived merge cache and the branches are cleared, so any branch's merge targets or
|
|
732
|
+
* metadata may have changed. Evicting the branches even when only merge keys changed is the price of not knowing.
|
|
733
|
+
*/
|
|
710
734
|
clearGkConfigCoarsely(repoPath) {
|
|
711
735
|
this.clearCaches(repoPath, "gkConfig");
|
|
736
|
+
this.deleteBranchMetadata(repoPath);
|
|
712
737
|
this._onDidChangeMergeTargets.fire({
|
|
713
738
|
repoPath: this.getCommonPath(repoPath),
|
|
714
739
|
refs: void 0
|
|
@@ -718,9 +743,17 @@ var Cache = class Cache {
|
|
|
718
743
|
deleteGkConfigMap(repoPath) {
|
|
719
744
|
this._caches.gkConfigMap?.delete(this.getCommonPath(repoPath));
|
|
720
745
|
}
|
|
721
|
-
/**
|
|
722
|
-
|
|
723
|
-
|
|
746
|
+
/**
|
|
747
|
+
* Whether a write of `key` must first read the gk config to seed the reconcile snapshot: without one,
|
|
748
|
+
* {@link recordGkConfigWrite} can't tell whether the write changed anything, and the watcher's reconcile of it
|
|
749
|
+
* would report every branch as changed.
|
|
750
|
+
*/
|
|
751
|
+
needsGkConfigSnapshot(repoPath, key) {
|
|
752
|
+
return getReconciledGkConfigRef(key) != null && !this._gkConfigSnapshots.has(this.getCommonPath(repoPath));
|
|
753
|
+
}
|
|
754
|
+
/** The gk config snapshot currently serving as the reconcile baseline. */
|
|
755
|
+
getGkConfigSnapshot(repoPath) {
|
|
756
|
+
return this._gkConfigSnapshots.get(this.getCommonPath(repoPath));
|
|
724
757
|
}
|
|
725
758
|
getBranches(repoPath, factory, mapper, cancellation) {
|
|
726
759
|
const commonPath = this.getCommonPath(repoPath);
|
|
@@ -767,20 +800,20 @@ var Cache = class Cache {
|
|
|
767
800
|
* per-namespace gk lookups are served from it in-memory (see `getGkConfigMap` in the CLI config
|
|
768
801
|
* sub-provider). Invalidated by `deleteGkConfig` (write) and, on a watcher `'gkConfig'` event, by
|
|
769
802
|
* `handleGkConfigChanged` (reconciled precisely when a reconciler is registered, or the coarse
|
|
770
|
-
* clear otherwise). The first fresh read seeds the
|
|
803
|
+
* clear otherwise). The first fresh read seeds the snapshot used by
|
|
771
804
|
* `reconcileGkConfigMap`; later refreshes of that snapshot are owned by the reconcile itself.
|
|
772
805
|
*/
|
|
773
806
|
getGkConfigMap(repoPath, factory) {
|
|
774
807
|
return this.getSharedSimple(this.gkConfigMap, repoPath, async (commonPath, cacheable) => {
|
|
775
808
|
const map = await factory(cacheable);
|
|
776
|
-
if (!this.
|
|
809
|
+
if (!this._gkConfigSnapshots.has(commonPath)) this._gkConfigSnapshots.set(commonPath, this.extractReconciledGkConfig(map));
|
|
777
810
|
return map;
|
|
778
811
|
});
|
|
779
812
|
}
|
|
780
|
-
/** Extracts the subset of a gk config map
|
|
781
|
-
|
|
813
|
+
/** Extracts the subset of a gk config map that the reconcile baseline tracks (see {@link _gkConfigSnapshots}). */
|
|
814
|
+
extractReconciledGkConfig(map) {
|
|
782
815
|
const subset = /* @__PURE__ */ new Map();
|
|
783
|
-
for (const [key, value] of map) if (
|
|
816
|
+
for (const [key, value] of map) if (getReconciledGkConfigRef(key) != null) subset.set(key, value);
|
|
784
817
|
return subset;
|
|
785
818
|
}
|
|
786
819
|
/**
|
|
@@ -800,36 +833,44 @@ var Cache = class Cache {
|
|
|
800
833
|
this.cascadeGkConfigKeyChange(commonPath, key, options?.skipInvalidation);
|
|
801
834
|
}
|
|
802
835
|
/**
|
|
803
|
-
* Keeps the
|
|
804
|
-
*
|
|
805
|
-
*
|
|
806
|
-
*
|
|
807
|
-
* the
|
|
836
|
+
* Keeps the gk config snapshot in sync with OUR OWN writes, so the next watcher-triggered
|
|
837
|
+
* `reconcileGkConfigMap` diffs a self-write as unchanged rather than as an external change (which would
|
|
838
|
+
* otherwise defeat `skipInvalidation`, or evict the branches a second time after a branch-activity stamp),
|
|
839
|
+
* and fires {@link onDidChangeMergeTargets} naming the key's branch when a merge-relevant value differs from
|
|
840
|
+
* the snapshot's, unless `announce` is `false` (an internal save while answering a read, which nobody awaits;
|
|
841
|
+
* only another process's watcher reports it). No-ops for keys the snapshot doesn't track, and if no snapshot exists yet for this repo
|
|
842
|
+
* (the writer seeds one first; see {@link needsGkConfigSnapshot}).
|
|
808
843
|
*/
|
|
809
|
-
recordGkConfigWrite(repoPath, key, value) {
|
|
810
|
-
|
|
844
|
+
recordGkConfigWrite(repoPath, key, value, options) {
|
|
845
|
+
const ref = getReconciledGkConfigRef(key);
|
|
846
|
+
if (ref == null) return;
|
|
811
847
|
const commonPath = this.getCommonPath(repoPath);
|
|
812
|
-
const snapshot = this.
|
|
813
|
-
if (snapshot == null) return;
|
|
848
|
+
const snapshot = this._gkConfigSnapshots.get(commonPath);
|
|
849
|
+
if (snapshot == null || snapshot.get(key) === value) return;
|
|
814
850
|
const next = new Map(snapshot);
|
|
815
851
|
if (value == null) next.delete(key);
|
|
816
852
|
else next.set(key, value);
|
|
817
|
-
this.
|
|
853
|
+
this._gkConfigSnapshots.set(commonPath, next);
|
|
854
|
+
if (options?.announce === false || !branchOverviewGkConfigKeysRegex.test(key)) return;
|
|
855
|
+
this._onDidChangeMergeTargets.fire({
|
|
856
|
+
repoPath: commonPath,
|
|
857
|
+
refs: /* @__PURE__ */ new Set([ref])
|
|
858
|
+
});
|
|
818
859
|
}
|
|
819
860
|
/**
|
|
820
861
|
* {@link recordGkConfigWrite} for a whole `branch.<ref>` section: our own `--remove-section` (a branch delete) or
|
|
821
862
|
* `--rename-section` (a branch rename, to `renamedTo`), so the reconcile that follows doesn't report it as an
|
|
822
|
-
* external merge-target change.
|
|
863
|
+
* external merge-target or metadata change — the branch op's own ref change already evicts the branches.
|
|
823
864
|
*/
|
|
824
865
|
recordGkConfigSectionWrite(repoPath, ref, renamedTo) {
|
|
825
866
|
const commonPath = this.getCommonPath(repoPath);
|
|
826
|
-
const snapshot = this.
|
|
867
|
+
const snapshot = this._gkConfigSnapshots.get(commonPath);
|
|
827
868
|
if (snapshot == null) return;
|
|
828
869
|
const prefixLength = `branch.${ref}.`.length;
|
|
829
870
|
const next = /* @__PURE__ */ new Map();
|
|
830
|
-
for (const [key, value] of snapshot) if (
|
|
871
|
+
for (const [key, value] of snapshot) if (getReconciledGkConfigRef(key) !== ref) next.set(key, value);
|
|
831
872
|
else if (renamedTo != null) next.set(`branch.${renamedTo}.${key.substring(prefixLength)}`, value);
|
|
832
|
-
this.
|
|
873
|
+
this._gkConfigSnapshots.set(commonPath, next);
|
|
833
874
|
}
|
|
834
875
|
/**
|
|
835
876
|
* Registers the reconciler invoked by {@link handleGkConfigChanged} on a watcher-observed
|
|
@@ -841,35 +882,63 @@ var Cache = class Cache {
|
|
|
841
882
|
this._gkConfigReconciler = reconciler;
|
|
842
883
|
}
|
|
843
884
|
/**
|
|
844
|
-
* Diffs a freshly-read gk config map's
|
|
845
|
-
* `handleGkConfigChanged` before the reconcile started, so it reflects the pre-change state
|
|
846
|
-
* though this call's own re-read already raced ahead and refreshed the live snapshot), cascading
|
|
885
|
+
* Diffs a freshly-read gk config map's tracked subset against `priorSnapshot` (captured by
|
|
886
|
+
* `handleGkConfigChanged` before the reconcile started, so it reflects the pre-change state), cascading
|
|
847
887
|
* `baseBranchName`/`branchOverviews` for every ref whose merge-relevant key actually changed, then firing
|
|
848
|
-
* {@link onDidChangeMergeTargets} naming those refs.
|
|
888
|
+
* {@link onDidChangeMergeTargets} naming those refs. Changed branch metadata keys (another process's
|
|
889
|
+
* activity stamps or disposition) are patched into the cached branches in one call, and never fire the event.
|
|
890
|
+
* A key core itself wrote since `priorSnapshot` was captured (recorded in the live snapshot) is neither diffed nor
|
|
891
|
+
* overwritten by this read, which may predate the write: its writer already cascaded and announced it.
|
|
849
892
|
* `priorSnapshot` of `undefined` (never observed before this reconcile) is treated as "everything
|
|
850
893
|
* may have changed" — a full coarse clear, same as the pre-existing watcher behavior. Returns
|
|
851
894
|
* whether anything changed (i.e. whether a follow-up repository-changed event is warranted).
|
|
852
895
|
*/
|
|
853
896
|
reconcileGkConfigMap(repoPath, priorSnapshot, freshMap) {
|
|
854
897
|
const commonPath = this.getCommonPath(repoPath);
|
|
855
|
-
const newSubset = this.
|
|
856
|
-
this._gkConfigMergeSnapshots.set(commonPath, newSubset);
|
|
898
|
+
const newSubset = this.extractReconciledGkConfig(freshMap);
|
|
857
899
|
if (priorSnapshot == null) {
|
|
900
|
+
this._gkConfigSnapshots.set(commonPath, newSubset);
|
|
858
901
|
this.evictShared("baseBranchName", repoPath);
|
|
859
902
|
this.evictShared("branchOverviews", repoPath);
|
|
903
|
+
this.deleteBranchMetadata(repoPath);
|
|
860
904
|
this._onDidChangeMergeTargets.fire({
|
|
861
905
|
repoPath: commonPath,
|
|
862
906
|
refs: void 0
|
|
863
907
|
});
|
|
864
908
|
return true;
|
|
865
909
|
}
|
|
910
|
+
const ownWrites = /* @__PURE__ */ new Set();
|
|
911
|
+
const live = this._gkConfigSnapshots.get(commonPath);
|
|
912
|
+
if (live != null && live !== priorSnapshot) for (const key of /* @__PURE__ */ new Set([...priorSnapshot.keys(), ...live.keys()])) {
|
|
913
|
+
const value = live.get(key);
|
|
914
|
+
if (priorSnapshot.get(key) === value) continue;
|
|
915
|
+
ownWrites.add(key);
|
|
916
|
+
if (value == null) newSubset.delete(key);
|
|
917
|
+
else newSubset.set(key, value);
|
|
918
|
+
}
|
|
919
|
+
this._gkConfigSnapshots.set(commonPath, newSubset);
|
|
866
920
|
let refs;
|
|
867
|
-
|
|
868
|
-
|
|
921
|
+
const metadataChanges = [];
|
|
922
|
+
for (const key of /* @__PURE__ */ new Set([...priorSnapshot.keys(), ...newSubset.keys()])) {
|
|
923
|
+
if (ownWrites.has(key)) continue;
|
|
924
|
+
const value = newSubset.get(key);
|
|
925
|
+
if (priorSnapshot.get(key) === value) continue;
|
|
869
926
|
const ref = branchOverviewGkConfigKeysRegex.exec(key)?.[1];
|
|
870
|
-
if (ref != null)
|
|
927
|
+
if (ref != null) {
|
|
928
|
+
this.cascadeGkConfigKeyChange(commonPath, key);
|
|
929
|
+
(refs ??= /* @__PURE__ */ new Set()).add(ref);
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
const metadataKey = parseBranchMetadataGkConfigKey(key);
|
|
933
|
+
if (metadataKey != null) metadataChanges.push({
|
|
934
|
+
branch: metadataKey.branch,
|
|
935
|
+
key: metadataKey.key,
|
|
936
|
+
value
|
|
937
|
+
});
|
|
871
938
|
}
|
|
872
|
-
|
|
939
|
+
const metadataChanged = metadataChanges.length > 0;
|
|
940
|
+
if (metadataChanged) this.patchBranchMetadata(repoPath, metadataChanges);
|
|
941
|
+
if (refs == null) return metadataChanged;
|
|
873
942
|
this._onDidChangeMergeTargets.fire({
|
|
874
943
|
repoPath: commonPath,
|
|
875
944
|
refs
|
|
@@ -931,6 +1000,71 @@ var Cache = class Cache {
|
|
|
931
1000
|
deleteBaseBranchName(repoPath, ref) {
|
|
932
1001
|
this._caches.baseBranchName?.delete(this.getCommonPath(repoPath), ref);
|
|
933
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Applies changed branch metadata keys (written here or reconciled from another process) to the cached branches
|
|
1005
|
+
* without re-reading them, by updating {@link BranchMetadata} objects in place — the one exception to cached values
|
|
1006
|
+
* never changing, since rebuilding every branch list, current branch and worktree list on every activity stamp
|
|
1007
|
+
* costs far more. Patches every object a cached branch of the repository's worktrees holds for a changed branch:
|
|
1008
|
+
* the cached map's, and any older map's that a cached branch list, current branch or worktree list still holds.
|
|
1009
|
+
*
|
|
1010
|
+
* Falls back to {@link deleteBranchMetadata} when one of them is still being read (it may have read the file
|
|
1011
|
+
* before the change), or the cached map or a cached branch of a changed name has no object to patch.
|
|
1012
|
+
*/
|
|
1013
|
+
patchBranchMetadata(repoPath, changes) {
|
|
1014
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1015
|
+
for (const change of changes) targets.set(change.branch, /* @__PURE__ */ new Set());
|
|
1016
|
+
if (!this.collectBranchMetadata(this.getCommonPath(repoPath), targets)) {
|
|
1017
|
+
this.deleteBranchMetadata(repoPath);
|
|
1018
|
+
return;
|
|
1019
|
+
}
|
|
1020
|
+
for (const change of changes) for (const metadata of targets.get(change.branch)) setBranchMetadataValue(metadata, change.key, change.value);
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Adds to `targets` every metadata object a cached holder has for each named branch (see
|
|
1024
|
+
* {@link patchBranchMetadata}); `false` when the holders must be evicted instead.
|
|
1025
|
+
*/
|
|
1026
|
+
collectBranchMetadata(commonPath, targets) {
|
|
1027
|
+
const metadataMap = getSettledOrPending(this._caches.branchMetadataMap, commonPath);
|
|
1028
|
+
if (metadataMap === "pending") return false;
|
|
1029
|
+
if (metadataMap != null) for (const [name, objects] of targets) {
|
|
1030
|
+
const metadata = metadataMap.value.get(name);
|
|
1031
|
+
if (metadata == null) return false;
|
|
1032
|
+
objects.add(metadata);
|
|
1033
|
+
}
|
|
1034
|
+
const add = (branch) => {
|
|
1035
|
+
if (branch == null || branch.remote || branch.detached) return true;
|
|
1036
|
+
const objects = targets.get(branch.name);
|
|
1037
|
+
if (objects == null) return true;
|
|
1038
|
+
if (branch.metadata == null) return false;
|
|
1039
|
+
objects.add(branch.metadata);
|
|
1040
|
+
return true;
|
|
1041
|
+
};
|
|
1042
|
+
for (const path of /* @__PURE__ */ new Set([commonPath, ...this.getWorktreePaths(commonPath)])) {
|
|
1043
|
+
for (const cache of [this._caches.sharedBranches, this._caches.branches]) {
|
|
1044
|
+
const branches = getSettledOrPending(cache, path);
|
|
1045
|
+
if (branches === "pending" || branches != null && !branches.value.values.every(add)) return false;
|
|
1046
|
+
}
|
|
1047
|
+
const current = getSettledOrPending(this._caches.branch, path);
|
|
1048
|
+
if (current === "pending" || current != null && !add(current.value)) return false;
|
|
1049
|
+
const worktrees = getSettledOrPending(this._caches.worktrees, path);
|
|
1050
|
+
if (worktrees === "pending" || worktrees != null && !worktrees.value.every((w) => add(w.branch))) return false;
|
|
1051
|
+
}
|
|
1052
|
+
return true;
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Drops the cached branches and worktrees for `repoPath` and its sibling worktrees when their metadata changed in
|
|
1056
|
+
* a way {@link patchBranchMetadata} can't apply in place. Hard-evicts, so a read that started before the change
|
|
1057
|
+
* isn't joined.
|
|
1058
|
+
*/
|
|
1059
|
+
deleteBranchMetadata(repoPath) {
|
|
1060
|
+
for (const key of [
|
|
1061
|
+
"branchMetadataMap",
|
|
1062
|
+
"sharedBranches",
|
|
1063
|
+
"branches",
|
|
1064
|
+
"branch",
|
|
1065
|
+
"worktrees"
|
|
1066
|
+
]) this.evictShared(key, repoPath, "evict");
|
|
1067
|
+
}
|
|
934
1068
|
getBranchMergedStatus(repoPath, cacheKey, factory, cancellation) {
|
|
935
1069
|
return this.getSharedOrCreateWithKey(this.branchMergedStatus, repoPath, cacheKey, factory, (data, newRepoPath) => {
|
|
936
1070
|
if (!data.merged) return data;
|