@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
|
@@ -113,7 +113,7 @@ export class BranchesGitSubProvider {
|
|
|
113
113
|
? this.provider.pausedOps?.getPausedOperationStatus(repoPath, cancellation)
|
|
114
114
|
: undefined,
|
|
115
115
|
this.git
|
|
116
|
-
.
|
|
116
|
+
.run({
|
|
117
117
|
cwd: repoPath,
|
|
118
118
|
cancellation: cancellation,
|
|
119
119
|
configs: gitConfigsLog,
|
|
@@ -150,7 +150,7 @@ export class BranchesGitSubProvider {
|
|
|
150
150
|
const supported = await this.git.supported('git:for-each-ref');
|
|
151
151
|
const parser = getBranchParser(supported);
|
|
152
152
|
const [gitResult, defaultWorktreePathResult, metadataMapResult] = await Promise.allSettled([
|
|
153
|
-
this.git.
|
|
153
|
+
this.git.run({ cwd: commonPath, cancellation: signal }, 'for-each-ref', ...parser.arguments, 'refs/heads/', 'refs/remotes/'),
|
|
154
154
|
this.provider.config.getDefaultWorktreePath?.(commonPath),
|
|
155
155
|
this.getBranchMetadataMap(commonPath),
|
|
156
156
|
]);
|
|
@@ -286,35 +286,46 @@ export class BranchesGitSubProvider {
|
|
|
286
286
|
}
|
|
287
287
|
async getBranchContributionsOverview(repoPath, ref, options, cancellation) {
|
|
288
288
|
const scope = getScopedLogger();
|
|
289
|
-
|
|
290
|
-
const branch = await this.getBranch(repoPath, ref, cancellation);
|
|
291
|
-
if (branch == null)
|
|
292
|
-
return undefined;
|
|
289
|
+
const getCore = async (_commonPath, _cacheable, signal, storedTarget) => {
|
|
293
290
|
let mergeTarget;
|
|
294
|
-
// Tier 1:
|
|
295
|
-
|
|
291
|
+
// Tier 1: Stored merge target (user-set or previously detected from PR). Caller has
|
|
292
|
+
// already read this once for the cache decision; reuse rather than re-fetching.
|
|
296
293
|
if (storedTarget) {
|
|
297
294
|
const validated = await this.provider.refs.getSymbolicReferenceName(repoPath, storedTarget);
|
|
298
295
|
mergeTarget = validated || storedTarget;
|
|
299
296
|
}
|
|
300
|
-
// Tier 2: PR-based lookup
|
|
297
|
+
// Tier 2: PR-based lookup. The branch object is only needed here (for `remoteName`),
|
|
298
|
+
// so lazy-load it on the cold path instead of paying for it on every call.
|
|
301
299
|
if (mergeTarget == null && options?.associatedPullRequest != null) {
|
|
302
300
|
const pr = await options.associatedPullRequest;
|
|
303
301
|
if (pr?.refs?.base != null) {
|
|
302
|
+
const branch = await this.getBranch(repoPath, ref, signal);
|
|
303
|
+
if (branch == null)
|
|
304
|
+
return undefined;
|
|
304
305
|
mergeTarget = `${branch.remoteName}/${pr.refs.base.branch}`;
|
|
305
|
-
// Store for future reuse
|
|
306
|
+
// Store for future reuse — turns the next call into a Tier 1 cache-eligible path.
|
|
306
307
|
void this.storeMergeTargetBranchName(repoPath, ref, mergeTarget);
|
|
307
308
|
}
|
|
308
309
|
}
|
|
309
310
|
// Tier 3: Base branch, then default branch
|
|
310
|
-
mergeTarget ??= await this.getBaseBranchName(repoPath, ref,
|
|
311
|
-
mergeTarget ??= await this.getDefaultBranchName(repoPath, undefined,
|
|
311
|
+
mergeTarget ??= await this.getBaseBranchName(repoPath, ref, signal);
|
|
312
|
+
mergeTarget ??= await this.getDefaultBranchName(repoPath, undefined, signal);
|
|
312
313
|
if (mergeTarget == null)
|
|
313
314
|
return undefined;
|
|
314
|
-
const mergeBase = await this.provider.refs.getMergeBase(repoPath, ref, mergeTarget, undefined,
|
|
315
|
+
const mergeBase = await this.provider.refs.getMergeBase(repoPath, ref, mergeTarget, undefined, signal);
|
|
315
316
|
if (mergeBase == null)
|
|
316
317
|
return undefined;
|
|
317
|
-
|
|
318
|
+
// Fetch the merge-base commit's dates in parallel with contributors so consumers
|
|
319
|
+
// (e.g. the graph minimap scope window) don't pay a separate round-trip for them.
|
|
320
|
+
// `allSettled` so a transient failure in one side doesn't drop the overview entirely.
|
|
321
|
+
const [contributorsSettled, mergeBaseDatesSettled] = await Promise.allSettled([
|
|
322
|
+
this.provider.contributors.getContributors(repoPath, createRevisionRange(mergeBase, ref, '..'), { stats: true }, signal),
|
|
323
|
+
this.provider.commits.getCommitDates(repoPath, mergeBase, signal),
|
|
324
|
+
]);
|
|
325
|
+
if (signal?.aborted)
|
|
326
|
+
throw new CancellationError();
|
|
327
|
+
const result = getSettledValue(contributorsSettled) ?? { contributors: [] };
|
|
328
|
+
const mergeBaseDates = getSettledValue(mergeBaseDatesSettled);
|
|
318
329
|
sortContributors(result.contributors, { orderBy: 'score:desc' });
|
|
319
330
|
let totalCommits = 0;
|
|
320
331
|
let totalFiles = 0;
|
|
@@ -345,6 +356,7 @@ export class BranchesGitSubProvider {
|
|
|
345
356
|
branch: ref,
|
|
346
357
|
mergeTarget: mergeTarget,
|
|
347
358
|
mergeBase: mergeBase,
|
|
359
|
+
mergeBaseDate: mergeBaseDates?.committerDate,
|
|
348
360
|
commits: totalCommits,
|
|
349
361
|
files: totalFiles,
|
|
350
362
|
additions: totalAdditions,
|
|
@@ -353,6 +365,16 @@ export class BranchesGitSubProvider {
|
|
|
353
365
|
firstCommitDate: firstCommitTimestamp != null ? new Date(firstCommitTimestamp) : undefined,
|
|
354
366
|
contributors: result.contributors,
|
|
355
367
|
};
|
|
368
|
+
};
|
|
369
|
+
try {
|
|
370
|
+
// Pre-check Tier 1: when a stored merge target exists the result is deterministic for
|
|
371
|
+
// `ref` (Tier 2's PR resolution is short-circuited), so it's safe to cache. Otherwise
|
|
372
|
+
// the result depends on PR resolution which the cache key can't capture — bypass.
|
|
373
|
+
const storedTarget = await this.getStoredMergeTargetBranchName(repoPath, ref);
|
|
374
|
+
if (storedTarget != null) {
|
|
375
|
+
return await this.cache.getBranchOverview(repoPath, ref, (commonPath, cacheable, signal) => getCore(commonPath, cacheable, signal ?? cancellation, storedTarget), { cancellation: cancellation });
|
|
376
|
+
}
|
|
377
|
+
return await getCore(repoPath, undefined, cancellation, undefined);
|
|
356
378
|
}
|
|
357
379
|
catch (ex) {
|
|
358
380
|
scope?.error(ex);
|
|
@@ -387,7 +409,7 @@ export class BranchesGitSubProvider {
|
|
|
387
409
|
if (options?.name != null) {
|
|
388
410
|
params.push(options.name);
|
|
389
411
|
}
|
|
390
|
-
return this.git.
|
|
412
|
+
return this.git.run({
|
|
391
413
|
cwd: repoPath,
|
|
392
414
|
cancellation: cancellation,
|
|
393
415
|
configs: gitConfigsBranch,
|
|
@@ -414,7 +436,7 @@ export class BranchesGitSubProvider {
|
|
|
414
436
|
let data;
|
|
415
437
|
let result;
|
|
416
438
|
try {
|
|
417
|
-
result = await this.git.
|
|
439
|
+
result = await this.git.run({ cwd: repoPath, cancellation: signal, errors: 'throw' }, 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@', '@{u}', '--');
|
|
418
440
|
data = [result.stdout, undefined];
|
|
419
441
|
}
|
|
420
442
|
catch (ex) {
|
|
@@ -427,7 +449,7 @@ export class BranchesGitSubProvider {
|
|
|
427
449
|
}
|
|
428
450
|
else {
|
|
429
451
|
try {
|
|
430
|
-
result = await this.git.
|
|
452
|
+
result = await this.git.run({ cwd: repoPath, cancellation: signal }, 'symbolic-ref', '--short', 'HEAD');
|
|
431
453
|
if (result.stdout) {
|
|
432
454
|
data = [result.stdout.trim(), undefined];
|
|
433
455
|
}
|
|
@@ -463,7 +485,7 @@ export class BranchesGitSubProvider {
|
|
|
463
485
|
}
|
|
464
486
|
}
|
|
465
487
|
else if (GitWarnings.headNotABranch.test(msg)) {
|
|
466
|
-
result = await this.git.
|
|
488
|
+
result = await this.git.run({
|
|
467
489
|
cwd: repoPath,
|
|
468
490
|
cancellation: signal,
|
|
469
491
|
configs: gitConfigsLog,
|
|
@@ -502,7 +524,7 @@ export class BranchesGitSubProvider {
|
|
|
502
524
|
let retried = false;
|
|
503
525
|
while (true) {
|
|
504
526
|
try {
|
|
505
|
-
const result = await this.git.
|
|
527
|
+
const result = await this.git.run({ cwd: commonPath, cancellation: cancellation }, 'symbolic-ref', '--short', `refs/remotes/${remote}/HEAD`);
|
|
506
528
|
return result.stdout.trim() || undefined;
|
|
507
529
|
}
|
|
508
530
|
catch (ex) {
|
|
@@ -510,10 +532,10 @@ export class BranchesGitSubProvider {
|
|
|
510
532
|
try {
|
|
511
533
|
if (!retried) {
|
|
512
534
|
retried = true;
|
|
513
|
-
await this.git.
|
|
535
|
+
await this.git.run({ cwd: commonPath, cancellation: cancellation }, 'remote', 'set-head', '-a', remote);
|
|
514
536
|
continue;
|
|
515
537
|
}
|
|
516
|
-
const result = await this.git.
|
|
538
|
+
const result = await this.git.run({ cwd: commonPath, cancellation: cancellation }, 'ls-remote', '--symref', remote, 'HEAD');
|
|
517
539
|
if (result.stdout) {
|
|
518
540
|
const match = /ref:\s(\S+)\s+HEAD/m.exec(result.stdout);
|
|
519
541
|
if (match != null) {
|
|
@@ -538,7 +560,7 @@ export class BranchesGitSubProvider {
|
|
|
538
560
|
args.push('--no-track');
|
|
539
561
|
}
|
|
540
562
|
try {
|
|
541
|
-
await this.git.
|
|
563
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
542
564
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
543
565
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
544
566
|
}
|
|
@@ -555,7 +577,7 @@ export class BranchesGitSubProvider {
|
|
|
555
577
|
const branches = ensureArray(names);
|
|
556
578
|
const args = ['branch', options?.force ? '-D' : '-d', ...branches];
|
|
557
579
|
try {
|
|
558
|
-
await this.git.
|
|
580
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
559
581
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
560
582
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
561
583
|
}
|
|
@@ -578,7 +600,7 @@ export class BranchesGitSubProvider {
|
|
|
578
600
|
const branches = ensureArray(names);
|
|
579
601
|
const args = ['push', '-d', remote, ...branches];
|
|
580
602
|
try {
|
|
581
|
-
await this.git.
|
|
603
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
582
604
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
583
605
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['remotes']);
|
|
584
606
|
}
|
|
@@ -593,42 +615,55 @@ export class BranchesGitSubProvider {
|
|
|
593
615
|
throw new BranchError({ action: 'delete', branch: branches.join(', '), gitCommand: { repoPath: repoPath, args: args } }, ex);
|
|
594
616
|
}
|
|
595
617
|
}
|
|
596
|
-
|
|
618
|
+
getBranchMergedStatus(repoPath, branch, into, cancellation) {
|
|
597
619
|
if (branch.name === into.name || branch.upstream?.name === into.name) {
|
|
598
|
-
return { merged: false };
|
|
620
|
+
return Promise.resolve({ merged: false });
|
|
599
621
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
const
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
622
|
+
// Cache key omits repoPath so worktrees of the same common repo share the cached answer.
|
|
623
|
+
// Branch refs (`refs/heads/*` / `refs/remotes/*`) are unique within a common repo, so
|
|
624
|
+
// `name` + `remote` flag is the minimal stable identity.
|
|
625
|
+
const cacheKey = `${branch.remote ? 'r' : 'l'}:${branch.name}|${into.remote ? 'r' : 'l'}:${into.name}`;
|
|
626
|
+
return this.cache.getBranchMergedStatus(repoPath, cacheKey, async (commonPath, _cacheable, signal) => {
|
|
627
|
+
const result = await this.getBranchMergedStatusCore(commonPath, branch, into, signal);
|
|
628
|
+
if (result.merged)
|
|
629
|
+
return result;
|
|
630
|
+
// If the branch we are checking is a remote branch, check if it has been merged into its local branch (if there is one)
|
|
631
|
+
if (into.remote) {
|
|
632
|
+
const localIntoBranch = await this.getLocalBranchByUpstream(commonPath, into.name, signal);
|
|
633
|
+
// If there is a local branch and it is not the branch we are checking, check if it has been merged into it
|
|
634
|
+
if (localIntoBranch != null && localIntoBranch.name !== branch.name) {
|
|
635
|
+
// Skip the second full merge-check cycle when the local branch points at the same commit as the remote —
|
|
636
|
+
// the merge-base/cherry/diff/apply pipeline would produce the same answer we already got for `into` above
|
|
637
|
+
if (localIntoBranch.sha != null && localIntoBranch.sha === into.sha) {
|
|
638
|
+
return { merged: false };
|
|
639
|
+
}
|
|
640
|
+
const result = await this.getBranchMergedStatusCore(commonPath, branch, localIntoBranch, signal);
|
|
641
|
+
if (result.merged) {
|
|
642
|
+
// `localBranchOnly` is built against `commonPath` here; the cache mapper rewrites
|
|
643
|
+
// `repoPath`/`id` to the requesting worktree on retrieval.
|
|
644
|
+
return {
|
|
645
|
+
...result,
|
|
646
|
+
localBranchOnly: createReference(localIntoBranch.ref, localIntoBranch.repoPath, {
|
|
647
|
+
id: localIntoBranch.id,
|
|
648
|
+
refType: 'branch',
|
|
649
|
+
name: localIntoBranch.name,
|
|
650
|
+
remote: localIntoBranch.remote,
|
|
651
|
+
upstream: localIntoBranch.upstream,
|
|
652
|
+
sha: localIntoBranch.sha,
|
|
653
|
+
}),
|
|
654
|
+
};
|
|
655
|
+
}
|
|
621
656
|
}
|
|
622
657
|
}
|
|
623
|
-
|
|
624
|
-
|
|
658
|
+
return { merged: false };
|
|
659
|
+
}, cancellation);
|
|
625
660
|
}
|
|
626
661
|
async getBranchMergedStatusCore(repoPath, branch, into, cancellation) {
|
|
627
662
|
const scope = getScopedLogger();
|
|
628
663
|
try {
|
|
629
664
|
// Check if branch is direct ancestor (handles FF merges)
|
|
630
665
|
try {
|
|
631
|
-
await this.git.
|
|
666
|
+
await this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, 'merge-base', '--is-ancestor', branch.name, into.name);
|
|
632
667
|
return { merged: true, confidence: 'highest' };
|
|
633
668
|
}
|
|
634
669
|
catch (ex) {
|
|
@@ -636,7 +671,7 @@ export class BranchesGitSubProvider {
|
|
|
636
671
|
throw ex;
|
|
637
672
|
}
|
|
638
673
|
// Cherry-pick detection (handles cherry-picks, rebases, etc)
|
|
639
|
-
let result = await this.git.
|
|
674
|
+
let result = await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'cherry', '--abbrev', '-v', into.name, branch.name);
|
|
640
675
|
// Check if there are no lines or all lines startwith a `-` (i.e. likely merged)
|
|
641
676
|
if (!result.stdout ||
|
|
642
677
|
result.stdout
|
|
@@ -647,14 +682,14 @@ export class BranchesGitSubProvider {
|
|
|
647
682
|
}
|
|
648
683
|
// Attempt to detect squash merges by checking if the diff of the branch can be cleanly removed from the target
|
|
649
684
|
const mergeBase = await this.provider.refs.getMergeBase(repoPath, into.name, branch.name, undefined, cancellation);
|
|
650
|
-
result = await this.git.
|
|
685
|
+
result = await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'diff', mergeBase, branch.name);
|
|
651
686
|
if (result.stdout) {
|
|
652
687
|
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
653
688
|
try {
|
|
654
689
|
// Create a temporary index file
|
|
655
690
|
const disposableIndex = __addDisposableResource(env_2, await this.provider.staging.createTemporaryIndex(repoPath, 'ref', into.name), true);
|
|
656
691
|
const { env } = disposableIndex;
|
|
657
|
-
result = await this.git.
|
|
692
|
+
result = await this.git.run({
|
|
658
693
|
cwd: repoPath,
|
|
659
694
|
cancellation: cancellation,
|
|
660
695
|
env: env,
|
|
@@ -700,7 +735,7 @@ export class BranchesGitSubProvider {
|
|
|
700
735
|
const parentRefs = shas.map(c => `${c}^`);
|
|
701
736
|
let parentShas;
|
|
702
737
|
try {
|
|
703
|
-
const result = await this.git.
|
|
738
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: signal, errors: 'throw' }, 'rev-parse', ...parentRefs);
|
|
704
739
|
parentShas = result.stdout.trim().split('\n');
|
|
705
740
|
}
|
|
706
741
|
catch (ex) {
|
|
@@ -727,7 +762,7 @@ export class BranchesGitSubProvider {
|
|
|
727
762
|
}
|
|
728
763
|
let data;
|
|
729
764
|
try {
|
|
730
|
-
const result = await this.git.
|
|
765
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: signal, errors: 'throw' }, 'merge-tree', '-z', '--name-only', '--no-messages', branch, targetBranch);
|
|
731
766
|
data = result.stdout;
|
|
732
767
|
}
|
|
733
768
|
catch (ex) {
|
|
@@ -780,7 +815,7 @@ export class BranchesGitSubProvider {
|
|
|
780
815
|
let currentTreeOid;
|
|
781
816
|
try {
|
|
782
817
|
// Get the initial target branch tree OID
|
|
783
|
-
const treeResult = await this.git.
|
|
818
|
+
const treeResult = await this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, 'rev-parse', `${targetBranch}^{tree}`);
|
|
784
819
|
currentTreeOid = treeResult.stdout.trim();
|
|
785
820
|
}
|
|
786
821
|
catch (ex) {
|
|
@@ -802,7 +837,7 @@ export class BranchesGitSubProvider {
|
|
|
802
837
|
// - base = commit.parent (where the commit started)
|
|
803
838
|
// - ours = currentTreeOid (current state of the target)
|
|
804
839
|
// - theirs = commit.sha (what we're cherry-picking)
|
|
805
|
-
const result = await this.git.
|
|
840
|
+
const result = await this.git.run({ cwd: repoPath, cancellation: cancellation, errors: 'throw' }, 'merge-tree', '--write-tree', '-z', '--name-only', '--no-messages', `--merge-base=${commit.parent}`, currentTreeOid, commit.sha);
|
|
806
841
|
data = result.stdout;
|
|
807
842
|
}
|
|
808
843
|
catch (ex) {
|
|
@@ -872,37 +907,39 @@ export class BranchesGitSubProvider {
|
|
|
872
907
|
}
|
|
873
908
|
return { status: 'clean' };
|
|
874
909
|
}
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
mergeBase
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
if (
|
|
889
|
-
|
|
910
|
+
getBaseBranchName(repoPath, ref, cancellation) {
|
|
911
|
+
return this.cache.getBaseBranchName(repoPath, ref, async (commonPath, signal) => {
|
|
912
|
+
try {
|
|
913
|
+
// getGkConfig has built-in fallback to regular config for backward compatibility
|
|
914
|
+
let mergeBase = await this.provider.config.getGkConfig(commonPath, `branch.${ref}.gk-merge-base`);
|
|
915
|
+
let update = false;
|
|
916
|
+
// Also check vscode-merge-base in regular config (VS Code compatibility)
|
|
917
|
+
if (mergeBase == null) {
|
|
918
|
+
mergeBase = await this.provider.config.getConfig(commonPath, `branch.${ref}.vscode-merge-base`);
|
|
919
|
+
update = mergeBase != null;
|
|
920
|
+
}
|
|
921
|
+
if (mergeBase != null) {
|
|
922
|
+
const branch = await this.provider.refs.getSymbolicReferenceName(commonPath, mergeBase);
|
|
923
|
+
if (branch != null) {
|
|
924
|
+
if (update) {
|
|
925
|
+
void this.storeBaseBranchName(commonPath, ref, branch);
|
|
926
|
+
}
|
|
927
|
+
return branch;
|
|
890
928
|
}
|
|
891
|
-
return branch;
|
|
892
929
|
}
|
|
893
930
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
931
|
+
catch { }
|
|
932
|
+
const branch = await this.getBaseBranchFromReflog(commonPath, ref, { upstream: true }, signal);
|
|
933
|
+
if (branch != null) {
|
|
934
|
+
void this.storeBaseBranchName(commonPath, ref, branch);
|
|
935
|
+
return branch;
|
|
936
|
+
}
|
|
937
|
+
return undefined;
|
|
938
|
+
}, cancellation);
|
|
902
939
|
}
|
|
903
940
|
async getBaseBranchFromReflog(repoPath, ref, options, cancellation) {
|
|
904
941
|
try {
|
|
905
|
-
let result = await this.git.
|
|
942
|
+
let result = await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'reflog', ref, '--grep-reflog=branch: Created from *.');
|
|
906
943
|
let entries = result.stdout.split('\n').filter(entry => Boolean(entry));
|
|
907
944
|
if (entries.length !== 1)
|
|
908
945
|
return undefined;
|
|
@@ -922,7 +959,7 @@ export class BranchesGitSubProvider {
|
|
|
922
959
|
}
|
|
923
960
|
}
|
|
924
961
|
// Check if branch was created from HEAD
|
|
925
|
-
result = await this.git.
|
|
962
|
+
result = await this.git.run({ cwd: repoPath, cancellation: cancellation }, 'reflog', 'HEAD', `--grep-reflog=checkout: moving from .* to ${ref.replace('refs/heads/', '')}`);
|
|
926
963
|
entries = result.stdout.split('\n').filter(entry => Boolean(entry));
|
|
927
964
|
if (!entries.length)
|
|
928
965
|
return undefined;
|
|
@@ -975,10 +1012,17 @@ export class BranchesGitSubProvider {
|
|
|
975
1012
|
await this.storeBranchMetadata(repoPath, 'gk-last-accessed', branch.name, now);
|
|
976
1013
|
await this.storeBranchMetadata(repoPath, 'gk-last-modified', branch.name, now);
|
|
977
1014
|
}
|
|
1015
|
+
/** Updates the agent last activity timestamp for the current branch */
|
|
1016
|
+
async onCurrentBranchAgentActivity(repoPath) {
|
|
1017
|
+
const branch = await this.getBranch(repoPath);
|
|
1018
|
+
if (branch == null || branch.remote || branch.detached)
|
|
1019
|
+
return;
|
|
1020
|
+
await this.storeBranchMetadata(repoPath, 'gk-agent-last-activity', branch.name, new Date());
|
|
1021
|
+
}
|
|
978
1022
|
async renameBranch(repoPath, oldName, newName) {
|
|
979
1023
|
const args = ['branch', '-m', oldName, newName];
|
|
980
1024
|
try {
|
|
981
|
-
await this.git.
|
|
1025
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
982
1026
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
983
1027
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
984
1028
|
}
|
|
@@ -994,7 +1038,7 @@ export class BranchesGitSubProvider {
|
|
|
994
1038
|
async setUpstreamBranch(repoPath, name, upstream) {
|
|
995
1039
|
const args = upstream == null ? ['branch', '--unset-upstream', name] : ['branch', '--set-upstream-to', upstream, name];
|
|
996
1040
|
try {
|
|
997
|
-
await this.git.
|
|
1041
|
+
await this.git.run({ cwd: repoPath }, ...args);
|
|
998
1042
|
this.context.hooks?.cache?.onReset?.(repoPath, 'branches');
|
|
999
1043
|
this.context.hooks?.repository?.onChanged?.(repoPath, ['heads']);
|
|
1000
1044
|
}
|
|
@@ -1020,47 +1064,52 @@ export class BranchesGitSubProvider {
|
|
|
1020
1064
|
* Gets all branch metadata (dates and disposition) from git config in a single batch operation.
|
|
1021
1065
|
* @returns A map of branch name to metadata.
|
|
1022
1066
|
*/
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
metadata
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
if (
|
|
1054
|
-
metadata.
|
|
1067
|
+
getBranchMetadataMap(repoPath) {
|
|
1068
|
+
return this.cache.getBranchMetadataMap(repoPath, async (commonPath) => {
|
|
1069
|
+
const scope = getScopedLogger();
|
|
1070
|
+
const metadataMap = new Map();
|
|
1071
|
+
try {
|
|
1072
|
+
// Use git config --get-regexp to load all gk-* branch metadata in one call
|
|
1073
|
+
const configMap = await this.provider.config.getGkConfigRegex(commonPath, '^branch\\..*\\.gk-(last-(accessed|modified)|disposition|agent-last-activity)$');
|
|
1074
|
+
if (!configMap.size)
|
|
1075
|
+
return metadataMap;
|
|
1076
|
+
for (const [key, value] of configMap) {
|
|
1077
|
+
// Extract branch name and metadata key from "branch.{name}.gk-{key}"
|
|
1078
|
+
if (!key.startsWith('branch.'))
|
|
1079
|
+
continue;
|
|
1080
|
+
const keyParts = key.split('.');
|
|
1081
|
+
if (keyParts.length < 3)
|
|
1082
|
+
continue;
|
|
1083
|
+
// Branch name is everything between "branch." and the last ".gk-*"
|
|
1084
|
+
const metaKey = keyParts.at(-1);
|
|
1085
|
+
const branchName = keyParts.slice(1, -1).join('.');
|
|
1086
|
+
let metadata = metadataMap.get(branchName);
|
|
1087
|
+
if (metadata == null) {
|
|
1088
|
+
metadata = {};
|
|
1089
|
+
metadataMap.set(branchName, metadata);
|
|
1090
|
+
}
|
|
1091
|
+
if (metaKey === 'gk-last-accessed') {
|
|
1092
|
+
metadata.lastAccessedAt = value;
|
|
1093
|
+
}
|
|
1094
|
+
else if (metaKey === 'gk-last-modified') {
|
|
1095
|
+
metadata.lastModifiedAt = value;
|
|
1096
|
+
}
|
|
1097
|
+
else if (metaKey === 'gk-agent-last-activity') {
|
|
1098
|
+
metadata.agentLastActivityAt = value;
|
|
1099
|
+
}
|
|
1100
|
+
else if (metaKey === 'gk-disposition') {
|
|
1101
|
+
if (value === 'starred' || value === 'archived') {
|
|
1102
|
+
metadata.disposition = value;
|
|
1103
|
+
}
|
|
1055
1104
|
}
|
|
1056
1105
|
}
|
|
1057
1106
|
}
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1107
|
+
catch (ex) {
|
|
1108
|
+
debugger;
|
|
1109
|
+
scope?.error(ex);
|
|
1110
|
+
}
|
|
1111
|
+
return metadataMap;
|
|
1112
|
+
});
|
|
1064
1113
|
}
|
|
1065
1114
|
async storeBranchMetadata(repoPath, key, ref, date) {
|
|
1066
1115
|
const value = await this.provider.config.getGkConfig(repoPath, `branch.${ref}.${key}`);
|
|
@@ -1135,6 +1184,10 @@ __decorate([
|
|
|
1135
1184
|
debounce(2.5 * 60 * 1000),
|
|
1136
1185
|
debug()
|
|
1137
1186
|
], BranchesGitSubProvider.prototype, "onCurrentBranchModified", null);
|
|
1187
|
+
__decorate([
|
|
1188
|
+
debounce(2.5 * 60 * 1000),
|
|
1189
|
+
debug()
|
|
1190
|
+
], BranchesGitSubProvider.prototype, "onCurrentBranchAgentActivity", null);
|
|
1138
1191
|
__decorate([
|
|
1139
1192
|
debug()
|
|
1140
1193
|
], BranchesGitSubProvider.prototype, "renameBranch", null);
|