@gitkraken/core-gitlens 0.1.0 → 0.2.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 +22 -1
- package/dist/git/cache.d.ts +21 -5
- package/dist/git/cache.d.ts.map +1 -1
- package/dist/git/cache.js +102 -6
- package/dist/git/cache.js.map +1 -1
- package/dist/git/context.d.ts +11 -2
- package/dist/git/context.d.ts.map +1 -1
- package/dist/git/context.js.map +1 -1
- package/dist/git/models/branch.d.ts +1 -0
- package/dist/git/models/branch.d.ts.map +1 -1
- package/dist/git/models/branch.js.map +1 -1
- package/dist/git/models/commit.d.ts +1 -9
- package/dist/git/models/commit.d.ts.map +1 -1
- package/dist/git/models/commit.js +4 -9
- package/dist/git/models/commit.js.map +1 -1
- package/dist/git/models/graph.d.ts +2 -4
- package/dist/git/models/graph.d.ts.map +1 -1
- package/dist/git/models/repository.d.ts +12 -3
- package/dist/git/models/repository.d.ts.map +1 -1
- package/dist/git/models/repository.js +25 -4
- package/dist/git/models/repository.js.map +1 -1
- package/dist/git/models/worktree.d.ts +1 -1
- package/dist/git/models/worktree.d.ts.map +1 -1
- package/dist/git/models/worktree.js +2 -2
- package/dist/git/models/worktree.js.map +1 -1
- package/dist/git/parsers/diffParser.d.ts +0 -1
- package/dist/git/parsers/diffParser.d.ts.map +1 -1
- package/dist/git/parsers/diffParser.js +22 -61
- package/dist/git/parsers/diffParser.js.map +1 -1
- package/dist/git/providers/branches.d.ts +3 -0
- package/dist/git/providers/branches.d.ts.map +1 -1
- package/dist/git/providers/commits.d.ts +6 -0
- package/dist/git/providers/commits.d.ts.map +1 -1
- package/dist/git/providers/config.d.ts +2 -0
- package/dist/git/providers/config.d.ts.map +1 -1
- package/dist/git/providers/diff.d.ts +2 -0
- package/dist/git/providers/diff.d.ts.map +1 -1
- package/dist/git/providers/operations.d.ts +6 -0
- package/dist/git/providers/operations.d.ts.map +1 -1
- package/dist/git/providers/revision.d.ts +1 -1
- package/dist/git/providers/revision.d.ts.map +1 -1
- package/dist/git/providers/staging.d.ts +2 -0
- package/dist/git/providers/staging.d.ts.map +1 -1
- package/dist/git/utils/sorting.d.ts +6 -0
- package/dist/git/utils/sorting.d.ts.map +1 -1
- package/dist/git/utils/sorting.js +19 -0
- package/dist/git/utils/sorting.js.map +1 -1
- package/dist/git-cli/cliGitProvider.js +1 -1
- package/dist/git-cli/cliGitProvider.js.map +1 -1
- package/dist/git-cli/exec/git.d.ts +29 -4
- package/dist/git-cli/exec/git.d.ts.map +1 -1
- package/dist/git-cli/exec/git.js +70 -9
- package/dist/git-cli/exec/git.js.map +1 -1
- package/dist/git-cli/exec/gitQueue.d.ts +12 -4
- package/dist/git-cli/exec/gitQueue.d.ts.map +1 -1
- package/dist/git-cli/exec/gitQueue.js +49 -12
- package/dist/git-cli/exec/gitQueue.js.map +1 -1
- package/dist/git-cli/parsers/logParser.d.ts +1 -0
- package/dist/git-cli/parsers/logParser.d.ts.map +1 -1
- package/dist/git-cli/parsers/logParser.js +4 -0
- package/dist/git-cli/parsers/logParser.js.map +1 -1
- package/dist/git-cli/providers/blame.js +1 -1
- package/dist/git-cli/providers/blame.js.map +1 -1
- package/dist/git-cli/providers/branches.d.ts +2 -0
- package/dist/git-cli/providers/branches.d.ts.map +1 -1
- package/dist/git-cli/providers/branches.js +176 -123
- package/dist/git-cli/providers/branches.js.map +1 -1
- package/dist/git-cli/providers/commits.d.ts +4 -0
- package/dist/git-cli/providers/commits.d.ts.map +1 -1
- package/dist/git-cli/providers/commits.js +58 -17
- package/dist/git-cli/providers/commits.js.map +1 -1
- package/dist/git-cli/providers/config.d.ts.map +1 -1
- package/dist/git-cli/providers/config.js +9 -9
- package/dist/git-cli/providers/config.js.map +1 -1
- package/dist/git-cli/providers/contributors.d.ts.map +1 -1
- package/dist/git-cli/providers/contributors.js +7 -4
- package/dist/git-cli/providers/contributors.js.map +1 -1
- package/dist/git-cli/providers/diff.d.ts +2 -0
- package/dist/git-cli/providers/diff.d.ts.map +1 -1
- package/dist/git-cli/providers/diff.js +89 -19
- package/dist/git-cli/providers/diff.js.map +1 -1
- package/dist/git-cli/providers/graph.d.ts.map +1 -1
- package/dist/git-cli/providers/graph.js +43 -27
- package/dist/git-cli/providers/graph.js.map +1 -1
- package/dist/git-cli/providers/operations.d.ts +17 -0
- package/dist/git-cli/providers/operations.d.ts.map +1 -1
- package/dist/git-cli/providers/operations.js +47 -14
- package/dist/git-cli/providers/operations.js.map +1 -1
- package/dist/git-cli/providers/patch.d.ts.map +1 -1
- package/dist/git-cli/providers/patch.js +15 -30
- package/dist/git-cli/providers/patch.js.map +1 -1
- package/dist/git-cli/providers/pausedOperations.js +6 -6
- package/dist/git-cli/providers/pausedOperations.js.map +1 -1
- package/dist/git-cli/providers/refs.d.ts.map +1 -1
- package/dist/git-cli/providers/refs.js +26 -7
- package/dist/git-cli/providers/refs.js.map +1 -1
- package/dist/git-cli/providers/remotes.js +4 -4
- package/dist/git-cli/providers/remotes.js.map +1 -1
- package/dist/git-cli/providers/revision.d.ts +1 -1
- package/dist/git-cli/providers/revision.d.ts.map +1 -1
- package/dist/git-cli/providers/revision.js +15 -11
- package/dist/git-cli/providers/revision.js.map +1 -1
- package/dist/git-cli/providers/staging.d.ts +2 -0
- package/dist/git-cli/providers/staging.d.ts.map +1 -1
- package/dist/git-cli/providers/staging.js +20 -8
- package/dist/git-cli/providers/staging.js.map +1 -1
- package/dist/git-cli/providers/stash.d.ts +0 -8
- package/dist/git-cli/providers/stash.d.ts.map +1 -1
- package/dist/git-cli/providers/stash.js +63 -119
- package/dist/git-cli/providers/stash.js.map +1 -1
- package/dist/git-cli/providers/status.js +6 -6
- package/dist/git-cli/providers/status.js.map +1 -1
- package/dist/git-cli/providers/tags.js +4 -4
- package/dist/git-cli/providers/tags.js.map +1 -1
- package/dist/git-cli/providers/worktrees.js +3 -3
- package/dist/git-cli/providers/worktrees.js.map +1 -1
- package/dist/plus/ai/constants.d.ts +2 -1
- package/dist/plus/ai/constants.d.ts.map +1 -1
- package/dist/plus/ai/constants.js +7 -0
- package/dist/plus/ai/constants.js.map +1 -1
- package/dist/plus/ai/models/model.d.ts +1 -1
- package/dist/plus/ai/models/model.d.ts.map +1 -1
- package/dist/plus/ai/models/model.js.map +1 -1
- package/dist/plus/ai/models/promptTemplates.d.ts +19 -2
- package/dist/plus/ai/models/promptTemplates.d.ts.map +1 -1
- package/dist/plus/ai/models/results.d.ts +28 -0
- package/dist/plus/ai/models/results.d.ts.map +1 -1
- package/dist/plus/ai/prompts.d.ts +3 -0
- package/dist/plus/ai/prompts.d.ts.map +1 -1
- package/dist/plus/ai/prompts.js +126 -0
- package/dist/plus/ai/prompts.js.map +1 -1
- package/dist/plus/ai/utils/ai.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/ai.utils.js +2 -0
- package/dist/plus/ai/utils/ai.utils.js.map +1 -1
- package/dist/plus/ai/utils/results.utils.d.ts +3 -0
- package/dist/plus/ai/utils/results.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/results.utils.js +75 -0
- package/dist/plus/ai/utils/results.utils.js.map +1 -1
- package/dist/plus/ai/utils/truncation.utils.d.ts +1 -1
- package/dist/plus/ai/utils/truncation.utils.d.ts.map +1 -1
- package/dist/plus/ai/utils/truncation.utils.js.map +1 -1
- package/dist/plus/git-github/providers/github/branches.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/branches.js +16 -1
- package/dist/plus/git-github/providers/github/branches.js.map +1 -1
- package/dist/plus/git-github/providers/github/diff.d.ts +2 -0
- package/dist/plus/git-github/providers/github/diff.d.ts.map +1 -1
- package/dist/plus/git-github/providers/github/diff.js.map +1 -1
- package/dist/utils/decorators/log.js +1 -1
- package/dist/utils/decorators/log.js.map +1 -1
- package/dist/utils/decorators/sequentialize.d.ts.map +1 -1
- package/dist/utils/decorators/sequentialize.js +4 -2
- package/dist/utils/decorators/sequentialize.js.map +1 -1
- package/dist/utils/env/node/base64.js.map +1 -1
- package/dist/utils/env/node/exec.js.map +1 -1
- package/dist/utils/formatter.d.ts.map +1 -1
- package/dist/utils/formatter.js +4 -10
- package/dist/utils/formatter.js.map +1 -1
- package/dist/utils/iterable.js.map +1 -1
- package/dist/utils/lruMap.d.ts +40 -0
- package/dist/utils/lruMap.d.ts.map +1 -0
- package/dist/utils/lruMap.js +81 -0
- package/dist/utils/lruMap.js.map +1 -0
- package/dist/utils/version.d.ts +14 -0
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +48 -0
- package/dist/utils/version.js.map +1 -1
- package/package.json +1 -1
- package/src/git/cache.ts +179 -13
- package/src/git/context.ts +11 -2
- package/src/git/models/branch.ts +1 -0
- package/src/git/models/commit.ts +1 -17
- package/src/git/models/graph.ts +2 -4
- package/src/git/models/repository.ts +26 -6
- package/src/git/models/worktree.ts +2 -2
- package/src/git/parsers/diffParser.ts +7 -44
- package/src/git/providers/branches.ts +3 -0
- package/src/git/providers/commits.ts +7 -0
- package/src/git/providers/config.ts +2 -0
- package/src/git/providers/diff.ts +8 -2
- package/src/git/providers/operations.ts +10 -3
- package/src/git/providers/revision.ts +5 -1
- package/src/git/providers/staging.ts +2 -0
- package/src/git/utils/sorting.ts +21 -0
- package/src/git-cli/cliGitProvider.ts +1 -1
- package/src/git-cli/exec/git.ts +70 -18
- package/src/git-cli/exec/gitQueue.ts +57 -13
- package/src/git-cli/parsers/logParser.ts +5 -1
- package/src/git-cli/providers/blame.ts +1 -1
- package/src/git-cli/providers/branches.ts +207 -133
- package/src/git-cli/providers/commits.ts +71 -18
- package/src/git-cli/providers/config.ts +9 -14
- package/src/git-cli/providers/contributors.ts +7 -12
- package/src/git-cli/providers/diff.ts +100 -19
- package/src/git-cli/providers/graph.ts +47 -27
- package/src/git-cli/providers/operations.ts +77 -18
- package/src/git-cli/providers/patch.ts +15 -37
- package/src/git-cli/providers/pausedOperations.ts +6 -6
- package/src/git-cli/providers/refs.ts +27 -8
- package/src/git-cli/providers/remotes.ts +4 -4
- package/src/git-cli/providers/revision.ts +20 -13
- package/src/git-cli/providers/staging.ts +18 -8
- package/src/git-cli/providers/stash.ts +67 -134
- package/src/git-cli/providers/status.ts +6 -6
- package/src/git-cli/providers/tags.ts +4 -4
- package/src/git-cli/providers/worktrees.ts +3 -3
- package/src/plus/ai/constants.ts +8 -0
- package/src/plus/ai/models/model.ts +1 -0
- package/src/plus/ai/models/promptTemplates.ts +29 -0
- package/src/plus/ai/models/results.ts +30 -0
- package/src/plus/ai/prompts.ts +129 -0
- package/src/plus/ai/utils/ai.utils.ts +2 -0
- package/src/plus/ai/utils/results.utils.ts +99 -0
- package/src/plus/ai/utils/truncation.utils.ts +3 -1
- package/src/plus/git-github/providers/github/branches.ts +17 -5
- package/src/plus/git-github/providers/github/diff.ts +8 -2
- package/src/utils/decorators/log.ts +1 -1
- package/src/utils/decorators/sequentialize.ts +8 -6
- package/src/utils/env/node/base64.ts +1 -1
- package/src/utils/env/node/exec.ts +2 -2
- package/src/utils/formatter.ts +4 -11
- package/src/utils/iterable.ts +1 -1
- package/src/utils/lruMap.ts +87 -0
- package/src/utils/version.ts +48 -0
|
@@ -99,7 +99,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
99
99
|
? this.provider.pausedOps?.getPausedOperationStatus(repoPath, cancellation)
|
|
100
100
|
: undefined,
|
|
101
101
|
this.git
|
|
102
|
-
.
|
|
102
|
+
.run(
|
|
103
103
|
{
|
|
104
104
|
cwd: repoPath,
|
|
105
105
|
cancellation: cancellation,
|
|
@@ -172,7 +172,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
172
172
|
const parser = getBranchParser(supported);
|
|
173
173
|
|
|
174
174
|
const [gitResult, defaultWorktreePathResult, metadataMapResult] = await Promise.allSettled([
|
|
175
|
-
this.git.
|
|
175
|
+
this.git.run(
|
|
176
176
|
{ cwd: commonPath, cancellation: signal },
|
|
177
177
|
'for-each-ref',
|
|
178
178
|
...parser.arguments,
|
|
@@ -365,50 +365,60 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
365
365
|
): Promise<BranchContributionsOverview | undefined> {
|
|
366
366
|
const scope = getScopedLogger();
|
|
367
367
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
368
|
+
const getCore = async (
|
|
369
|
+
_commonPath: string,
|
|
370
|
+
_cacheable: CacheController | undefined,
|
|
371
|
+
signal: AbortSignal | undefined,
|
|
372
|
+
storedTarget: string | undefined,
|
|
373
|
+
): Promise<BranchContributionsOverview | undefined> => {
|
|
372
374
|
let mergeTarget: string | undefined;
|
|
373
375
|
|
|
374
|
-
// Tier 1:
|
|
375
|
-
|
|
376
|
+
// Tier 1: Stored merge target (user-set or previously detected from PR). Caller has
|
|
377
|
+
// already read this once for the cache decision; reuse rather than re-fetching.
|
|
376
378
|
if (storedTarget) {
|
|
377
379
|
const validated = await this.provider.refs.getSymbolicReferenceName(repoPath, storedTarget);
|
|
378
380
|
mergeTarget = validated || storedTarget;
|
|
379
381
|
}
|
|
380
382
|
|
|
381
|
-
// Tier 2: PR-based lookup
|
|
383
|
+
// Tier 2: PR-based lookup. The branch object is only needed here (for `remoteName`),
|
|
384
|
+
// so lazy-load it on the cold path instead of paying for it on every call.
|
|
382
385
|
if (mergeTarget == null && options?.associatedPullRequest != null) {
|
|
383
386
|
const pr = await options.associatedPullRequest;
|
|
384
387
|
if (pr?.refs?.base != null) {
|
|
388
|
+
const branch = await this.getBranch(repoPath, ref, signal);
|
|
389
|
+
if (branch == null) return undefined;
|
|
385
390
|
mergeTarget = `${branch.remoteName}/${pr.refs.base.branch}`;
|
|
386
|
-
// Store for future reuse
|
|
391
|
+
// Store for future reuse — turns the next call into a Tier 1 cache-eligible path.
|
|
387
392
|
void this.storeMergeTargetBranchName(repoPath, ref, mergeTarget);
|
|
388
393
|
}
|
|
389
394
|
}
|
|
390
395
|
|
|
391
396
|
// Tier 3: Base branch, then default branch
|
|
392
|
-
mergeTarget ??= await this.getBaseBranchName(repoPath, ref,
|
|
393
|
-
mergeTarget ??= await this.getDefaultBranchName(repoPath, undefined,
|
|
397
|
+
mergeTarget ??= await this.getBaseBranchName(repoPath, ref, signal);
|
|
398
|
+
mergeTarget ??= await this.getDefaultBranchName(repoPath, undefined, signal);
|
|
394
399
|
|
|
395
400
|
if (mergeTarget == null) return undefined;
|
|
396
401
|
|
|
397
|
-
const mergeBase = await this.provider.refs.getMergeBase(
|
|
398
|
-
repoPath,
|
|
399
|
-
ref,
|
|
400
|
-
mergeTarget,
|
|
401
|
-
undefined,
|
|
402
|
-
cancellation,
|
|
403
|
-
);
|
|
402
|
+
const mergeBase = await this.provider.refs.getMergeBase(repoPath, ref, mergeTarget, undefined, signal);
|
|
404
403
|
if (mergeBase == null) return undefined;
|
|
405
404
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
405
|
+
// Fetch the merge-base commit's dates in parallel with contributors so consumers
|
|
406
|
+
// (e.g. the graph minimap scope window) don't pay a separate round-trip for them.
|
|
407
|
+
// `allSettled` so a transient failure in one side doesn't drop the overview entirely.
|
|
408
|
+
const [contributorsSettled, mergeBaseDatesSettled] = await Promise.allSettled([
|
|
409
|
+
this.provider.contributors.getContributors(
|
|
410
|
+
repoPath,
|
|
411
|
+
createRevisionRange(mergeBase, ref, '..'),
|
|
412
|
+
{ stats: true },
|
|
413
|
+
signal,
|
|
414
|
+
),
|
|
415
|
+
this.provider.commits.getCommitDates(repoPath, mergeBase, signal),
|
|
416
|
+
]);
|
|
417
|
+
|
|
418
|
+
if (signal?.aborted) throw new CancellationError();
|
|
419
|
+
|
|
420
|
+
const result = getSettledValue(contributorsSettled) ?? { contributors: [] };
|
|
421
|
+
const mergeBaseDates = getSettledValue(mergeBaseDatesSettled);
|
|
412
422
|
|
|
413
423
|
sortContributors(result.contributors, { orderBy: 'score:desc' });
|
|
414
424
|
|
|
@@ -446,6 +456,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
446
456
|
branch: ref,
|
|
447
457
|
mergeTarget: mergeTarget,
|
|
448
458
|
mergeBase: mergeBase,
|
|
459
|
+
mergeBaseDate: mergeBaseDates?.committerDate,
|
|
449
460
|
|
|
450
461
|
commits: totalCommits,
|
|
451
462
|
files: totalFiles,
|
|
@@ -457,6 +468,23 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
457
468
|
|
|
458
469
|
contributors: result.contributors,
|
|
459
470
|
};
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
// Pre-check Tier 1: when a stored merge target exists the result is deterministic for
|
|
475
|
+
// `ref` (Tier 2's PR resolution is short-circuited), so it's safe to cache. Otherwise
|
|
476
|
+
// the result depends on PR resolution which the cache key can't capture — bypass.
|
|
477
|
+
const storedTarget = await this.getStoredMergeTargetBranchName(repoPath, ref);
|
|
478
|
+
if (storedTarget != null) {
|
|
479
|
+
return await this.cache.getBranchOverview(
|
|
480
|
+
repoPath,
|
|
481
|
+
ref,
|
|
482
|
+
(commonPath, cacheable, signal) =>
|
|
483
|
+
getCore(commonPath, cacheable, signal ?? cancellation, storedTarget),
|
|
484
|
+
{ cancellation: cancellation },
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
return await getCore(repoPath, undefined, cancellation, undefined);
|
|
460
488
|
} catch (ex) {
|
|
461
489
|
scope?.error(ex);
|
|
462
490
|
if (isCancellationError(ex)) throw ex;
|
|
@@ -520,7 +548,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
520
548
|
params.push(options.name);
|
|
521
549
|
}
|
|
522
550
|
|
|
523
|
-
return this.git.
|
|
551
|
+
return this.git.run(
|
|
524
552
|
{
|
|
525
553
|
cwd: repoPath,
|
|
526
554
|
cancellation: cancellation,
|
|
@@ -563,7 +591,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
563
591
|
let data: [string, string | undefined] | undefined;
|
|
564
592
|
let result;
|
|
565
593
|
try {
|
|
566
|
-
result = await this.git.
|
|
594
|
+
result = await this.git.run(
|
|
567
595
|
{ cwd: repoPath, cancellation: signal, errors: 'throw' },
|
|
568
596
|
'rev-parse',
|
|
569
597
|
'--abbrev-ref',
|
|
@@ -582,7 +610,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
582
610
|
data = [ex.stdout, undefined];
|
|
583
611
|
} else {
|
|
584
612
|
try {
|
|
585
|
-
result = await this.git.
|
|
613
|
+
result = await this.git.run(
|
|
586
614
|
{ cwd: repoPath, cancellation: signal },
|
|
587
615
|
'symbolic-ref',
|
|
588
616
|
'--short',
|
|
@@ -629,7 +657,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
629
657
|
}
|
|
630
658
|
}
|
|
631
659
|
} else if (GitWarnings.headNotABranch.test(msg)) {
|
|
632
|
-
result = await this.git.
|
|
660
|
+
result = await this.git.run(
|
|
633
661
|
{
|
|
634
662
|
cwd: repoPath,
|
|
635
663
|
cancellation: signal,
|
|
@@ -687,7 +715,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
687
715
|
let retried = false;
|
|
688
716
|
while (true) {
|
|
689
717
|
try {
|
|
690
|
-
const result = await this.git.
|
|
718
|
+
const result = await this.git.run(
|
|
691
719
|
{ cwd: commonPath, cancellation: cancellation },
|
|
692
720
|
'symbolic-ref',
|
|
693
721
|
'--short',
|
|
@@ -699,7 +727,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
699
727
|
try {
|
|
700
728
|
if (!retried) {
|
|
701
729
|
retried = true;
|
|
702
|
-
await this.git.
|
|
730
|
+
await this.git.run(
|
|
703
731
|
{ cwd: commonPath, cancellation: cancellation },
|
|
704
732
|
'remote',
|
|
705
733
|
'set-head',
|
|
@@ -709,7 +737,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
709
737
|
continue;
|
|
710
738
|
}
|
|
711
739
|
|
|
712
|
-
const result = await this.git.
|
|
740
|
+
const result = await this.git.run(
|
|
713
741
|
{ cwd: commonPath, cancellation: cancellation },
|
|
714
742
|
'ls-remote',
|
|
715
743
|
'--symref',
|
|
@@ -741,7 +769,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
741
769
|
args.push('--no-track');
|
|
742
770
|
}
|
|
743
771
|
try {
|
|
744
|
-
await this.git.
|
|
772
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
745
773
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
746
774
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
747
775
|
} catch (ex) {
|
|
@@ -767,7 +795,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
767
795
|
const branches = ensureArray(names);
|
|
768
796
|
const args = ['branch', options?.force ? '-D' : '-d', ...branches];
|
|
769
797
|
try {
|
|
770
|
-
await this.git.
|
|
798
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
771
799
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
772
800
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
773
801
|
} catch (ex) {
|
|
@@ -795,7 +823,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
795
823
|
const branches = ensureArray(names);
|
|
796
824
|
const args = ['push', '-d', remote, ...branches];
|
|
797
825
|
try {
|
|
798
|
-
await this.git.
|
|
826
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
799
827
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
800
828
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
|
|
801
829
|
} catch (ex) {
|
|
@@ -815,42 +843,67 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
815
843
|
}
|
|
816
844
|
|
|
817
845
|
@debug()
|
|
818
|
-
|
|
846
|
+
getBranchMergedStatus(
|
|
819
847
|
repoPath: string,
|
|
820
848
|
branch: GitBranchReference,
|
|
821
849
|
into: GitBranchReference,
|
|
822
850
|
cancellation?: AbortSignal,
|
|
823
851
|
): Promise<GitBranchMergedStatus> {
|
|
824
852
|
if (branch.name === into.name || branch.upstream?.name === into.name) {
|
|
825
|
-
return { merged: false };
|
|
853
|
+
return Promise.resolve({ merged: false });
|
|
826
854
|
}
|
|
827
855
|
|
|
828
|
-
|
|
829
|
-
|
|
856
|
+
// Cache key omits repoPath so worktrees of the same common repo share the cached answer.
|
|
857
|
+
// Branch refs (`refs/heads/*` / `refs/remotes/*`) are unique within a common repo, so
|
|
858
|
+
// `name` + `remote` flag is the minimal stable identity.
|
|
859
|
+
const cacheKey = `${branch.remote ? 'r' : 'l'}:${branch.name}|${into.remote ? 'r' : 'l'}:${into.name}`;
|
|
830
860
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
}
|
|
848
|
-
|
|
861
|
+
return this.cache.getBranchMergedStatus(
|
|
862
|
+
repoPath,
|
|
863
|
+
cacheKey,
|
|
864
|
+
async (commonPath, _cacheable, signal) => {
|
|
865
|
+
const result = await this.getBranchMergedStatusCore(commonPath, branch, into, signal);
|
|
866
|
+
if (result.merged) return result;
|
|
867
|
+
|
|
868
|
+
// If the branch we are checking is a remote branch, check if it has been merged into its local branch (if there is one)
|
|
869
|
+
if (into.remote) {
|
|
870
|
+
const localIntoBranch = await this.getLocalBranchByUpstream(commonPath, into.name, signal);
|
|
871
|
+
// If there is a local branch and it is not the branch we are checking, check if it has been merged into it
|
|
872
|
+
if (localIntoBranch != null && localIntoBranch.name !== branch.name) {
|
|
873
|
+
// Skip the second full merge-check cycle when the local branch points at the same commit as the remote —
|
|
874
|
+
// the merge-base/cherry/diff/apply pipeline would produce the same answer we already got for `into` above
|
|
875
|
+
if (localIntoBranch.sha != null && localIntoBranch.sha === into.sha) {
|
|
876
|
+
return { merged: false };
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
const result = await this.getBranchMergedStatusCore(
|
|
880
|
+
commonPath,
|
|
881
|
+
branch,
|
|
882
|
+
localIntoBranch,
|
|
883
|
+
signal,
|
|
884
|
+
);
|
|
885
|
+
if (result.merged) {
|
|
886
|
+
// `localBranchOnly` is built against `commonPath` here; the cache mapper rewrites
|
|
887
|
+
// `repoPath`/`id` to the requesting worktree on retrieval.
|
|
888
|
+
return {
|
|
889
|
+
...result,
|
|
890
|
+
localBranchOnly: createReference(localIntoBranch.ref, localIntoBranch.repoPath, {
|
|
891
|
+
id: localIntoBranch.id,
|
|
892
|
+
refType: 'branch',
|
|
893
|
+
name: localIntoBranch.name,
|
|
894
|
+
remote: localIntoBranch.remote,
|
|
895
|
+
upstream: localIntoBranch.upstream,
|
|
896
|
+
sha: localIntoBranch.sha,
|
|
897
|
+
}),
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
}
|
|
849
901
|
}
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
902
|
|
|
853
|
-
|
|
903
|
+
return { merged: false };
|
|
904
|
+
},
|
|
905
|
+
cancellation,
|
|
906
|
+
);
|
|
854
907
|
}
|
|
855
908
|
|
|
856
909
|
private async getBranchMergedStatusCore(
|
|
@@ -864,7 +917,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
864
917
|
try {
|
|
865
918
|
// Check if branch is direct ancestor (handles FF merges)
|
|
866
919
|
try {
|
|
867
|
-
await this.git.
|
|
920
|
+
await this.git.run(
|
|
868
921
|
{ cwd: repoPath, cancellation: cancellation, errors: 'throw' },
|
|
869
922
|
'merge-base',
|
|
870
923
|
'--is-ancestor',
|
|
@@ -877,7 +930,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
877
930
|
}
|
|
878
931
|
|
|
879
932
|
// Cherry-pick detection (handles cherry-picks, rebases, etc)
|
|
880
|
-
let result = await this.git.
|
|
933
|
+
let result = await this.git.run(
|
|
881
934
|
{ cwd: repoPath, cancellation: cancellation },
|
|
882
935
|
'cherry',
|
|
883
936
|
'--abbrev',
|
|
@@ -904,7 +957,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
904
957
|
undefined,
|
|
905
958
|
cancellation,
|
|
906
959
|
);
|
|
907
|
-
result = await this.git.
|
|
960
|
+
result = await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'diff', mergeBase, branch.name);
|
|
908
961
|
if (result.stdout) {
|
|
909
962
|
// Create a temporary index file
|
|
910
963
|
await using disposableIndex = await this.provider.staging.createTemporaryIndex(
|
|
@@ -914,7 +967,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
914
967
|
);
|
|
915
968
|
const { env } = disposableIndex;
|
|
916
969
|
|
|
917
|
-
result = await this.git.
|
|
970
|
+
result = await this.git.run(
|
|
918
971
|
{
|
|
919
972
|
cwd: repoPath,
|
|
920
973
|
cancellation: cancellation,
|
|
@@ -977,7 +1030,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
977
1030
|
const parentRefs = shas.map(c => `${c}^`);
|
|
978
1031
|
let parentShas: string[];
|
|
979
1032
|
try {
|
|
980
|
-
const result = await this.git.
|
|
1033
|
+
const result = await this.git.run(
|
|
981
1034
|
{ cwd: repoPath, cancellation: signal, errors: 'throw' },
|
|
982
1035
|
'rev-parse',
|
|
983
1036
|
...parentRefs,
|
|
@@ -1022,7 +1075,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1022
1075
|
|
|
1023
1076
|
let data;
|
|
1024
1077
|
try {
|
|
1025
|
-
const result = await this.git.
|
|
1078
|
+
const result = await this.git.run(
|
|
1026
1079
|
{ cwd: repoPath, cancellation: signal, errors: 'throw' },
|
|
1027
1080
|
'merge-tree',
|
|
1028
1081
|
'-z',
|
|
@@ -1095,7 +1148,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1095
1148
|
let currentTreeOid: string;
|
|
1096
1149
|
try {
|
|
1097
1150
|
// Get the initial target branch tree OID
|
|
1098
|
-
const treeResult = await this.git.
|
|
1151
|
+
const treeResult = await this.git.run(
|
|
1099
1152
|
{ cwd: repoPath, cancellation: cancellation, errors: 'throw' },
|
|
1100
1153
|
'rev-parse',
|
|
1101
1154
|
`${targetBranch}^{tree}`,
|
|
@@ -1121,7 +1174,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1121
1174
|
// - base = commit.parent (where the commit started)
|
|
1122
1175
|
// - ours = currentTreeOid (current state of the target)
|
|
1123
1176
|
// - theirs = commit.sha (what we're cherry-picking)
|
|
1124
|
-
const result = await this.git.
|
|
1177
|
+
const result = await this.git.run(
|
|
1125
1178
|
{ cwd: repoPath, cancellation: cancellation, errors: 'throw' },
|
|
1126
1179
|
'merge-tree',
|
|
1127
1180
|
'--write-tree',
|
|
@@ -1210,36 +1263,43 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1210
1263
|
}
|
|
1211
1264
|
|
|
1212
1265
|
@debug({ exit: true })
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1266
|
+
getBaseBranchName(repoPath: string, ref: string, cancellation?: AbortSignal): Promise<string | undefined> {
|
|
1267
|
+
return this.cache.getBaseBranchName(
|
|
1268
|
+
repoPath,
|
|
1269
|
+
ref,
|
|
1270
|
+
async (commonPath, signal) => {
|
|
1271
|
+
try {
|
|
1272
|
+
// getGkConfig has built-in fallback to regular config for backward compatibility
|
|
1273
|
+
let mergeBase = await this.provider.config.getGkConfig(commonPath, `branch.${ref}.gk-merge-base`);
|
|
1274
|
+
let update = false;
|
|
1275
|
+
|
|
1276
|
+
// Also check vscode-merge-base in regular config (VS Code compatibility)
|
|
1277
|
+
if (mergeBase == null) {
|
|
1278
|
+
mergeBase = await this.provider.config.getConfig(commonPath, `branch.${ref}.vscode-merge-base`);
|
|
1279
|
+
update = mergeBase != null;
|
|
1280
|
+
}
|
|
1224
1281
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1282
|
+
if (mergeBase != null) {
|
|
1283
|
+
const branch = await this.provider.refs.getSymbolicReferenceName(commonPath, mergeBase);
|
|
1284
|
+
if (branch != null) {
|
|
1285
|
+
if (update) {
|
|
1286
|
+
void this.storeBaseBranchName(commonPath, ref, branch);
|
|
1287
|
+
}
|
|
1288
|
+
return branch;
|
|
1289
|
+
}
|
|
1230
1290
|
}
|
|
1291
|
+
} catch {}
|
|
1292
|
+
|
|
1293
|
+
const branch = await this.getBaseBranchFromReflog(commonPath, ref, { upstream: true }, signal);
|
|
1294
|
+
if (branch != null) {
|
|
1295
|
+
void this.storeBaseBranchName(commonPath, ref, branch);
|
|
1231
1296
|
return branch;
|
|
1232
1297
|
}
|
|
1233
|
-
}
|
|
1234
|
-
} catch {}
|
|
1235
1298
|
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
}
|
|
1241
|
-
|
|
1242
|
-
return undefined;
|
|
1299
|
+
return undefined;
|
|
1300
|
+
},
|
|
1301
|
+
cancellation,
|
|
1302
|
+
);
|
|
1243
1303
|
}
|
|
1244
1304
|
|
|
1245
1305
|
private async getBaseBranchFromReflog(
|
|
@@ -1249,7 +1309,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1249
1309
|
cancellation?: AbortSignal,
|
|
1250
1310
|
): Promise<string | undefined> {
|
|
1251
1311
|
try {
|
|
1252
|
-
let result = await this.git.
|
|
1312
|
+
let result = await this.git.run(
|
|
1253
1313
|
{ cwd: repoPath, cancellation: cancellation },
|
|
1254
1314
|
'reflog',
|
|
1255
1315
|
ref,
|
|
@@ -1275,7 +1335,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1275
1335
|
}
|
|
1276
1336
|
|
|
1277
1337
|
// Check if branch was created from HEAD
|
|
1278
|
-
result = await this.git.
|
|
1338
|
+
result = await this.git.run(
|
|
1279
1339
|
{ cwd: repoPath, cancellation: cancellation },
|
|
1280
1340
|
'reflog',
|
|
1281
1341
|
'HEAD',
|
|
@@ -1347,11 +1407,21 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1347
1407
|
await this.storeBranchMetadata(repoPath, 'gk-last-modified', branch.name, now);
|
|
1348
1408
|
}
|
|
1349
1409
|
|
|
1410
|
+
/** Updates the agent last activity timestamp for the current branch */
|
|
1411
|
+
@debounce(2.5 * 60 * 1000)
|
|
1412
|
+
@debug()
|
|
1413
|
+
async onCurrentBranchAgentActivity(repoPath: string): Promise<void> {
|
|
1414
|
+
const branch = await this.getBranch(repoPath);
|
|
1415
|
+
if (branch == null || branch.remote || branch.detached) return;
|
|
1416
|
+
|
|
1417
|
+
await this.storeBranchMetadata(repoPath, 'gk-agent-last-activity', branch.name, new Date());
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1350
1420
|
@debug()
|
|
1351
1421
|
async renameBranch(repoPath: string, oldName: string, newName: string): Promise<void> {
|
|
1352
1422
|
const args = ['branch', '-m', oldName, newName];
|
|
1353
1423
|
try {
|
|
1354
|
-
await this.git.
|
|
1424
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
1355
1425
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
1356
1426
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
1357
1427
|
} catch (ex) {
|
|
@@ -1377,7 +1447,7 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1377
1447
|
const args =
|
|
1378
1448
|
upstream == null ? ['branch', '--unset-upstream', name] : ['branch', '--set-upstream-to', upstream, name];
|
|
1379
1449
|
try {
|
|
1380
|
-
await this.git.
|
|
1450
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
1381
1451
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
1382
1452
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
1383
1453
|
} catch (ex) {
|
|
@@ -1417,56 +1487,60 @@ export class BranchesGitSubProvider implements GitBranchesSubProvider {
|
|
|
1417
1487
|
* Gets all branch metadata (dates and disposition) from git config in a single batch operation.
|
|
1418
1488
|
* @returns A map of branch name to metadata.
|
|
1419
1489
|
*/
|
|
1420
|
-
private
|
|
1421
|
-
|
|
1422
|
-
|
|
1490
|
+
private getBranchMetadataMap(repoPath: string): Promise<Map<string, BranchMetadata>> {
|
|
1491
|
+
return this.cache.getBranchMetadataMap(repoPath, async commonPath => {
|
|
1492
|
+
const scope = getScopedLogger();
|
|
1493
|
+
const metadataMap = new Map<string, BranchMetadata>();
|
|
1423
1494
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1495
|
+
try {
|
|
1496
|
+
// Use git config --get-regexp to load all gk-* branch metadata in one call
|
|
1497
|
+
const configMap = await this.provider.config.getGkConfigRegex(
|
|
1498
|
+
commonPath,
|
|
1499
|
+
'^branch\\..*\\.gk-(last-(accessed|modified)|disposition|agent-last-activity)$',
|
|
1500
|
+
);
|
|
1501
|
+
if (!configMap.size) return metadataMap;
|
|
1431
1502
|
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1503
|
+
for (const [key, value] of configMap) {
|
|
1504
|
+
// Extract branch name and metadata key from "branch.{name}.gk-{key}"
|
|
1505
|
+
if (!key.startsWith('branch.')) continue;
|
|
1435
1506
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1507
|
+
const keyParts = key.split('.');
|
|
1508
|
+
if (keyParts.length < 3) continue;
|
|
1438
1509
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1510
|
+
// Branch name is everything between "branch." and the last ".gk-*"
|
|
1511
|
+
const metaKey = keyParts.at(-1);
|
|
1512
|
+
const branchName = keyParts.slice(1, -1).join('.');
|
|
1442
1513
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1514
|
+
let metadata = metadataMap.get(branchName);
|
|
1515
|
+
if (metadata == null) {
|
|
1516
|
+
metadata = {};
|
|
1517
|
+
metadataMap.set(branchName, metadata);
|
|
1518
|
+
}
|
|
1448
1519
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1520
|
+
if (metaKey === 'gk-last-accessed') {
|
|
1521
|
+
metadata.lastAccessedAt = value;
|
|
1522
|
+
} else if (metaKey === 'gk-last-modified') {
|
|
1523
|
+
metadata.lastModifiedAt = value;
|
|
1524
|
+
} else if (metaKey === 'gk-agent-last-activity') {
|
|
1525
|
+
metadata.agentLastActivityAt = value;
|
|
1526
|
+
} else if (metaKey === 'gk-disposition') {
|
|
1527
|
+
if (value === 'starred' || value === 'archived') {
|
|
1528
|
+
metadata.disposition = value;
|
|
1529
|
+
}
|
|
1456
1530
|
}
|
|
1457
1531
|
}
|
|
1532
|
+
} catch (ex) {
|
|
1533
|
+
debugger;
|
|
1534
|
+
scope?.error(ex);
|
|
1458
1535
|
}
|
|
1459
|
-
} catch (ex) {
|
|
1460
|
-
debugger;
|
|
1461
|
-
scope?.error(ex);
|
|
1462
|
-
}
|
|
1463
1536
|
|
|
1464
|
-
|
|
1537
|
+
return metadataMap;
|
|
1538
|
+
});
|
|
1465
1539
|
}
|
|
1466
1540
|
|
|
1467
1541
|
private async storeBranchMetadata(
|
|
1468
1542
|
repoPath: string,
|
|
1469
|
-
key: 'gk-last-accessed' | 'gk-last-modified',
|
|
1543
|
+
key: 'gk-last-accessed' | 'gk-last-modified' | 'gk-agent-last-activity',
|
|
1470
1544
|
ref: string,
|
|
1471
1545
|
date: Date,
|
|
1472
1546
|
): Promise<void> {
|