@solaqua/gji 0.13.0 → 0.14.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/dist/application/worktree/go-resolution.js +5 -3
- package/dist/application/worktree/sources.d.ts +13 -4
- package/dist/application/worktree/sources.js +34 -13
- package/dist/cli/commands/clean.d.ts +3 -2
- package/dist/cli/commands/clean.js +299 -65
- package/dist/cli/commands/go.d.ts +2 -1
- package/dist/cli/commands/go.js +17 -13
- package/dist/cli/commands/hub.js +4 -1
- package/dist/cli/commands/warp.js +6 -4
- package/dist/domain/shared/concurrency.d.ts +1 -1
- package/dist/domain/shared/concurrency.js +6 -1
- package/dist/gji-bundle.mjs +492 -225
- package/dist/infrastructure/git/refs.js +3 -11
- package/dist/presentation/worktree/picker.d.ts +9 -6
- package/dist/presentation/worktree/picker.js +19 -2
- package/man/man1/gji-back.1 +1 -1
- package/man/man1/gji-clean.1 +1 -1
- package/man/man1/gji-completion.1 +1 -1
- package/man/man1/gji-config.1 +1 -1
- package/man/man1/gji-doctor.1 +1 -1
- package/man/man1/gji-done.1 +1 -1
- package/man/man1/gji-go.1 +1 -1
- package/man/man1/gji-history.1 +1 -1
- package/man/man1/gji-init.1 +1 -1
- package/man/man1/gji-ls.1 +1 -1
- package/man/man1/gji-new.1 +1 -1
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +1 -1
- package/man/man1/gji-remove.1 +1 -1
- package/man/man1/gji-root.1 +1 -1
- package/man/man1/gji-run-hook.1 +1 -1
- package/man/man1/gji-status.1 +1 -1
- package/man/man1/gji-sync-files.1 +1 -1
- package/man/man1/gji-sync.1 +1 -1
- package/man/man1/gji-task.1 +1 -1
- package/man/man1/gji-undo.1 +1 -1
- package/man/man1/gji-warp.1 +1 -1
- package/man/man1/gji.1 +1 -1
- package/package.json +1 -1
package/dist/gji-bundle.mjs
CHANGED
|
@@ -12810,7 +12810,7 @@ function updateNotifier(options) {
|
|
|
12810
12810
|
// package.json
|
|
12811
12811
|
var package_default = {
|
|
12812
12812
|
name: "@solaqua/gji",
|
|
12813
|
-
version: "0.
|
|
12813
|
+
version: "0.14.0",
|
|
12814
12814
|
description: "Git worktree CLI for fast context switching.",
|
|
12815
12815
|
license: "MIT",
|
|
12816
12816
|
author: "sjquant",
|
|
@@ -13931,16 +13931,7 @@ async function resolveCachedRemoteDefaultBranch(cwd, remote) {
|
|
|
13931
13931
|
const prefix = `${remote}/`;
|
|
13932
13932
|
return ref.startsWith(prefix) ? ref.slice(prefix.length) : void 0;
|
|
13933
13933
|
} catch {
|
|
13934
|
-
|
|
13935
|
-
const refs = await runGit(cwd, [
|
|
13936
|
-
"for-each-ref",
|
|
13937
|
-
"--format=%(refname:strip=3)",
|
|
13938
|
-
`refs/remotes/${remote}`
|
|
13939
|
-
]);
|
|
13940
|
-
return refs.split("\n").find((ref) => ref !== "HEAD");
|
|
13941
|
-
} catch {
|
|
13942
|
-
return void 0;
|
|
13943
|
-
}
|
|
13934
|
+
return void 0;
|
|
13944
13935
|
}
|
|
13945
13936
|
}
|
|
13946
13937
|
async function readBranchLastCommitTimestamp(cwd, branch) {
|
|
@@ -15294,11 +15285,12 @@ function toErrorMessage2(error) {
|
|
|
15294
15285
|
}
|
|
15295
15286
|
|
|
15296
15287
|
// src/domain/shared/concurrency.ts
|
|
15297
|
-
async function mapWithConcurrency(items, limit, mapper) {
|
|
15288
|
+
async function mapWithConcurrency(items, limit, mapper, signal) {
|
|
15298
15289
|
const results = new Array(items.length);
|
|
15299
15290
|
let nextIndex = 0;
|
|
15300
15291
|
async function readNext() {
|
|
15301
15292
|
for (; ; ) {
|
|
15293
|
+
throwIfAborted(signal);
|
|
15302
15294
|
const index = nextIndex;
|
|
15303
15295
|
nextIndex += 1;
|
|
15304
15296
|
if (index >= items.length) return;
|
|
@@ -15310,6 +15302,9 @@ async function mapWithConcurrency(items, limit, mapper) {
|
|
|
15310
15302
|
);
|
|
15311
15303
|
return results;
|
|
15312
15304
|
}
|
|
15305
|
+
function throwIfAborted(signal) {
|
|
15306
|
+
if (signal?.aborted) throw new Error("Operation cancelled");
|
|
15307
|
+
}
|
|
15313
15308
|
|
|
15314
15309
|
// src/infrastructure/worktree/info.ts
|
|
15315
15310
|
var MAX_WORKTREE_INFO_READ_CONCURRENCY = 8;
|
|
@@ -16401,6 +16396,90 @@ function sortPullRequests2(pullRequests) {
|
|
|
16401
16396
|
);
|
|
16402
16397
|
}
|
|
16403
16398
|
|
|
16399
|
+
// src/application/worktree/sources.ts
|
|
16400
|
+
var MAX_REPOSITORY_DISCOVERY_CONCURRENCY = 4;
|
|
16401
|
+
async function listRegisteredWorktreeSources(options) {
|
|
16402
|
+
const { cwd, repositoryPort, signal } = options;
|
|
16403
|
+
throwIfAborted2(signal);
|
|
16404
|
+
const registry = await repositoryPort.loadRegistry();
|
|
16405
|
+
const currentRoot = await repositoryPort.detectRepository(cwd).then((repository) => repository.currentRoot).catch(() => null);
|
|
16406
|
+
throwIfAborted2(signal);
|
|
16407
|
+
const results = await mapWithConcurrency(
|
|
16408
|
+
registry,
|
|
16409
|
+
MAX_REPOSITORY_DISCOVERY_CONCURRENCY,
|
|
16410
|
+
async (entry) => {
|
|
16411
|
+
throwIfAborted2(signal);
|
|
16412
|
+
try {
|
|
16413
|
+
const worktrees = await repositoryPort.listWorktrees(entry.path);
|
|
16414
|
+
return { entry, skipped: false, worktrees };
|
|
16415
|
+
} catch {
|
|
16416
|
+
return { entry, skipped: true, worktrees: [] };
|
|
16417
|
+
}
|
|
16418
|
+
},
|
|
16419
|
+
signal
|
|
16420
|
+
);
|
|
16421
|
+
const allItems = [];
|
|
16422
|
+
const skipped = [];
|
|
16423
|
+
for (const result of results) {
|
|
16424
|
+
if (result.skipped) {
|
|
16425
|
+
skipped.push(result.entry);
|
|
16426
|
+
continue;
|
|
16427
|
+
}
|
|
16428
|
+
const { entry, worktrees } = result;
|
|
16429
|
+
for (const worktree of worktrees) {
|
|
16430
|
+
allItems.push({
|
|
16431
|
+
repoRoot: entry.path,
|
|
16432
|
+
repoName: entry.name,
|
|
16433
|
+
worktree: {
|
|
16434
|
+
...worktree,
|
|
16435
|
+
isCurrent: currentRoot !== null && worktree.path === currentRoot
|
|
16436
|
+
}
|
|
16437
|
+
});
|
|
16438
|
+
}
|
|
16439
|
+
}
|
|
16440
|
+
return { skipped, sources: allItems };
|
|
16441
|
+
}
|
|
16442
|
+
async function listDiscoverableWorktreeSources(options) {
|
|
16443
|
+
const { cwd, repositoryPort } = options;
|
|
16444
|
+
const currentRepository = await repositoryPort.detectRepository(cwd).catch(() => null);
|
|
16445
|
+
const registered = await listRegisteredWorktreeSources(options);
|
|
16446
|
+
if (currentRepository === null) {
|
|
16447
|
+
return {
|
|
16448
|
+
skipped: registered.skipped,
|
|
16449
|
+
sources: deduplicateWorktreeSources(registered.sources)
|
|
16450
|
+
};
|
|
16451
|
+
}
|
|
16452
|
+
let currentSources = [];
|
|
16453
|
+
try {
|
|
16454
|
+
currentSources = (await repositoryPort.listWorktrees(cwd)).map(
|
|
16455
|
+
(worktree) => ({
|
|
16456
|
+
repoName: currentRepository.repoName,
|
|
16457
|
+
repoRoot: currentRepository.repoRoot,
|
|
16458
|
+
worktree
|
|
16459
|
+
})
|
|
16460
|
+
);
|
|
16461
|
+
} catch {
|
|
16462
|
+
}
|
|
16463
|
+
return {
|
|
16464
|
+
skipped: registered.skipped,
|
|
16465
|
+
sources: deduplicateWorktreeSources([
|
|
16466
|
+
...currentSources,
|
|
16467
|
+
...registered.sources
|
|
16468
|
+
])
|
|
16469
|
+
};
|
|
16470
|
+
}
|
|
16471
|
+
function deduplicateWorktreeSources(sources) {
|
|
16472
|
+
const seen = /* @__PURE__ */ new Set();
|
|
16473
|
+
return sources.filter((source) => {
|
|
16474
|
+
if (seen.has(source.worktree.path)) return false;
|
|
16475
|
+
seen.add(source.worktree.path);
|
|
16476
|
+
return true;
|
|
16477
|
+
});
|
|
16478
|
+
}
|
|
16479
|
+
function throwIfAborted2(signal) {
|
|
16480
|
+
if (signal?.aborted) throw new Error("Operation cancelled");
|
|
16481
|
+
}
|
|
16482
|
+
|
|
16404
16483
|
// src/presentation/worktree/picker.ts
|
|
16405
16484
|
import { env as env4, platform as platform2, stdin, stdout } from "node:process";
|
|
16406
16485
|
|
|
@@ -16501,6 +16580,7 @@ async function promptForSingleWorktree(message, worktrees, io = {}) {
|
|
|
16501
16580
|
message,
|
|
16502
16581
|
multiple: false,
|
|
16503
16582
|
output: io.output,
|
|
16583
|
+
catalog: io.catalog,
|
|
16504
16584
|
scope: io.scope
|
|
16505
16585
|
});
|
|
16506
16586
|
return typeof choice === "string" ? choice : null;
|
|
@@ -16511,7 +16591,9 @@ async function promptForMultipleWorktrees(message, worktrees, io = {}) {
|
|
|
16511
16591
|
input: io.input,
|
|
16512
16592
|
message,
|
|
16513
16593
|
multiple: true,
|
|
16514
|
-
output: io.output
|
|
16594
|
+
output: io.output,
|
|
16595
|
+
catalog: io.catalog,
|
|
16596
|
+
scope: io.scope
|
|
16515
16597
|
});
|
|
16516
16598
|
return Array.isArray(choice) ? choice : null;
|
|
16517
16599
|
}
|
|
@@ -16600,6 +16682,7 @@ var SearchablePrompt = class {
|
|
|
16600
16682
|
scope;
|
|
16601
16683
|
scopeTogglePending = false;
|
|
16602
16684
|
promptClosed = false;
|
|
16685
|
+
cancellation = new AbortController();
|
|
16603
16686
|
prompt;
|
|
16604
16687
|
run() {
|
|
16605
16688
|
return this.prompt.prompt();
|
|
@@ -16650,9 +16733,14 @@ var SearchablePrompt = class {
|
|
|
16650
16733
|
this.scopeTogglePending = true;
|
|
16651
16734
|
renderPrompt(prompt);
|
|
16652
16735
|
try {
|
|
16653
|
-
const nextScope = await currentScope.toggle();
|
|
16736
|
+
const nextScope = await currentScope.toggle(this.cancellation.signal);
|
|
16654
16737
|
if (this.promptClosed) return;
|
|
16655
|
-
|
|
16738
|
+
const nextEntries = await buildWorktreePromptEntries(nextScope.sources, {
|
|
16739
|
+
catalog: this.options.catalog,
|
|
16740
|
+
metadata: nextScope.metadata
|
|
16741
|
+
});
|
|
16742
|
+
this.entries = nextEntries.map(buildSearchableWorktreeEntry);
|
|
16743
|
+
this.pruneHiddenSelections();
|
|
16656
16744
|
this.scope = {
|
|
16657
16745
|
label: nextScope.label,
|
|
16658
16746
|
toggle: currentScope.toggle,
|
|
@@ -16683,6 +16771,7 @@ var SearchablePrompt = class {
|
|
|
16683
16771
|
}
|
|
16684
16772
|
cancel(prompt) {
|
|
16685
16773
|
this.promptClosed = true;
|
|
16774
|
+
this.cancellation.abort();
|
|
16686
16775
|
prompt.state = "cancel";
|
|
16687
16776
|
renderPrompt(prompt);
|
|
16688
16777
|
closePrompt(prompt);
|
|
@@ -16773,6 +16862,14 @@ var SearchablePrompt = class {
|
|
|
16773
16862
|
}
|
|
16774
16863
|
this.selected.add(entry.value);
|
|
16775
16864
|
}
|
|
16865
|
+
pruneHiddenSelections() {
|
|
16866
|
+
const visibleValues = new Set(
|
|
16867
|
+
this.entries.filter(isSelectableEntry).map((entry) => entry.value)
|
|
16868
|
+
);
|
|
16869
|
+
for (const value of this.selected) {
|
|
16870
|
+
if (!visibleValues.has(value)) this.selected.delete(value);
|
|
16871
|
+
}
|
|
16872
|
+
}
|
|
16776
16873
|
render(state, error) {
|
|
16777
16874
|
const entries = this.visibleEntries();
|
|
16778
16875
|
const visibleEntries = windowPromptEntries(
|
|
@@ -17790,6 +17887,15 @@ function emitUndoError(options, message) {
|
|
|
17790
17887
|
}
|
|
17791
17888
|
|
|
17792
17889
|
// src/cli/commands/clean.ts
|
|
17890
|
+
function toPromptSource(candidate) {
|
|
17891
|
+
return {
|
|
17892
|
+
repoName: candidate.repoName,
|
|
17893
|
+
repoRoot: candidate.repoRoot,
|
|
17894
|
+
worktree: candidate.worktree
|
|
17895
|
+
};
|
|
17896
|
+
}
|
|
17897
|
+
var MAX_CLEAN_REPOSITORY_CONCURRENCY = 4;
|
|
17898
|
+
var MAX_CLEAN_WORKTREE_CONCURRENCY = 8;
|
|
17793
17899
|
function createCleanCommand(dependencies = {}) {
|
|
17794
17900
|
const promptForWorktrees = dependencies.promptForWorktrees ?? defaultPromptForWorktrees;
|
|
17795
17901
|
const confirmRemoval = dependencies.confirmRemoval ?? defaultConfirmRemoval;
|
|
@@ -17800,7 +17906,7 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17800
17906
|
const {
|
|
17801
17907
|
readWorktreeHealth: readWorktreeHealth2,
|
|
17802
17908
|
isBranchMergedInto: isBranchMergedInto2,
|
|
17803
|
-
|
|
17909
|
+
resolveRemoteBase: resolveRemoteBase2,
|
|
17804
17910
|
runGit: runGit2
|
|
17805
17911
|
} = runtime.git;
|
|
17806
17912
|
const { loadEffectiveConfig: loadEffectiveConfig2 } = runtime.config;
|
|
@@ -17830,24 +17936,98 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17830
17936
|
options.cwd,
|
|
17831
17937
|
sourceDependencies
|
|
17832
17938
|
);
|
|
17833
|
-
const
|
|
17834
|
-
|
|
17939
|
+
const currentSources = linkedWorktrees.filter((worktree) => worktree.path !== repository.currentRoot).map((worktree) => ({
|
|
17940
|
+
repoName: repository.repoName,
|
|
17941
|
+
repoRoot: repository.repoRoot,
|
|
17942
|
+
worktree
|
|
17943
|
+
}));
|
|
17944
|
+
const cleanupDependencies = {
|
|
17945
|
+
isBranchMergedInto: isBranchMergedInto2,
|
|
17946
|
+
loadEffectiveConfig: loadEffectiveConfig2,
|
|
17947
|
+
readWorktreeHealth: readWorktreeHealth2,
|
|
17948
|
+
resolveRemoteBase: resolveRemoteBase2,
|
|
17949
|
+
runGit: runGit2
|
|
17950
|
+
};
|
|
17951
|
+
const currentCandidates = await resolveCleanupCandidates(
|
|
17952
|
+
{
|
|
17953
|
+
sources: currentSources,
|
|
17954
|
+
stale: options.stale,
|
|
17955
|
+
stderr: options.stderr
|
|
17956
|
+
},
|
|
17957
|
+
cleanupDependencies
|
|
17835
17958
|
);
|
|
17836
|
-
|
|
17837
|
-
|
|
17838
|
-
|
|
17839
|
-
|
|
17840
|
-
|
|
17841
|
-
|
|
17842
|
-
|
|
17843
|
-
|
|
17844
|
-
|
|
17845
|
-
|
|
17846
|
-
|
|
17847
|
-
|
|
17848
|
-
|
|
17849
|
-
|
|
17850
|
-
|
|
17959
|
+
let activeCandidates = currentCandidates;
|
|
17960
|
+
let allCandidates = null;
|
|
17961
|
+
let currentScope = true;
|
|
17962
|
+
const interactiveSelection = !options.force && !options.json && !isHeadless();
|
|
17963
|
+
const skippedRepositories = [];
|
|
17964
|
+
const loadAllCandidates = async (signal) => {
|
|
17965
|
+
throwIfAborted3(signal);
|
|
17966
|
+
if (allCandidates !== null) return allCandidates;
|
|
17967
|
+
const registered = await listRegisteredWorktreeSources({
|
|
17968
|
+
cwd: options.cwd,
|
|
17969
|
+
repositoryPort: sourceDependencies,
|
|
17970
|
+
signal
|
|
17971
|
+
});
|
|
17972
|
+
skippedRepositories.push(...registered.skipped);
|
|
17973
|
+
if (registered.skipped.length > 0) {
|
|
17974
|
+
reportSkippedRepositories(skippedRepositories, options.stderr);
|
|
17975
|
+
}
|
|
17976
|
+
const sources = deduplicateWorktreeSources([
|
|
17977
|
+
...currentSources,
|
|
17978
|
+
...registered.sources
|
|
17979
|
+
]).filter(
|
|
17980
|
+
(source) => source.repoRoot !== void 0 && source.worktree.path !== source.repoRoot && source.worktree.path !== repository.currentRoot
|
|
17981
|
+
);
|
|
17982
|
+
const otherRepositorySources = sources.filter(
|
|
17983
|
+
(source) => source.repoRoot !== repository.repoRoot
|
|
17984
|
+
);
|
|
17985
|
+
const otherRepositoryCandidates = await resolveCleanupCandidates(
|
|
17986
|
+
{
|
|
17987
|
+
sources: otherRepositorySources,
|
|
17988
|
+
stale: options.stale,
|
|
17989
|
+
stderr: options.stderr,
|
|
17990
|
+
signal
|
|
17991
|
+
},
|
|
17992
|
+
cleanupDependencies
|
|
17993
|
+
);
|
|
17994
|
+
allCandidates = [...currentCandidates, ...otherRepositoryCandidates];
|
|
17995
|
+
throwIfAborted3(signal);
|
|
17996
|
+
return allCandidates;
|
|
17997
|
+
};
|
|
17998
|
+
const scope = {
|
|
17999
|
+
label: "current repository",
|
|
18000
|
+
toggleLabel: "all repositories",
|
|
18001
|
+
toggle: async (signal) => {
|
|
18002
|
+
const nextCurrentScope = !currentScope;
|
|
18003
|
+
const nextCandidates = nextCurrentScope ? currentCandidates : await loadAllCandidates(signal);
|
|
18004
|
+
throwIfAborted3(signal);
|
|
18005
|
+
currentScope = nextCurrentScope;
|
|
18006
|
+
activeCandidates = nextCandidates;
|
|
18007
|
+
return {
|
|
18008
|
+
sources: nextCandidates.map(toPromptSource),
|
|
18009
|
+
metadata: nextCurrentScope ? "full" : "fast",
|
|
18010
|
+
label: nextCurrentScope ? "current repository" : "all repositories",
|
|
18011
|
+
toggleLabel: nextCurrentScope ? "all repositories" : "current repository"
|
|
18012
|
+
};
|
|
18013
|
+
}
|
|
18014
|
+
};
|
|
18015
|
+
if (currentCandidates.length === 0 && interactiveSelection) {
|
|
18016
|
+
const loadedAllCandidates = await loadAllCandidates();
|
|
18017
|
+
if (loadedAllCandidates.length === 0) {
|
|
18018
|
+
if (options.stale) {
|
|
18019
|
+
emitNoStaleCandidates(options);
|
|
18020
|
+
return 0;
|
|
18021
|
+
}
|
|
18022
|
+
emitError(options, "No linked worktrees to clean");
|
|
18023
|
+
return 1;
|
|
18024
|
+
}
|
|
18025
|
+
currentScope = false;
|
|
18026
|
+
activeCandidates = loadedAllCandidates;
|
|
18027
|
+
scope.label = "all repositories";
|
|
18028
|
+
scope.toggleLabel = "current repository";
|
|
18029
|
+
}
|
|
18030
|
+
if (currentCandidates.length === 0 && !interactiveSelection) {
|
|
17851
18031
|
if (options.stale) {
|
|
17852
18032
|
emitNoStaleCandidates(options);
|
|
17853
18033
|
return 0;
|
|
@@ -17868,27 +18048,29 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17868
18048
|
return 1;
|
|
17869
18049
|
}
|
|
17870
18050
|
const shouldSelectAll = options.force || options.dryRun && (options.stale || options.json || isHeadless());
|
|
17871
|
-
const selections = shouldSelectAll ?
|
|
17872
|
-
await buildWorktreePromptEntries(
|
|
17873
|
-
|
|
17874
|
-
|
|
17875
|
-
|
|
17876
|
-
|
|
17877
|
-
|
|
17878
|
-
)
|
|
18051
|
+
const selections = shouldSelectAll ? activeCandidates.map(({ worktree }) => worktree.path) : await promptForWorktrees(
|
|
18052
|
+
await buildWorktreePromptEntries(activeCandidates, {
|
|
18053
|
+
metadata: currentScope ? "full" : "fast",
|
|
18054
|
+
catalog: runtime.worktreeCatalog
|
|
18055
|
+
}),
|
|
18056
|
+
scope,
|
|
18057
|
+
runtime.worktreeCatalog
|
|
17879
18058
|
);
|
|
17880
18059
|
if (!selections || selections.length === 0) {
|
|
17881
18060
|
options.stderr("Aborted\n");
|
|
17882
18061
|
return 1;
|
|
17883
18062
|
}
|
|
17884
|
-
const
|
|
17885
|
-
|
|
18063
|
+
const selectedCandidates = resolveSelectedCandidates(
|
|
18064
|
+
activeCandidates,
|
|
17886
18065
|
selections
|
|
17887
18066
|
);
|
|
17888
|
-
if (
|
|
18067
|
+
if (selectedCandidates.length !== selections.length) {
|
|
17889
18068
|
options.stderr("Selected worktree no longer exists\n");
|
|
17890
18069
|
return 1;
|
|
17891
18070
|
}
|
|
18071
|
+
const selectedWorktrees = selectedCandidates.map(
|
|
18072
|
+
({ worktree }) => worktree
|
|
18073
|
+
);
|
|
17892
18074
|
const selectedWorktreeInfos = await readWorktreeInfos2(selectedWorktrees);
|
|
17893
18075
|
const selectedInfoByPath = new Map(
|
|
17894
18076
|
selectedWorktreeInfos.map((info) => [info.path, info])
|
|
@@ -17916,38 +18098,48 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17916
18098
|
}
|
|
17917
18099
|
return 0;
|
|
17918
18100
|
}
|
|
17919
|
-
const
|
|
18101
|
+
const candidatesByRepository = groupCandidatesByRepository(selectedCandidates);
|
|
18102
|
+
const journals = /* @__PURE__ */ new Map();
|
|
17920
18103
|
const failures = [];
|
|
17921
|
-
let journal;
|
|
17922
18104
|
try {
|
|
17923
|
-
|
|
17924
|
-
|
|
17925
|
-
|
|
17926
|
-
|
|
17927
|
-
|
|
17928
|
-
|
|
17929
|
-
|
|
18105
|
+
for (const [repoRoot, candidates] of candidatesByRepository) {
|
|
18106
|
+
const journal = await recordUndoOperation(
|
|
18107
|
+
"clean",
|
|
18108
|
+
repoRoot,
|
|
18109
|
+
candidates.map(({ worktree }) => worktree),
|
|
18110
|
+
void 0,
|
|
18111
|
+
runtime
|
|
18112
|
+
);
|
|
18113
|
+
if (!journal) {
|
|
18114
|
+
await discardUndoRecords(
|
|
18115
|
+
journals.values(),
|
|
18116
|
+
runtime.configStore.GLOBAL_CONFIG_DIRECTORY
|
|
18117
|
+
);
|
|
18118
|
+
emitError(
|
|
18119
|
+
options,
|
|
18120
|
+
"could not capture undo state; no worktrees were removed"
|
|
18121
|
+
);
|
|
18122
|
+
return 1;
|
|
18123
|
+
}
|
|
18124
|
+
journals.set(repoRoot, journal);
|
|
18125
|
+
}
|
|
17930
18126
|
} catch (error) {
|
|
17931
|
-
|
|
17932
|
-
|
|
17933
|
-
|
|
18127
|
+
await discardUndoRecords(
|
|
18128
|
+
journals.values(),
|
|
18129
|
+
runtime.configStore.GLOBAL_CONFIG_DIRECTORY
|
|
17934
18130
|
);
|
|
17935
|
-
return 1;
|
|
17936
|
-
}
|
|
17937
|
-
if (!journal) {
|
|
17938
18131
|
emitError(
|
|
17939
18132
|
options,
|
|
17940
|
-
|
|
18133
|
+
`could not write undo journal; no worktrees were removed: ${toMessage2(error)}`
|
|
17941
18134
|
);
|
|
17942
18135
|
return 1;
|
|
17943
18136
|
}
|
|
17944
|
-
|
|
18137
|
+
const removedCandidates = [];
|
|
18138
|
+
for (const candidate of selectedCandidates) {
|
|
18139
|
+
const { repoRoot, staleBaseRef, worktree } = candidate;
|
|
17945
18140
|
if (options.stale && !await isStaleCleanupCandidate(
|
|
17946
|
-
|
|
17947
|
-
|
|
17948
|
-
staleBaseRef,
|
|
17949
|
-
readWorktreeHealth2,
|
|
17950
|
-
isBranchMergedInto2
|
|
18141
|
+
{ repoRoot, worktree, baseBranch: staleBaseRef },
|
|
18142
|
+
cleanupDependencies
|
|
17951
18143
|
)) {
|
|
17952
18144
|
options.stderr(
|
|
17953
18145
|
`Skipped ${worktree.path}: no longer a safe stale cleanup candidate
|
|
@@ -17956,7 +18148,7 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17956
18148
|
continue;
|
|
17957
18149
|
}
|
|
17958
18150
|
try {
|
|
17959
|
-
await removeWorktree2(
|
|
18151
|
+
await removeWorktree2(repoRoot, worktree.path);
|
|
17960
18152
|
} catch (error) {
|
|
17961
18153
|
if (!isWorktreeForceRemovalError2(error)) {
|
|
17962
18154
|
failures.push({
|
|
@@ -17982,7 +18174,7 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17982
18174
|
continue;
|
|
17983
18175
|
}
|
|
17984
18176
|
try {
|
|
17985
|
-
await forceRemoveWorktree2(
|
|
18177
|
+
await forceRemoveWorktree2(repoRoot, worktree.path);
|
|
17986
18178
|
} catch (forceError) {
|
|
17987
18179
|
failures.push({
|
|
17988
18180
|
branch: worktree.branch,
|
|
@@ -17992,10 +18184,10 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17992
18184
|
continue;
|
|
17993
18185
|
}
|
|
17994
18186
|
}
|
|
17995
|
-
|
|
18187
|
+
removedCandidates.push(candidate);
|
|
17996
18188
|
if (worktree.branch) {
|
|
17997
18189
|
try {
|
|
17998
|
-
await deleteBranch2(
|
|
18190
|
+
await deleteBranch2(repoRoot, worktree.branch);
|
|
17999
18191
|
} catch (error) {
|
|
18000
18192
|
if (!isBranchUnmergedError2(error)) {
|
|
18001
18193
|
failures.push({
|
|
@@ -18007,7 +18199,7 @@ function createCleanCommand(dependencies = {}) {
|
|
|
18007
18199
|
}
|
|
18008
18200
|
if (options.force || await confirmForceDeleteBranch(worktree.branch)) {
|
|
18009
18201
|
try {
|
|
18010
|
-
await forceDeleteBranch2(
|
|
18202
|
+
await forceDeleteBranch2(repoRoot, worktree.branch);
|
|
18011
18203
|
} catch (forceError) {
|
|
18012
18204
|
options.stderr(
|
|
18013
18205
|
`Failed to delete branch ${worktree.branch}: ${toMessage2(forceError)}
|
|
@@ -18023,83 +18215,180 @@ function createCleanCommand(dependencies = {}) {
|
|
|
18023
18215
|
}
|
|
18024
18216
|
}
|
|
18025
18217
|
}
|
|
18026
|
-
|
|
18027
|
-
|
|
18028
|
-
|
|
18029
|
-
|
|
18030
|
-
|
|
18218
|
+
for (const [repoRoot, journal] of journals) {
|
|
18219
|
+
await finalizeUndoOperation(
|
|
18220
|
+
journal,
|
|
18221
|
+
removedCandidates.filter((candidate) => candidate.repoRoot === repoRoot).map(({ worktree }) => worktree),
|
|
18222
|
+
void 0,
|
|
18223
|
+
runtime.configStore.GLOBAL_CONFIG_DIRECTORY
|
|
18224
|
+
);
|
|
18225
|
+
}
|
|
18226
|
+
const undoRecords = remainingUndoRecords(
|
|
18227
|
+
journals.values(),
|
|
18228
|
+
removedCandidates
|
|
18031
18229
|
);
|
|
18032
18230
|
await Promise.all(
|
|
18033
|
-
|
|
18231
|
+
removedCandidates.map(
|
|
18232
|
+
({ worktree }) => releaseWorktreeSlot2(worktree.path)
|
|
18233
|
+
)
|
|
18034
18234
|
);
|
|
18035
18235
|
if (options.json) {
|
|
18036
|
-
const removed =
|
|
18236
|
+
const removed = removedCandidates.map(({ worktree }) => {
|
|
18037
18237
|
const info = selectedInfoByPath.get(worktree.path);
|
|
18038
18238
|
return info === void 0 ? { branch: worktree.branch, path: worktree.path } : serializeWorktreeInfo2(info);
|
|
18039
18239
|
});
|
|
18040
|
-
const payload =
|
|
18240
|
+
const payload = {
|
|
18241
|
+
removed,
|
|
18242
|
+
...failures.length === 0 ? {} : { failed: failures },
|
|
18243
|
+
...needsExplicitUndoIds(undoRecords, repository.repoRoot) ? {
|
|
18244
|
+
undo: undoRecords.map(({ id, repoRoot }) => ({ id, repoRoot }))
|
|
18245
|
+
} : {}
|
|
18246
|
+
};
|
|
18041
18247
|
options.stdout(`${JSON.stringify(payload, null, 2)}
|
|
18042
18248
|
`);
|
|
18043
18249
|
} else if (failures.length > 0) {
|
|
18044
18250
|
reportCleanFailures(failures, options.stderr);
|
|
18251
|
+
emitUndoHints(undoRecords, repository.repoRoot, options.stderr);
|
|
18045
18252
|
} else {
|
|
18046
|
-
options.stderr
|
|
18047
|
-
options.stdout(
|
|
18048
|
-
|
|
18253
|
+
emitUndoHints(undoRecords, repository.repoRoot, options.stderr);
|
|
18254
|
+
options.stdout(
|
|
18255
|
+
`${[...new Set(selectedCandidates.map(({ repoRoot }) => repoRoot))].join("\n")}
|
|
18256
|
+
`
|
|
18257
|
+
);
|
|
18049
18258
|
}
|
|
18050
18259
|
return failures.length === 0 ? 0 : 1;
|
|
18051
18260
|
};
|
|
18052
18261
|
}
|
|
18053
18262
|
var runCleanCommand = createCleanCommand();
|
|
18054
|
-
async function
|
|
18055
|
-
|
|
18056
|
-
|
|
18263
|
+
async function resolveCleanupCandidates(options, dependencies) {
|
|
18264
|
+
const { sources, stale, stderr, signal } = options;
|
|
18265
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
18266
|
+
for (const source of sources) {
|
|
18267
|
+
if (source.repoRoot === void 0) continue;
|
|
18268
|
+
const existing = grouped.get(source.repoRoot);
|
|
18269
|
+
if (existing === void 0) {
|
|
18270
|
+
grouped.set(source.repoRoot, {
|
|
18271
|
+
repoName: source.repoName,
|
|
18272
|
+
worktrees: [source.worktree]
|
|
18273
|
+
});
|
|
18274
|
+
} else {
|
|
18275
|
+
existing.worktrees.push(source.worktree);
|
|
18276
|
+
}
|
|
18057
18277
|
}
|
|
18058
|
-
const results = await
|
|
18059
|
-
|
|
18060
|
-
|
|
18278
|
+
const results = await mapWithConcurrency(
|
|
18279
|
+
[...grouped.entries()],
|
|
18280
|
+
MAX_CLEAN_REPOSITORY_CONCURRENCY,
|
|
18281
|
+
async ([repoRoot, group]) => {
|
|
18282
|
+
throwIfAborted3(signal);
|
|
18283
|
+
const staleBaseRef = stale ? await resolveStaleBaseRef({ repoRoot, stderr }, dependencies) : null;
|
|
18284
|
+
const worktrees = stale ? await filterStaleCleanupCandidates(
|
|
18285
|
+
{
|
|
18286
|
+
repoRoot,
|
|
18287
|
+
worktrees: group.worktrees,
|
|
18288
|
+
baseBranch: staleBaseRef,
|
|
18289
|
+
signal
|
|
18290
|
+
},
|
|
18291
|
+
dependencies
|
|
18292
|
+
) : group.worktrees;
|
|
18293
|
+
return worktrees.map((worktree) => ({
|
|
18294
|
+
repoName: group.repoName,
|
|
18061
18295
|
repoRoot,
|
|
18062
|
-
|
|
18063
|
-
|
|
18064
|
-
|
|
18065
|
-
|
|
18066
|
-
|
|
18296
|
+
staleBaseRef,
|
|
18297
|
+
worktree
|
|
18298
|
+
}));
|
|
18299
|
+
},
|
|
18300
|
+
signal
|
|
18301
|
+
);
|
|
18302
|
+
return results.flat();
|
|
18303
|
+
}
|
|
18304
|
+
function resolveSelectedCandidates(candidates, selections) {
|
|
18305
|
+
const selected = [];
|
|
18306
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
18307
|
+
for (const selection of selections) {
|
|
18308
|
+
const candidate = candidates.find(
|
|
18309
|
+
(entry) => entry.worktree.path === selection || entry.worktree.branch === selection
|
|
18310
|
+
);
|
|
18311
|
+
if (candidate === void 0 || seenPaths.has(candidate.worktree.path)) {
|
|
18312
|
+
continue;
|
|
18313
|
+
}
|
|
18314
|
+
selected.push(candidate);
|
|
18315
|
+
seenPaths.add(candidate.worktree.path);
|
|
18316
|
+
}
|
|
18317
|
+
return selected;
|
|
18318
|
+
}
|
|
18319
|
+
function groupCandidatesByRepository(candidates) {
|
|
18320
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
18321
|
+
for (const candidate of candidates) {
|
|
18322
|
+
const existing = grouped.get(candidate.repoRoot);
|
|
18323
|
+
if (existing === void 0) grouped.set(candidate.repoRoot, [candidate]);
|
|
18324
|
+
else existing.push(candidate);
|
|
18325
|
+
}
|
|
18326
|
+
return grouped;
|
|
18327
|
+
}
|
|
18328
|
+
function remainingUndoRecords(records, removedCandidates) {
|
|
18329
|
+
return [...records].filter(
|
|
18330
|
+
(record) => removedCandidates.some(
|
|
18331
|
+
(candidate) => candidate.repoRoot === record.repoRoot && record.entries.some((entry) => entry.path === candidate.worktree.path)
|
|
18332
|
+
)
|
|
18333
|
+
);
|
|
18334
|
+
}
|
|
18335
|
+
async function discardUndoRecords(records, globalConfigDirectory) {
|
|
18336
|
+
await Promise.all(
|
|
18337
|
+
[...records].map(
|
|
18338
|
+
(record) => finalizeUndoOperation(record, [], void 0, globalConfigDirectory)
|
|
18067
18339
|
)
|
|
18068
18340
|
);
|
|
18341
|
+
}
|
|
18342
|
+
async function filterStaleCleanupCandidates(options, dependencies) {
|
|
18343
|
+
const { repoRoot, worktrees, baseBranch, signal } = options;
|
|
18344
|
+
throwIfAborted3(signal);
|
|
18345
|
+
if (baseBranch === null) {
|
|
18346
|
+
return [];
|
|
18347
|
+
}
|
|
18348
|
+
const results = await mapWithConcurrency(
|
|
18349
|
+
worktrees,
|
|
18350
|
+
MAX_CLEAN_WORKTREE_CONCURRENCY,
|
|
18351
|
+
(worktree) => isStaleCleanupCandidate({ repoRoot, worktree, baseBranch }, dependencies),
|
|
18352
|
+
signal
|
|
18353
|
+
);
|
|
18354
|
+
throwIfAborted3(signal);
|
|
18069
18355
|
return worktrees.filter((_3, index) => results[index]);
|
|
18070
18356
|
}
|
|
18071
|
-
async function resolveStaleBaseRef(
|
|
18072
|
-
const
|
|
18357
|
+
async function resolveStaleBaseRef(options, dependencies) {
|
|
18358
|
+
const { repoRoot, stderr } = options;
|
|
18359
|
+
const config = await dependencies.loadEffectiveConfig(
|
|
18360
|
+
repoRoot,
|
|
18361
|
+
void 0,
|
|
18362
|
+
stderr
|
|
18363
|
+
);
|
|
18073
18364
|
const remote = resolveConfiguredString(config.syncRemote) ?? "origin";
|
|
18074
18365
|
const configuredDefaultBranch = resolveConfiguredString(
|
|
18075
18366
|
config.syncDefaultBranch
|
|
18076
18367
|
);
|
|
18077
|
-
if (configuredDefaultBranch) {
|
|
18078
|
-
return await resolveFetchedRemoteRef(
|
|
18079
|
-
repoRoot,
|
|
18080
|
-
remote,
|
|
18081
|
-
configuredDefaultBranch,
|
|
18082
|
-
runGit2
|
|
18083
|
-
);
|
|
18084
|
-
}
|
|
18085
18368
|
try {
|
|
18086
|
-
const
|
|
18087
|
-
repoRoot,
|
|
18088
|
-
remote
|
|
18089
|
-
);
|
|
18090
|
-
return remoteDefaultBranch === null ? null : await resolveFetchedRemoteRef(
|
|
18369
|
+
const remoteBase = await dependencies.resolveRemoteBase(
|
|
18091
18370
|
repoRoot,
|
|
18092
18371
|
remote,
|
|
18093
|
-
|
|
18094
|
-
|
|
18372
|
+
configuredDefaultBranch ?? void 0
|
|
18373
|
+
);
|
|
18374
|
+
return remoteBase === null ? null : resolveFetchedRemoteRef(
|
|
18375
|
+
{ branch: remoteBase.branch, remote, repoRoot },
|
|
18376
|
+
dependencies.runGit
|
|
18095
18377
|
);
|
|
18096
18378
|
} catch {
|
|
18097
18379
|
return null;
|
|
18098
18380
|
}
|
|
18099
18381
|
}
|
|
18100
|
-
async function resolveFetchedRemoteRef(
|
|
18382
|
+
async function resolveFetchedRemoteRef(options, runGit2) {
|
|
18383
|
+
const { repoRoot, remote, branch } = options;
|
|
18101
18384
|
try {
|
|
18102
18385
|
await runGit2(repoRoot, ["fetch", "--prune", remote]);
|
|
18386
|
+
await runGit2(repoRoot, [
|
|
18387
|
+
"rev-parse",
|
|
18388
|
+
"--verify",
|
|
18389
|
+
"--quiet",
|
|
18390
|
+
`${remote}/${branch}`
|
|
18391
|
+
]);
|
|
18103
18392
|
return `${remote}/${branch}`;
|
|
18104
18393
|
} catch {
|
|
18105
18394
|
return null;
|
|
@@ -18108,33 +18397,39 @@ async function resolveFetchedRemoteRef(repoRoot, remote, branch, runGit2) {
|
|
|
18108
18397
|
function resolveConfiguredString(value) {
|
|
18109
18398
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
18110
18399
|
}
|
|
18111
|
-
async function isStaleCleanupCandidate(
|
|
18400
|
+
async function isStaleCleanupCandidate(options, dependencies) {
|
|
18401
|
+
const { repoRoot, worktree, baseBranch } = options;
|
|
18112
18402
|
if (baseBranch === null) {
|
|
18113
18403
|
return false;
|
|
18114
18404
|
}
|
|
18115
18405
|
if (worktree.branch === null) {
|
|
18116
18406
|
return false;
|
|
18117
18407
|
}
|
|
18118
|
-
const health = await
|
|
18408
|
+
const health = await dependencies.readWorktreeHealth(worktree.path);
|
|
18119
18409
|
if (health.status !== "clean" || !health.upstreamGone) {
|
|
18120
18410
|
return false;
|
|
18121
18411
|
}
|
|
18122
|
-
|
|
18412
|
+
if (await dependencies.isBranchMergedInto(repoRoot, worktree.branch, baseBranch)) {
|
|
18413
|
+
return true;
|
|
18414
|
+
}
|
|
18415
|
+
return isBranchPatchEquivalentInto(
|
|
18416
|
+
{ baseBranch, repoRoot, worktreeBranch: worktree.branch },
|
|
18417
|
+
dependencies.runGit
|
|
18418
|
+
);
|
|
18123
18419
|
}
|
|
18124
|
-
function
|
|
18125
|
-
const
|
|
18126
|
-
|
|
18127
|
-
|
|
18128
|
-
|
|
18129
|
-
|
|
18130
|
-
|
|
18131
|
-
|
|
18132
|
-
|
|
18133
|
-
|
|
18134
|
-
|
|
18135
|
-
|
|
18420
|
+
async function isBranchPatchEquivalentInto(options, runGit2) {
|
|
18421
|
+
const { repoRoot, worktreeBranch, baseBranch } = options;
|
|
18422
|
+
try {
|
|
18423
|
+
const output = await runGit2(repoRoot, [
|
|
18424
|
+
"cherry",
|
|
18425
|
+
baseBranch,
|
|
18426
|
+
worktreeBranch
|
|
18427
|
+
]);
|
|
18428
|
+
const lines = output.split("\n").filter(Boolean);
|
|
18429
|
+
return lines.length > 0 && lines.every((line) => line.startsWith("-"));
|
|
18430
|
+
} catch {
|
|
18431
|
+
return false;
|
|
18136
18432
|
}
|
|
18137
|
-
return selectedWorktrees;
|
|
18138
18433
|
}
|
|
18139
18434
|
function reportCleanFailures(failures, stderr) {
|
|
18140
18435
|
const noun = failures.length === 1 ? "worktree" : "worktrees";
|
|
@@ -18146,6 +18441,37 @@ function reportCleanFailures(failures, stderr) {
|
|
|
18146
18441
|
`);
|
|
18147
18442
|
}
|
|
18148
18443
|
}
|
|
18444
|
+
function emitUndoHints(records, currentRepositoryRoot, stderr) {
|
|
18445
|
+
if (records.length === 0) return;
|
|
18446
|
+
if (!needsExplicitUndoIds(records, currentRepositoryRoot)) {
|
|
18447
|
+
stderr("undo: gji undo\n");
|
|
18448
|
+
return;
|
|
18449
|
+
}
|
|
18450
|
+
const message = records.length === 1 ? "undo: restore with:" : "undo: restore each repository with:";
|
|
18451
|
+
stderr(`${message}
|
|
18452
|
+
`);
|
|
18453
|
+
for (const record of records) {
|
|
18454
|
+
stderr(` gji undo --id ${record.id} (${record.repoRoot})
|
|
18455
|
+
`);
|
|
18456
|
+
}
|
|
18457
|
+
}
|
|
18458
|
+
function needsExplicitUndoIds(records, currentRepositoryRoot) {
|
|
18459
|
+
return records.length > 1 || records.some((record) => record.repoRoot !== currentRepositoryRoot);
|
|
18460
|
+
}
|
|
18461
|
+
function reportSkippedRepositories(repositories, stderr) {
|
|
18462
|
+
const noun = repositories.length === 1 ? "repository" : "repositories";
|
|
18463
|
+
stderr(
|
|
18464
|
+
`Skipped ${repositories.length} registered ${noun} because worktrees could not be listed:
|
|
18465
|
+
`
|
|
18466
|
+
);
|
|
18467
|
+
for (const repository of repositories) {
|
|
18468
|
+
stderr(`- ${repository.name} (${repository.path})
|
|
18469
|
+
`);
|
|
18470
|
+
}
|
|
18471
|
+
}
|
|
18472
|
+
function throwIfAborted3(signal) {
|
|
18473
|
+
if (signal?.aborted) throw new Error("Operation cancelled");
|
|
18474
|
+
}
|
|
18149
18475
|
function formatCleanInfo(info, formatLastCommit3, formatUpstreamState3) {
|
|
18150
18476
|
const branch = info.branch === null ? "detached" : `branch: ${info.branch}`;
|
|
18151
18477
|
const status = `status: ${info.status}`;
|
|
@@ -18174,8 +18500,11 @@ function emitNoStaleCandidates(options) {
|
|
|
18174
18500
|
function toMessage2(error) {
|
|
18175
18501
|
return error instanceof Error ? error.message : String(error);
|
|
18176
18502
|
}
|
|
18177
|
-
async function defaultPromptForWorktrees(worktrees) {
|
|
18178
|
-
return promptForMultipleWorktrees("Choose worktrees to clean", worktrees
|
|
18503
|
+
async function defaultPromptForWorktrees(worktrees, scope, catalog) {
|
|
18504
|
+
return promptForMultipleWorktrees("Choose worktrees to clean", worktrees, {
|
|
18505
|
+
catalog,
|
|
18506
|
+
scope
|
|
18507
|
+
});
|
|
18179
18508
|
}
|
|
18180
18509
|
async function defaultConfirmRemoval(worktrees) {
|
|
18181
18510
|
const branchCount = worktrees.filter(
|
|
@@ -19984,71 +20313,6 @@ function parsePrInput(input) {
|
|
|
19984
20313
|
return urlMatch?.[1] ?? null;
|
|
19985
20314
|
}
|
|
19986
20315
|
|
|
19987
|
-
// src/application/worktree/sources.ts
|
|
19988
|
-
var MAX_REPOSITORY_DISCOVERY_CONCURRENCY = 4;
|
|
19989
|
-
async function listRegisteredWorktreeSources(cwd, repositoryPort, onSkipped) {
|
|
19990
|
-
const registry = await repositoryPort.loadRegistry();
|
|
19991
|
-
const currentRoot = await repositoryPort.detectRepository(cwd).then((repository) => repository.currentRoot).catch(() => null);
|
|
19992
|
-
const results = await mapWithConcurrency(
|
|
19993
|
-
registry,
|
|
19994
|
-
MAX_REPOSITORY_DISCOVERY_CONCURRENCY,
|
|
19995
|
-
async (entry) => {
|
|
19996
|
-
try {
|
|
19997
|
-
const worktrees = await repositoryPort.listWorktrees(entry.path);
|
|
19998
|
-
return { entry, worktrees };
|
|
19999
|
-
} catch {
|
|
20000
|
-
onSkipped?.(entry);
|
|
20001
|
-
return null;
|
|
20002
|
-
}
|
|
20003
|
-
}
|
|
20004
|
-
);
|
|
20005
|
-
const allItems = [];
|
|
20006
|
-
for (const result of results) {
|
|
20007
|
-
if (result === null) continue;
|
|
20008
|
-
const { entry, worktrees } = result;
|
|
20009
|
-
for (const worktree of worktrees) {
|
|
20010
|
-
allItems.push({
|
|
20011
|
-
repoRoot: entry.path,
|
|
20012
|
-
repoName: entry.name,
|
|
20013
|
-
worktree: {
|
|
20014
|
-
...worktree,
|
|
20015
|
-
isCurrent: currentRoot !== null && worktree.path === currentRoot
|
|
20016
|
-
}
|
|
20017
|
-
});
|
|
20018
|
-
}
|
|
20019
|
-
}
|
|
20020
|
-
return allItems;
|
|
20021
|
-
}
|
|
20022
|
-
async function listDiscoverableWorktreeSources(cwd, repositoryPort, onSkipped) {
|
|
20023
|
-
const currentRepository = await repositoryPort.detectRepository(cwd).catch(() => null);
|
|
20024
|
-
const registeredSources = await listRegisteredWorktreeSources(
|
|
20025
|
-
cwd,
|
|
20026
|
-
repositoryPort,
|
|
20027
|
-
onSkipped
|
|
20028
|
-
);
|
|
20029
|
-
if (currentRepository === null) return dedupeSources(registeredSources);
|
|
20030
|
-
let currentSources = [];
|
|
20031
|
-
try {
|
|
20032
|
-
currentSources = (await repositoryPort.listWorktrees(cwd)).map(
|
|
20033
|
-
(worktree) => ({
|
|
20034
|
-
repoName: currentRepository.repoName,
|
|
20035
|
-
repoRoot: currentRepository.repoRoot,
|
|
20036
|
-
worktree
|
|
20037
|
-
})
|
|
20038
|
-
);
|
|
20039
|
-
} catch {
|
|
20040
|
-
}
|
|
20041
|
-
return dedupeSources([...currentSources, ...registeredSources]);
|
|
20042
|
-
}
|
|
20043
|
-
function dedupeSources(sources) {
|
|
20044
|
-
const seen = /* @__PURE__ */ new Set();
|
|
20045
|
-
return sources.filter((source) => {
|
|
20046
|
-
if (seen.has(source.worktree.path)) return false;
|
|
20047
|
-
seen.add(source.worktree.path);
|
|
20048
|
-
return true;
|
|
20049
|
-
});
|
|
20050
|
-
}
|
|
20051
|
-
|
|
20052
20316
|
// src/application/worktree/go-resolution.ts
|
|
20053
20317
|
async function resolveGoBranch(options) {
|
|
20054
20318
|
const {
|
|
@@ -20115,14 +20379,12 @@ async function resolveGoBranch(options) {
|
|
|
20115
20379
|
return finish({ kind: "existing", source: pullRequestMatches[0] }, false);
|
|
20116
20380
|
}
|
|
20117
20381
|
}
|
|
20118
|
-
|
|
20119
|
-
const registeredSources = await listRegisteredWorktreeSources(
|
|
20382
|
+
const registered = await listRegisteredWorktreeSources({
|
|
20120
20383
|
cwd,
|
|
20121
|
-
sourceDependencies
|
|
20122
|
-
|
|
20123
|
-
|
|
20124
|
-
|
|
20125
|
-
);
|
|
20384
|
+
repositoryPort: sourceDependencies
|
|
20385
|
+
});
|
|
20386
|
+
const registeredSources = registered.sources;
|
|
20387
|
+
const skippedRegisteredRepos = registered.skipped.length;
|
|
20126
20388
|
const crossRepoSources = registeredSources.filter(
|
|
20127
20389
|
(source) => source.repoRoot !== repository?.repoRoot
|
|
20128
20390
|
);
|
|
@@ -21705,17 +21967,16 @@ function createGoCommand(dependencies = {}) {
|
|
|
21705
21967
|
let promptSources = currentSources;
|
|
21706
21968
|
const loadAllSources = async () => {
|
|
21707
21969
|
if (registeredSources === null) {
|
|
21708
|
-
|
|
21709
|
-
options.cwd,
|
|
21710
|
-
{
|
|
21970
|
+
const registered = await listRegisteredWorktreeSources({
|
|
21971
|
+
cwd: options.cwd,
|
|
21972
|
+
repositoryPort: {
|
|
21711
21973
|
...runtime.repositoryContext,
|
|
21712
21974
|
...runtime.repositoryRegistry,
|
|
21713
21975
|
...runtime.worktrees
|
|
21714
|
-
},
|
|
21715
|
-
() => {
|
|
21716
|
-
skippedRegisteredRepos++;
|
|
21717
21976
|
}
|
|
21718
|
-
);
|
|
21977
|
+
});
|
|
21978
|
+
registeredSources = registered.sources;
|
|
21979
|
+
skippedRegisteredRepos = registered.skipped.length;
|
|
21719
21980
|
}
|
|
21720
21981
|
return deduplicateSources([...currentSources, ...registeredSources]);
|
|
21721
21982
|
};
|
|
@@ -21737,13 +21998,8 @@ function createGoCommand(dependencies = {}) {
|
|
|
21737
21998
|
currentRepositoryScope = nextCurrentRepositoryScope;
|
|
21738
21999
|
promptSources = nextSources;
|
|
21739
22000
|
return {
|
|
21740
|
-
|
|
21741
|
-
|
|
21742
|
-
catalog: withPullRequestQueries(
|
|
21743
|
-
runtime,
|
|
21744
|
-
dependencies.queryPullRequests
|
|
21745
|
-
)
|
|
21746
|
-
}),
|
|
22001
|
+
sources: promptSources,
|
|
22002
|
+
metadata: currentRepositoryScope ? "full" : "fast",
|
|
21747
22003
|
label: currentRepositoryScope ? "current repository" : "all repositories",
|
|
21748
22004
|
toggleLabel: currentRepositoryScope ? "all repositories" : "current repository"
|
|
21749
22005
|
};
|
|
@@ -21757,7 +22013,11 @@ function createGoCommand(dependencies = {}) {
|
|
|
21757
22013
|
dependencies.queryPullRequests
|
|
21758
22014
|
)
|
|
21759
22015
|
});
|
|
21760
|
-
const selectedPath = await prompt(
|
|
22016
|
+
const selectedPath = await prompt(
|
|
22017
|
+
promptEntries,
|
|
22018
|
+
scope,
|
|
22019
|
+
withPullRequestQueries(runtime, dependencies.queryPullRequests)
|
|
22020
|
+
);
|
|
21761
22021
|
if (!selectedPath) {
|
|
21762
22022
|
options.stderr("Aborted\n");
|
|
21763
22023
|
return 1;
|
|
@@ -22055,8 +22315,11 @@ async function defaultConfirmBranchCreation(branch) {
|
|
|
22055
22315
|
});
|
|
22056
22316
|
return !pD(choice) && choice;
|
|
22057
22317
|
}
|
|
22058
|
-
async function promptForWorktree(worktrees, scope) {
|
|
22059
|
-
return promptForSingleWorktree("Choose a worktree", worktrees, {
|
|
22318
|
+
async function promptForWorktree(worktrees, scope, catalog) {
|
|
22319
|
+
return promptForSingleWorktree("Choose a worktree", worktrees, {
|
|
22320
|
+
catalog,
|
|
22321
|
+
scope
|
|
22322
|
+
});
|
|
22060
22323
|
}
|
|
22061
22324
|
|
|
22062
22325
|
// src/cli/commands/history-command.ts
|
|
@@ -22089,10 +22352,10 @@ async function buildHubData(cwd, dependencies = {}, runtime = defaultCliDependen
|
|
|
22089
22352
|
};
|
|
22090
22353
|
const queryRepositoryPullRequests = dependencies.queryRepositoryPullRequests ?? runtime.pullRequests.listOpenPullRequestsForRepository;
|
|
22091
22354
|
const currentRepository = await detectRepository2(cwd).catch(() => null);
|
|
22092
|
-
const sources = await listDiscoverableWorktreeSources(
|
|
22355
|
+
const { sources } = await listDiscoverableWorktreeSources({
|
|
22093
22356
|
cwd,
|
|
22094
|
-
sourceDependencies
|
|
22095
|
-
);
|
|
22357
|
+
repositoryPort: sourceDependencies
|
|
22358
|
+
});
|
|
22096
22359
|
const groups = /* @__PURE__ */ new Map();
|
|
22097
22360
|
for (const source of sources) {
|
|
22098
22361
|
if (!source.repoRoot) continue;
|
|
@@ -24446,10 +24709,14 @@ async function resolveWarpTarget(options) {
|
|
|
24446
24709
|
);
|
|
24447
24710
|
return null;
|
|
24448
24711
|
}
|
|
24449
|
-
|
|
24450
|
-
|
|
24451
|
-
|
|
24452
|
-
})
|
|
24712
|
+
const registered = await listRegisteredWorktreeSources({
|
|
24713
|
+
cwd: options.cwd,
|
|
24714
|
+
repositoryPort: sourceDependencies
|
|
24715
|
+
});
|
|
24716
|
+
const skippedRegisteredRepos = registered.skipped.length;
|
|
24717
|
+
const allItems = registered.sources.filter(
|
|
24718
|
+
(item) => item.repoRoot !== options.excludeRepoRoot
|
|
24719
|
+
);
|
|
24453
24720
|
if (allItems.length === 0) {
|
|
24454
24721
|
emitError5(
|
|
24455
24722
|
"no accessible worktrees found in any registered repo.",
|