@gitkraken/core-gitlens 0.15.7 → 0.16.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 CHANGED
@@ -6,6 +6,15 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.16.0] - 2026-10-22
10
+
11
+ ### Fixed
12
+
13
+ - Fixes a fetch that moves a worktree's base (usually `origin/main`) making every worktree summary based on it re-read its base, four or five git processes each: when the base ref is the only dependency that moved, by name, and it only fast-forwarded since the last read (by its reflog, since a commit of the branch's that the base held in between moves the fork point), the summary recounts ahead with one `rev-list --count`, past the new tip, the previous fork point and whatever the base's reflog reaches off the new tip's history, and, when the count is unchanged, keeps the target, tier and fork point, updating only the base's commit. The reflog and ancestry check is shared by every worktree of the repository, and anything else (the branch landing, a force-push, another dependency or an unnamed ref change moving, a base name another ref now also answers to, a remote `HEAD` retargeted so the default branch resolves differently, a fork point the base's reflog supplied, possibly since expired, a reflog entry holding the branch's commits, a shallow repository) re-reads the base in full. Adds an optional `GitBranchesSubProvider.getAheadOfBaseAfterBaseMove` for it, and an optional `AheadOfBase.defaultBranch`, the default branch a read resolved (`null` for none), which the re-measure compares and keeps (git, git-cli)
14
+ - Fixes a `.git/config` write that leaves a worktree's base alone making its summary re-read it anyway, and every checkout list its config: a write confined to another branch's section (a task's new branch) woke no summary but left each one's config key stale, so the next unrelated write (`core.*`) re-read every base, 46 git processes over 8 linked worktrees, and any unconfined write listed the config once per checkout. A summary now keys its base on the remotes, `init.defaultBranch`, conditional includes and the tracking of only the branches its base read depended on, and the checkouts of a repository share one `git config` listing, read in the main checkout, unless it shows config that can differ by checkout (`extensions.worktreeConfig`, or an `includeIf` other than `hasconfig:` and `gitdir:`, such as `onbranch:`), when each checkout lists its own as before. A `gitdir:` include stays shared, as the common identity setup, so an edit to a file only some checkouts include goes unseen there until a change the shared listing sees. That write now costs 3 git processes, an unrelated write lists the config once rather than once per checkout, and a fetch after a write to another branch's section costs each linked worktree 1 process rather than 2 or 3. Adds an optional `GitBranchesSubProvider.getAheadOfBaseConfigEntries`, the entries `getAheadOfBaseConfigKey` is taken from, which a summary filters; with a provider without it, a summary keys on the whole snapshot as before (git, git-cli)
15
+ - Fixes `getConfig` and `getConfigRegex` serving every worktree of a repository the answer of whichever one read first, so with `extensions.worktreeConfig` a worktree's own `config.worktree` values (its user identity or signing settings, read by `getCurrentUser` and `getSigningConfig`, or its pull and rebase settings) were missed or taken from another worktree: both are now cached per worktree, and a config write clears every worktree's entries (git, git-cli)
16
+ - Fixes `createWorktree` announcing nothing about a branch git created on its own, from a branch name that exists only on a remote or, with no commitish, named after the path: it now announces `heads` with the branch's `refs/heads/<name>` and resets the branch and config caches, as with `createBranch`, and leaves the tracking config to the watcher's `.git/config` report. An add cancelled, timed out or refused a retry by the host, whose output (and so the name of any branch git created) is lost, announces `heads` unnamed (git-cli)
17
+
9
18
  ## [0.15.7] - 2026-10-22
10
19
 
11
20
  ### Added
@@ -862,7 +871,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
862
871
 
863
872
  - Initial release. Bundles `@gitlens/utils`, `@gitlens/git`, `@gitlens/git-cli`, `@gitlens/ai`, and `@gitlens/git-github` into a single core npm package with subpath exports.
864
873
 
865
- [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.7...HEAD
874
+ [unreleased]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.16.0...HEAD
875
+ [0.16.0]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.7...gitkraken:releases/core/v0.16.0
866
876
  [0.15.7]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.6...gitkraken:releases/core/v0.15.7
867
877
  [0.15.6]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.5...gitkraken:releases/core/v0.15.6
868
878
  [0.15.5]: https://github.com/gitkraken/vscode-gitlens/compare/releases/core/v0.15.4...gitkraken:releases/core/v0.15.5
@@ -110,6 +110,7 @@ export declare class Cache implements Disposable {
110
110
  /** Resource usage retained by instantiated sub-caches and their bookkeeping maps. */
111
111
  getResourceUsage(): ResourceUsage;
112
112
  get aheadOfBaseConfigKey(): PromiseMap<RepoPath, string | undefined>;
113
+ get aheadOfBaseConfigListing(): PromiseMap<RepoPath, string | undefined>;
113
114
  get bestRemotes(): PromiseMap<RepoPath, GitRemote<RemoteProvider>[]>;
114
115
  get blame(): RepoPromiseCacheMap<string, ProgressiveGitBlame | undefined>;
115
116
  get diff(): RepoPromiseCacheMap<string, ParsedGitDiffHunks | undefined>;
@@ -293,7 +294,13 @@ export declare class Cache implements Disposable {
293
294
  getGkConfigSnapshot(repoPath: string): ReadonlyMap<string, string> | undefined;
294
295
  getBranches(repoPath: string, factory: (commonPath: string, cacheable: CacheController, cancellation?: AbortSignal) => PromiseOrValue<PagedResult<GitBranch>>, mapper: (branches: PagedResult<GitBranch>, targetRepoPath: string, commonPath: string, cancellation?: AbortSignal) => PromiseOrValue<PagedResult<GitBranch>>, cancellation?: AbortSignal): Promise<PagedResult<GitBranch>>;
295
296
  private static readonly globalConfigKey;
297
+ /**
298
+ * Keyed by `repoPath` itself, not its common path: git reads a worktree's own `config.worktree` too, so worktrees
299
+ * of one repository can see different values. A read that is the same for all of them runs from, and passes, the
300
+ * common path. The same holds for {@link getConfigRegex}.
301
+ */
296
302
  getConfig(repoPath: string | undefined, key: string, factory: () => PromiseOrValue<string | undefined>): Promise<string | undefined>;
303
+ /** Keyed by `repoPath` itself, as {@link getConfig} is */
297
304
  getConfigRegex(repoPath: string | undefined, pattern: string, factory: () => PromiseOrValue<Map<string, string>>): Promise<Map<string, string>>;
298
305
  deleteConfig(repoPath: string | undefined, key: string): void;
299
306
  /**
@@ -425,6 +432,12 @@ export declare class Cache implements Disposable {
425
432
  getBranchMergedStatus(repoPath: string, cacheKey: string, factory: (commonPath: string, cacheable: CacheController, cancellation?: AbortSignal) => PromiseOrValue<GitBranchMergedStatus>, cancellation?: AbortSignal): Promise<GitBranchMergedStatus>;
426
433
  /** Config can differ by checkout through config.worktree and conditional includes. */
427
434
  getAheadOfBaseConfigKey(repoPath: string, factory: (repoPath: string, cacheable: CacheController, cancellation?: AbortSignal) => PromiseOrValue<string | undefined>, cancellation?: AbortSignal): Promise<string | undefined>;
435
+ /**
436
+ * One listing for every checkout of a repository, which serves a checkout's {@link getAheadOfBaseConfigKey} while
437
+ * nothing makes config differ by checkout. Evicted with it, hard, so a read after a change never joins one from
438
+ * before it.
439
+ */
440
+ getAheadOfBaseConfigListing(repoPath: string, factory: (commonPath: string, cacheable: CacheController, cancellation?: AbortSignal) => PromiseOrValue<string | undefined>, cancellation?: AbortSignal): Promise<string | undefined>;
428
441
  getBranchMetadataMap(repoPath: string, factory: (commonPath: string) => PromiseOrValue<Map<string, BranchMetadata>>): Promise<Map<string, BranchMetadata>>;
429
442
  getDefaultBranchName(repoPath: string, remote: string, factory: (commonPath: string, cacheable: CacheController, cancellation?: AbortSignal) => PromiseOrValue<string | undefined>, cancellation?: AbortSignal): Promise<string | undefined>;
430
443
  /** Clears the cached default branch for one `remote` key (the networked lookup or its `:local` variant). */
package/dist/git/cache.js CHANGED
@@ -54,6 +54,7 @@ function getSettledOrPending(cache, key) {
54
54
  }
55
55
  /** Compile-time enforced: adding a key to SharedCaches without listing it here is a type error */
56
56
  const sharedCacheKeys = new Set(exhaustiveArray()([
57
+ "aheadOfBaseConfigListing",
57
58
  "baseBranchName",
58
59
  "branchMergedStatus",
59
60
  "branchMetadataMap",
@@ -82,6 +83,7 @@ const sharedCacheKeys = new Set(exhaustiveArray()([
82
83
  function createEmptyCaches() {
83
84
  return {
84
85
  aheadOfBaseConfigKey: void 0,
86
+ aheadOfBaseConfigListing: void 0,
85
87
  baseBranchName: void 0,
86
88
  bestRemotes: void 0,
87
89
  blame: void 0,
@@ -187,6 +189,9 @@ var Cache = class Cache {
187
189
  get aheadOfBaseConfigKey() {
188
190
  return this._caches.aheadOfBaseConfigKey ??= new PromiseMap();
189
191
  }
192
+ get aheadOfBaseConfigListing() {
193
+ return this._caches.aheadOfBaseConfigListing ??= new PromiseMap();
194
+ }
190
195
  get bestRemotes() {
191
196
  return this._caches.bestRemotes ??= new PromiseMap();
192
197
  }
@@ -420,6 +425,7 @@ var Cache = class Cache {
420
425
  }
421
426
  if (types.includes("branch")) {
422
427
  keysToClear.add("aheadOfBaseConfigKey");
428
+ keysToClear.add("aheadOfBaseConfigListing");
423
429
  keysToClear.add("baseBranchName");
424
430
  }
425
431
  if (types.includes("branches")) {
@@ -434,6 +440,7 @@ var Cache = class Cache {
434
440
  }
435
441
  if (types.includes("config")) {
436
442
  keysToClear.add("aheadOfBaseConfigKey");
443
+ keysToClear.add("aheadOfBaseConfigListing");
437
444
  keysToClear.add("baseBranchName");
438
445
  keysToClear.add("configKeys");
439
446
  keysToClear.add("configPatterns");
@@ -493,7 +500,7 @@ var Cache = class Cache {
493
500
  else if (key === "aheadOfBaseConfigKey" && !worktreeOnly && (!types.length || types.includes("config"))) this.evictShared(key, repoPath, "evict");
494
501
  else if (sharedCacheKeys.has(key)) {
495
502
  if (!worktreeOnly) {
496
- const hardEvict = key === "gitResults" && (types.includes("config") || types.includes("branch"));
503
+ const hardEvict = key === "aheadOfBaseConfigListing" || key === "gitResults" && (types.includes("config") || types.includes("branch"));
497
504
  this.evictShared(key, repoPath, hardEvict ? "evict" : inFlight);
498
505
  }
499
506
  } else cache.delete(repoPath);
@@ -606,6 +613,7 @@ var Cache = class Cache {
606
613
  if (previousCommonPath != null && previousCommonPath !== commonPath) this.unregisterRepoPath(normalizedPath);
607
614
  if (previousCommonPath !== commonPath) {
608
615
  this._caches.aheadOfBaseConfigKey?.delete(normalizedPath);
616
+ if (normalizedPath === commonPath) this._caches.aheadOfBaseConfigListing?.delete(commonPath);
609
617
  this._caches.gitResults?.delete(normalizedPath);
610
618
  }
611
619
  this._commonPathRegistry.set(normalizedPath, commonPath);
@@ -649,6 +657,10 @@ var Cache = class Cache {
649
657
  if (key === "gitResults" && isLastWorktree && repoPath !== commonPath) cache.delete(commonPath);
650
658
  continue;
651
659
  }
660
+ if (key === "aheadOfBaseConfigListing") {
661
+ if (repoPath === commonPath || isLastWorktree) cache.delete(commonPath);
662
+ continue;
663
+ }
652
664
  const invalidate = cache.invalidate;
653
665
  const evict = typeof invalidate === "function" ? (k) => invalidate.call(cache, k) : (k) => cache.delete(k);
654
666
  if (sharedCacheKeys.has(key)) {
@@ -809,16 +821,22 @@ var Cache = class Cache {
809
821
  }, cancellation);
810
822
  }
811
823
  static globalConfigKey = "";
824
+ /**
825
+ * Keyed by `repoPath` itself, not its common path: git reads a worktree's own `config.worktree` too, so worktrees
826
+ * of one repository can see different values. A read that is the same for all of them runs from, and passes, the
827
+ * common path. The same holds for {@link getConfigRegex}.
828
+ */
812
829
  getConfig(repoPath, key, factory) {
813
- const cacheKey = repoPath != null ? this.getCommonPath(repoPath) : Cache.globalConfigKey;
830
+ const cacheKey = repoPath ?? Cache.globalConfigKey;
814
831
  const result = this.configKeys.get(cacheKey, key);
815
832
  if (result != null) return result;
816
833
  const factoryPromise = Promise.resolve(factory());
817
834
  this.configKeys.set(cacheKey, key, factoryPromise);
818
835
  return factoryPromise;
819
836
  }
837
+ /** Keyed by `repoPath` itself, as {@link getConfig} is */
820
838
  getConfigRegex(repoPath, pattern, factory) {
821
- const cacheKey = repoPath != null ? this.getCommonPath(repoPath) : Cache.globalConfigKey;
839
+ const cacheKey = repoPath ?? Cache.globalConfigKey;
822
840
  const result = this.configPatterns.get(cacheKey, pattern);
823
841
  if (result != null) return result;
824
842
  const factoryPromise = Promise.resolve(factory());
@@ -826,9 +844,20 @@ var Cache = class Cache {
826
844
  return factoryPromise;
827
845
  }
828
846
  deleteConfig(repoPath, key) {
829
- const cacheKey = repoPath != null ? this.getCommonPath(repoPath) : Cache.globalConfigKey;
830
- this._caches.configKeys?.delete(cacheKey, key);
831
- this._caches.configPatterns?.delete(cacheKey);
847
+ if (repoPath == null) {
848
+ this._caches.configKeys?.delete(Cache.globalConfigKey, key);
849
+ this._caches.configPatterns?.delete(Cache.globalConfigKey);
850
+ return;
851
+ }
852
+ const commonPath = this.getCommonPath(repoPath);
853
+ for (const path of /* @__PURE__ */ new Set([
854
+ commonPath,
855
+ repoPath,
856
+ ...this.getWorktreePaths(commonPath)
857
+ ])) {
858
+ this._caches.configKeys?.delete(path, key);
859
+ this._caches.configPatterns?.delete(path);
860
+ }
832
861
  }
833
862
  /**
834
863
  * The whole `.git/gk/config` is read once and cached as a single map per commonPath; per-key and
@@ -1129,6 +1158,14 @@ var Cache = class Cache {
1129
1158
  getAheadOfBaseConfigKey(repoPath, factory, cancellation) {
1130
1159
  return this.aheadOfBaseConfigKey.getOrCreate(repoPath, (cacheable, signal) => Promise.resolve(factory(repoPath, cacheable, signal)), cancellation);
1131
1160
  }
1161
+ /**
1162
+ * One listing for every checkout of a repository, which serves a checkout's {@link getAheadOfBaseConfigKey} while
1163
+ * nothing makes config differ by checkout. Evicted with it, hard, so a read after a change never joins one from
1164
+ * before it.
1165
+ */
1166
+ getAheadOfBaseConfigListing(repoPath, factory, cancellation) {
1167
+ return this.getSharedSimple(this.aheadOfBaseConfigListing, repoPath, factory, cancellation);
1168
+ }
1132
1169
  getBranchMetadataMap(repoPath, factory) {
1133
1170
  return this.getSharedSimple(this.branchMetadataMap, repoPath, factory);
1134
1171
  }