@solaqua/gji 0.12.4 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/application/worktree/catalog.d.ts +31 -0
- package/dist/application/worktree/catalog.js +88 -0
- package/dist/application/worktree/context-card.d.ts +14 -0
- package/dist/application/worktree/context-card.js +12 -0
- package/dist/{go-resolver.d.ts → application/worktree/go-resolution.d.ts} +12 -4
- package/dist/{go-resolver.js → application/worktree/go-resolution.js} +46 -44
- package/dist/application/worktree/read-models.d.ts +31 -0
- package/dist/application/worktree/sources.d.ts +7 -0
- package/dist/{worktree-sources.js → application/worktree/sources.js} +12 -24
- package/dist/{back.d.ts → cli/commands/back.d.ts} +3 -1
- package/dist/{back.js → cli/commands/back.js} +8 -6
- package/dist/{clean.d.ts → cli/commands/clean.d.ts} +4 -2
- package/dist/{clean.js → cli/commands/clean.js} +32 -24
- package/dist/{completion.js → cli/commands/completion.js} +2 -2
- package/dist/{config-command.d.ts → cli/commands/config-command.d.ts} +2 -0
- package/dist/{config-command.js → cli/commands/config-command.js} +7 -5
- package/dist/{doctor.d.ts → cli/commands/doctor.d.ts} +2 -0
- package/dist/{doctor.js → cli/commands/doctor.js} +23 -25
- package/dist/{done.d.ts → cli/commands/done.d.ts} +2 -0
- package/dist/{done.js → cli/commands/done.js} +26 -23
- package/dist/{go.d.ts → cli/commands/go.d.ts} +6 -1
- package/dist/{go.js → cli/commands/go.js} +57 -41
- package/dist/{history-command.d.ts → cli/commands/history-command.d.ts} +2 -0
- package/dist/{history-command.js → cli/commands/history-command.js} +3 -1
- package/dist/{hub.d.ts → cli/commands/hub.d.ts} +7 -3
- package/dist/{hub.js → cli/commands/hub.js} +23 -33
- package/dist/{init.d.ts → cli/commands/init.d.ts} +3 -1
- package/dist/{init.js → cli/commands/init.js} +37 -34
- package/dist/{ls.d.ts → cli/commands/ls.d.ts} +4 -2
- package/dist/{ls.js → cli/commands/ls.js} +8 -5
- package/dist/{new.d.ts → cli/commands/new.d.ts} +6 -2
- package/dist/{new.js → cli/commands/new.js} +237 -109
- package/dist/{open.d.ts → cli/commands/open.d.ts} +4 -2
- package/dist/{open.js → cli/commands/open.js} +19 -12
- package/dist/{pr-open.d.ts → cli/commands/pr-open.d.ts} +4 -2
- package/dist/{pr-open.js → cli/commands/pr-open.js} +20 -18
- package/dist/{pr.d.ts → cli/commands/pr.d.ts} +5 -3
- package/dist/{pr.js → cli/commands/pr.js} +34 -41
- package/dist/{remove.d.ts → cli/commands/remove.d.ts} +4 -2
- package/dist/{remove.js → cli/commands/remove.js} +23 -17
- package/dist/{root.d.ts → cli/commands/root.d.ts} +2 -0
- package/dist/{root.js → cli/commands/root.js} +4 -2
- package/dist/{run-hook.d.ts → cli/commands/run-hook.d.ts} +2 -0
- package/dist/{run-hook.js → cli/commands/run-hook.js} +7 -4
- package/dist/{status.d.ts → cli/commands/status.d.ts} +2 -0
- package/dist/{status.js → cli/commands/status.js} +10 -7
- package/dist/{sync-files-command.d.ts → cli/commands/sync-files-command.d.ts} +2 -0
- package/dist/{sync-files-command.js → cli/commands/sync-files-command.js} +11 -9
- package/dist/{sync.d.ts → cli/commands/sync.d.ts} +2 -0
- package/dist/{sync.js → cli/commands/sync.js} +7 -4
- package/dist/{task-command.d.ts → cli/commands/task-command.d.ts} +2 -0
- package/dist/{task-command.js → cli/commands/task-command.js} +4 -2
- package/dist/{undo.d.ts → cli/commands/undo.d.ts} +8 -6
- package/dist/{undo.js → cli/commands/undo.js} +44 -34
- package/dist/{warp.d.ts → cli/commands/warp.d.ts} +5 -2
- package/dist/{warp.js → cli/commands/warp.js} +20 -10
- package/dist/cli/dependencies.d.ts +114 -0
- package/dist/cli/dependencies.js +118 -0
- package/dist/{cli.d.ts → cli/program.d.ts} +4 -2
- package/dist/{cli.js → cli/program.js} +108 -124
- package/dist/domain/repository/context.d.ts +6 -0
- package/dist/domain/repository/context.js +1 -0
- package/dist/domain/repository/registry.d.ts +5 -0
- package/dist/domain/repository/registry.js +1 -0
- package/dist/domain/shared/concurrency.d.ts +1 -0
- package/dist/domain/shared/concurrency.js +15 -0
- package/dist/domain/worktree/matching.d.ts +4 -0
- package/dist/domain/worktree/matching.js +79 -0
- package/dist/domain/worktree/policy.d.ts +2 -0
- package/dist/domain/worktree/policy.js +58 -0
- package/dist/domain/worktree/pr-reference.d.ts +1 -0
- package/dist/domain/worktree/pr-reference.js +9 -0
- package/dist/{worktree-source.d.ts → domain/worktree/source.d.ts} +1 -1
- package/dist/domain/worktree/source.js +1 -0
- package/dist/domain/worktree/types.d.ts +5 -0
- package/dist/domain/worktree/types.js +1 -0
- package/dist/gji-bundle.mjs +7100 -6265
- package/dist/index.js +3 -3
- package/dist/{dependency-bootstrap.d.ts → infrastructure/bootstrap/dependency-bootstrap.d.ts} +3 -30
- package/dist/{dependency-bootstrap.js → infrastructure/bootstrap/dependency-bootstrap.js} +4 -4
- package/dist/infrastructure/git/health.d.ts +9 -0
- package/dist/infrastructure/git/health.js +44 -0
- package/dist/infrastructure/git/refs.d.ts +6 -0
- package/dist/{git.js → infrastructure/git/refs.js} +1 -51
- package/dist/infrastructure/git/runner.d.ts +2 -0
- package/dist/infrastructure/git/runner.js +16 -0
- package/dist/{editor.d.ts → infrastructure/integrations/editor.d.ts} +2 -6
- package/dist/{pull-requests.d.ts → infrastructure/integrations/pull-requests.d.ts} +3 -11
- package/dist/{config.d.ts → infrastructure/persistence/config.d.ts} +3 -17
- package/dist/{config.js → infrastructure/persistence/config.js} +2 -17
- package/dist/{history.d.ts → infrastructure/persistence/history.d.ts} +2 -5
- package/dist/{task.js → infrastructure/persistence/task.js} +1 -1
- package/dist/infrastructure/process/command-runner.d.ts +3 -0
- package/dist/{hooks.d.ts → infrastructure/process/hooks.d.ts} +2 -12
- package/dist/infrastructure/repository/adapters.d.ts +7 -0
- package/dist/infrastructure/repository/adapters.js +23 -0
- package/dist/infrastructure/repository/context.d.ts +3 -0
- package/dist/infrastructure/repository/context.js +20 -0
- package/dist/infrastructure/repository/refs.d.ts +3 -0
- package/dist/infrastructure/repository/refs.js +37 -0
- package/dist/{repo-registry.d.ts → infrastructure/repository/registry.d.ts} +1 -5
- package/dist/{repo-registry.js → infrastructure/repository/registry.js} +1 -1
- package/dist/infrastructure/repository/worktrees.d.ts +2 -0
- package/dist/infrastructure/repository/worktrees.js +48 -0
- package/dist/{uv-validation.d.ts → infrastructure/validation/uv.d.ts} +1 -1
- package/dist/{uv-validation.js → infrastructure/validation/uv.js} +1 -1
- package/dist/infrastructure/worktree/bootstrap.d.ts +3 -0
- package/dist/{worktree-bootstrap.js → infrastructure/worktree/bootstrap.js} +7 -10
- package/dist/infrastructure/worktree/info.d.ts +6 -0
- package/dist/infrastructure/worktree/info.js +61 -0
- package/dist/{worktree-management.d.ts → infrastructure/worktree/lifecycle.d.ts} +0 -6
- package/dist/{worktree-management.js → infrastructure/worktree/lifecycle.js} +1 -9
- package/dist/ports/bootstrap.d.ts +61 -0
- package/dist/ports/bootstrap.js +1 -0
- package/dist/ports/config.d.ts +21 -0
- package/dist/ports/config.js +17 -0
- package/dist/ports/editor.d.ts +6 -0
- package/dist/ports/editor.js +1 -0
- package/dist/ports/git.d.ts +8 -0
- package/dist/ports/git.js +1 -0
- package/dist/ports/history.d.ts +5 -0
- package/dist/ports/history.js +1 -0
- package/dist/ports/hooks.d.ts +12 -0
- package/dist/ports/hooks.js +1 -0
- package/dist/{command-runner.d.ts → ports/process.d.ts} +0 -1
- package/dist/ports/process.js +1 -0
- package/dist/ports/pull-requests.d.ts +11 -0
- package/dist/ports/pull-requests.js +1 -0
- package/dist/ports/repository.d.ts +17 -0
- package/dist/ports/repository.js +1 -0
- package/dist/{bootstrap-output.d.ts → presentation/bootstrap/output.d.ts} +1 -1
- package/dist/presentation/bootstrap/preview.d.ts +2 -0
- package/dist/{bootstrap-preview.js → presentation/bootstrap/preview.js} +0 -4
- package/dist/{shell-completion.js → presentation/shell/completion.js} +5 -4
- package/dist/presentation/terminal/context-card.d.ts +2 -0
- package/dist/{context-card.js → presentation/terminal/context-card.js} +3 -12
- package/dist/presentation/worktree/format.d.ts +4 -0
- package/dist/presentation/worktree/format.js +41 -0
- package/dist/{worktree-picker.d.ts → presentation/worktree/picker.d.ts} +7 -11
- package/dist/{worktree-picker.js → presentation/worktree/picker.js} +36 -191
- 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 +6 -3
- 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 +2 -2
- package/package.json +1 -1
- package/dist/bootstrap-preview.d.ts +0 -9
- package/dist/context-card.d.ts +0 -1
- package/dist/format-bytes.d.ts +0 -1
- package/dist/format-bytes.js +0 -14
- package/dist/git.d.ts +0 -18
- package/dist/repo.d.ts +0 -20
- package/dist/repo.js +0 -161
- package/dist/worktree-bootstrap.d.ts +0 -24
- package/dist/worktree-info.d.ts +0 -37
- package/dist/worktree-info.js +0 -123
- package/dist/worktree-sources.d.ts +0 -4
- /package/dist/{worktree-source.js → application/worktree/read-models.js} +0 -0
- /package/dist/{completion.d.ts → cli/commands/completion.d.ts} +0 -0
- /package/dist/{headless.d.ts → cli/runtime/headless.d.ts} +0 -0
- /package/dist/{headless.js → cli/runtime/headless.js} +0 -0
- /package/dist/{paths.d.ts → domain/shared/paths.d.ts} +0 -0
- /package/dist/{paths.js → domain/shared/paths.js} +0 -0
- /package/dist/{file-sync.d.ts → infrastructure/filesystem/file-sync.d.ts} +0 -0
- /package/dist/{file-sync.js → infrastructure/filesystem/file-sync.js} +0 -0
- /package/dist/{fs-utils.d.ts → infrastructure/filesystem/fs-utils.d.ts} +0 -0
- /package/dist/{fs-utils.js → infrastructure/filesystem/fs-utils.js} +0 -0
- /package/dist/{safe-destination.d.ts → infrastructure/filesystem/safe-destination.d.ts} +0 -0
- /package/dist/{safe-destination.js → infrastructure/filesystem/safe-destination.js} +0 -0
- /package/dist/{browser.d.ts → infrastructure/integrations/browser.d.ts} +0 -0
- /package/dist/{browser.js → infrastructure/integrations/browser.js} +0 -0
- /package/dist/{editor.js → infrastructure/integrations/editor.js} +0 -0
- /package/dist/{pull-requests.js → infrastructure/integrations/pull-requests.js} +0 -0
- /package/dist/{history.js → infrastructure/persistence/history.js} +0 -0
- /package/dist/{slots.d.ts → infrastructure/persistence/slots.d.ts} +0 -0
- /package/dist/{slots.js → infrastructure/persistence/slots.js} +0 -0
- /package/dist/{task.d.ts → infrastructure/persistence/task.d.ts} +0 -0
- /package/dist/{command-runner.js → infrastructure/process/command-runner.js} +0 -0
- /package/dist/{hooks.js → infrastructure/process/hooks.js} +0 -0
- /package/dist/{bootstrap-output.js → presentation/bootstrap/output.js} +0 -0
- /package/dist/{conflict.d.ts → presentation/prompts/path-conflict.d.ts} +0 -0
- /package/dist/{conflict.js → presentation/prompts/path-conflict.js} +0 -0
- /package/dist/{shell-completion.d.ts → presentation/shell/completion.d.ts} +0 -0
- /package/dist/{shell-handoff.d.ts → presentation/shell/handoff.d.ts} +0 -0
- /package/dist/{shell-handoff.js → presentation/shell/handoff.js} +0 -0
- /package/dist/{shell-setup.d.ts → presentation/shell/setup.d.ts} +0 -0
- /package/dist/{shell-setup.js → presentation/shell/setup.js} +0 -0
- /package/dist/{shell.d.ts → presentation/shell/shell.d.ts} +0 -0
- /package/dist/{shell.js → presentation/shell/shell.js} +0 -0
- /package/dist/{navigation-output.d.ts → presentation/terminal/navigation.d.ts} +0 -0
- /package/dist/{navigation-output.js → presentation/terminal/navigation.js} +0 -0
- /package/dist/{terminal-text.d.ts → presentation/terminal/text.d.ts} +0 -0
- /package/dist/{terminal-text.js → presentation/terminal/text.js} +0 -0
- /package/dist/{worktree-prompts.d.ts → presentation/worktree/prompts.d.ts} +0 -0
- /package/dist/{worktree-prompts.js → presentation/worktree/prompts.js} +0 -0
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { confirm, isCancel } from "@clack/prompts";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { loadLinkedWorktrees } from "../../application/worktree/catalog.js";
|
|
3
|
+
import { buildWorktreePromptEntries, promptForMultipleWorktrees, } from "../../presentation/worktree/picker.js";
|
|
4
|
+
import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "../../presentation/worktree/prompts.js";
|
|
5
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
6
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
6
7
|
import { finalizeUndoOperation, recordUndoOperation } from "./undo.js";
|
|
7
|
-
import { formatLastCommit, formatUpstreamState, readWorktreeInfos, serializeWorktreeInfo, } from "./worktree-info.js";
|
|
8
|
-
import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isSubmoduleWorktreeRemovalError, isWorktreeDeletionError, isWorktreeForceRemovalError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
|
|
9
|
-
import { buildWorktreePromptEntries, promptForMultipleWorktrees, } from "./worktree-picker.js";
|
|
10
|
-
import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "./worktree-prompts.js";
|
|
11
8
|
export function createCleanCommand(dependencies = {}) {
|
|
12
9
|
const promptForWorktrees = dependencies.promptForWorktrees ?? defaultPromptForWorktrees;
|
|
13
10
|
const confirmRemoval = dependencies.confirmRemoval ?? defaultConfirmRemoval;
|
|
@@ -15,13 +12,24 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
15
12
|
defaultConfirmForceRemoveWorktree;
|
|
16
13
|
const confirmForceDeleteBranch = dependencies.confirmForceDeleteBranch ?? defaultConfirmForceDeleteBranch;
|
|
17
14
|
return async function runCleanCommand(options) {
|
|
18
|
-
const
|
|
15
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
16
|
+
const { readWorktreeHealth, isBranchMergedInto, resolveRemoteDefaultBranch, runGit, } = runtime.git;
|
|
17
|
+
const { loadEffectiveConfig } = runtime.config;
|
|
18
|
+
const { releaseWorktreeSlot } = runtime.slots;
|
|
19
|
+
const sourceDependencies = {
|
|
20
|
+
...runtime.repositoryContext,
|
|
21
|
+
...runtime.repositoryRegistry,
|
|
22
|
+
...runtime.worktrees,
|
|
23
|
+
};
|
|
24
|
+
const { formatLastCommit, formatUpstreamState, readWorktreeInfos, serializeWorktreeInfo, } = runtime.worktreeInfo;
|
|
25
|
+
const { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isSubmoduleWorktreeRemovalError, isWorktreeDeletionError, isWorktreeForceRemovalError, removeWorktree, } = runtime.worktreeLifecycle;
|
|
26
|
+
const { linkedWorktrees, repository } = await loadLinkedWorktrees(options.cwd, sourceDependencies);
|
|
19
27
|
const linkedCleanupCandidates = linkedWorktrees.filter((worktree) => worktree.path !== repository.currentRoot);
|
|
20
28
|
const staleBaseRef = options.stale
|
|
21
|
-
? await resolveStaleBaseRef(repository.repoRoot, options.stderr)
|
|
29
|
+
? await resolveStaleBaseRef(repository.repoRoot, options.stderr, loadEffectiveConfig, resolveRemoteDefaultBranch, runGit)
|
|
22
30
|
: null;
|
|
23
31
|
const cleanupCandidates = options.stale
|
|
24
|
-
? await filterStaleCleanupCandidates(repository.repoRoot, linkedCleanupCandidates, staleBaseRef)
|
|
32
|
+
? await filterStaleCleanupCandidates(repository.repoRoot, linkedCleanupCandidates, staleBaseRef, readWorktreeHealth, isBranchMergedInto)
|
|
25
33
|
: linkedCleanupCandidates;
|
|
26
34
|
if (cleanupCandidates.length === 0) {
|
|
27
35
|
if (options.stale) {
|
|
@@ -49,7 +57,7 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
49
57
|
: await promptForWorktrees(await buildWorktreePromptEntries(cleanupCandidates.map((worktree) => ({
|
|
50
58
|
repoName: repository.repoName,
|
|
51
59
|
worktree,
|
|
52
|
-
}))));
|
|
60
|
+
})), { catalog: runtime.worktreeCatalog }));
|
|
53
61
|
if (!selections || selections.length === 0) {
|
|
54
62
|
options.stderr("Aborted\n");
|
|
55
63
|
return 1;
|
|
@@ -74,7 +82,7 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
74
82
|
}
|
|
75
83
|
else {
|
|
76
84
|
for (const info of selectedWorktreeInfos) {
|
|
77
|
-
options.stdout(`Would remove worktree at ${info.path} (${formatCleanInfo(info)})\n`);
|
|
85
|
+
options.stdout(`Would remove worktree at ${info.path} (${formatCleanInfo(info, formatLastCommit, formatUpstreamState)})\n`);
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
88
|
return 0;
|
|
@@ -83,7 +91,7 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
83
91
|
const failures = [];
|
|
84
92
|
let journal;
|
|
85
93
|
try {
|
|
86
|
-
journal = await recordUndoOperation("clean", repository.repoRoot, selectedWorktrees);
|
|
94
|
+
journal = await recordUndoOperation("clean", repository.repoRoot, selectedWorktrees, undefined, runtime);
|
|
87
95
|
}
|
|
88
96
|
catch (error) {
|
|
89
97
|
emitError(options, `could not write undo journal; no worktrees were removed: ${toMessage(error)}`);
|
|
@@ -95,7 +103,7 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
95
103
|
}
|
|
96
104
|
for (const worktree of selectedWorktrees) {
|
|
97
105
|
if (options.stale &&
|
|
98
|
-
!(await isStaleCleanupCandidate(repository.repoRoot, worktree, staleBaseRef))) {
|
|
106
|
+
!(await isStaleCleanupCandidate(repository.repoRoot, worktree, staleBaseRef, readWorktreeHealth, isBranchMergedInto))) {
|
|
99
107
|
options.stderr(`Skipped ${worktree.path}: no longer a safe stale cleanup candidate\n`);
|
|
100
108
|
continue;
|
|
101
109
|
}
|
|
@@ -167,7 +175,7 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
167
175
|
}
|
|
168
176
|
}
|
|
169
177
|
}
|
|
170
|
-
await finalizeUndoOperation(journal, removedWorktrees);
|
|
178
|
+
await finalizeUndoOperation(journal, removedWorktrees, undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
171
179
|
await Promise.all(removedWorktrees.map((worktree) => releaseWorktreeSlot(worktree.path)));
|
|
172
180
|
if (options.json) {
|
|
173
181
|
const removed = removedWorktrees.map((worktree) => {
|
|
@@ -190,31 +198,31 @@ export function createCleanCommand(dependencies = {}) {
|
|
|
190
198
|
};
|
|
191
199
|
}
|
|
192
200
|
export const runCleanCommand = createCleanCommand();
|
|
193
|
-
async function filterStaleCleanupCandidates(repoRoot, worktrees, baseBranch) {
|
|
201
|
+
async function filterStaleCleanupCandidates(repoRoot, worktrees, baseBranch, readWorktreeHealth, isBranchMergedInto) {
|
|
194
202
|
if (baseBranch === null) {
|
|
195
203
|
return [];
|
|
196
204
|
}
|
|
197
|
-
const results = await Promise.all(worktrees.map((worktree) => isStaleCleanupCandidate(repoRoot, worktree, baseBranch)));
|
|
205
|
+
const results = await Promise.all(worktrees.map((worktree) => isStaleCleanupCandidate(repoRoot, worktree, baseBranch, readWorktreeHealth, isBranchMergedInto)));
|
|
198
206
|
return worktrees.filter((_, index) => results[index]);
|
|
199
207
|
}
|
|
200
|
-
async function resolveStaleBaseRef(repoRoot, stderr) {
|
|
208
|
+
async function resolveStaleBaseRef(repoRoot, stderr, loadEffectiveConfig, resolveRemoteDefaultBranch, runGit) {
|
|
201
209
|
const config = await loadEffectiveConfig(repoRoot, undefined, stderr);
|
|
202
210
|
const remote = resolveConfiguredString(config.syncRemote) ?? "origin";
|
|
203
211
|
const configuredDefaultBranch = resolveConfiguredString(config.syncDefaultBranch);
|
|
204
212
|
if (configuredDefaultBranch) {
|
|
205
|
-
return await resolveFetchedRemoteRef(repoRoot, remote, configuredDefaultBranch);
|
|
213
|
+
return await resolveFetchedRemoteRef(repoRoot, remote, configuredDefaultBranch, runGit);
|
|
206
214
|
}
|
|
207
215
|
try {
|
|
208
216
|
const remoteDefaultBranch = await resolveRemoteDefaultBranch(repoRoot, remote);
|
|
209
217
|
return remoteDefaultBranch === null
|
|
210
218
|
? null
|
|
211
|
-
: await resolveFetchedRemoteRef(repoRoot, remote, remoteDefaultBranch);
|
|
219
|
+
: await resolveFetchedRemoteRef(repoRoot, remote, remoteDefaultBranch, runGit);
|
|
212
220
|
}
|
|
213
221
|
catch {
|
|
214
222
|
return null;
|
|
215
223
|
}
|
|
216
224
|
}
|
|
217
|
-
async function resolveFetchedRemoteRef(repoRoot, remote, branch) {
|
|
225
|
+
async function resolveFetchedRemoteRef(repoRoot, remote, branch, runGit) {
|
|
218
226
|
try {
|
|
219
227
|
await runGit(repoRoot, ["fetch", "--prune", remote]);
|
|
220
228
|
return `${remote}/${branch}`;
|
|
@@ -226,7 +234,7 @@ async function resolveFetchedRemoteRef(repoRoot, remote, branch) {
|
|
|
226
234
|
function resolveConfiguredString(value) {
|
|
227
235
|
return typeof value === "string" && value.length > 0 ? value : null;
|
|
228
236
|
}
|
|
229
|
-
async function isStaleCleanupCandidate(repoRoot, worktree, baseBranch) {
|
|
237
|
+
async function isStaleCleanupCandidate(repoRoot, worktree, baseBranch, readWorktreeHealth, isBranchMergedInto) {
|
|
230
238
|
if (baseBranch === null) {
|
|
231
239
|
return false;
|
|
232
240
|
}
|
|
@@ -260,7 +268,7 @@ function reportCleanFailures(failures, stderr) {
|
|
|
260
268
|
stderr(`- ${failure.path} (${branch}): ${failure.message}\n`);
|
|
261
269
|
}
|
|
262
270
|
}
|
|
263
|
-
function formatCleanInfo(info) {
|
|
271
|
+
function formatCleanInfo(info, formatLastCommit, formatUpstreamState) {
|
|
264
272
|
const branch = info.branch === null ? "detached" : `branch: ${info.branch}`;
|
|
265
273
|
const status = `status: ${info.status}`;
|
|
266
274
|
const upstream = `upstream: ${formatUpstreamState(info.upstream)}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { renderShellCompletion } from "../../presentation/shell/completion.js";
|
|
2
|
+
import { resolveSupportedShell } from "../../presentation/shell/shell.js";
|
|
3
3
|
export async function runCompletionCommand(options) {
|
|
4
4
|
const shell = options.shell
|
|
5
5
|
? resolveSupportedShell(options.shell, undefined)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface ConfigCommandOptions {
|
|
2
3
|
action?: string;
|
|
3
4
|
cwd: string;
|
|
@@ -5,5 +6,6 @@ export interface ConfigCommandOptions {
|
|
|
5
6
|
stderr?: (chunk: string) => void;
|
|
6
7
|
stdout: (chunk: string) => void;
|
|
7
8
|
value?: string;
|
|
9
|
+
runtime?: CliRuntime<"configStore">;
|
|
8
10
|
}
|
|
9
11
|
export declare function runConfigCommand(options: ConfigCommandOptions): Promise<number>;
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
2
2
|
export async function runConfigCommand(options) {
|
|
3
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
4
|
+
const config = runtime.configStore;
|
|
3
5
|
switch (options.action) {
|
|
4
6
|
case undefined: {
|
|
5
|
-
const loaded = await loadGlobalConfig();
|
|
7
|
+
const loaded = await config.loadGlobalConfig();
|
|
6
8
|
writeJson(options.stdout, loaded.config);
|
|
7
9
|
return 0;
|
|
8
10
|
}
|
|
9
11
|
case "get": {
|
|
10
|
-
const loaded = await loadGlobalConfig();
|
|
12
|
+
const loaded = await config.loadGlobalConfig();
|
|
11
13
|
writeJson(options.stdout, options.key ? loaded.config[options.key] : loaded.config);
|
|
12
14
|
return 0;
|
|
13
15
|
}
|
|
14
16
|
case "set":
|
|
15
17
|
if (options.key && options.value !== undefined) {
|
|
16
18
|
try {
|
|
17
|
-
await updateGlobalConfigKey(options.key, parseConfigValue(options.value));
|
|
19
|
+
await config.updateGlobalConfigKey(options.key, config.parseConfigValue(options.value));
|
|
18
20
|
}
|
|
19
21
|
catch (error) {
|
|
20
22
|
options.stderr?.(`gji config: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -25,7 +27,7 @@ export async function runConfigCommand(options) {
|
|
|
25
27
|
break;
|
|
26
28
|
case "unset":
|
|
27
29
|
if (options.key) {
|
|
28
|
-
await unsetGlobalConfigKey(options.key);
|
|
30
|
+
await config.unsetGlobalConfigKey(options.key);
|
|
29
31
|
return 0;
|
|
30
32
|
}
|
|
31
33
|
break;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export type DoctorCheckStatus = "fail" | "ok" | "skip";
|
|
2
3
|
export interface DoctorCheck {
|
|
3
4
|
hint?: string;
|
|
@@ -22,6 +23,7 @@ export interface DoctorCommandOptions {
|
|
|
22
23
|
confirmFixes?: (fixes: DoctorFix[]) => Promise<boolean>;
|
|
23
24
|
shell?: string;
|
|
24
25
|
yes?: boolean;
|
|
26
|
+
runtime?: CliRuntime<"integrations" | "slots" | "repositoryContext" | "configStore" | "registry">;
|
|
25
27
|
stderr?: (chunk: string) => void;
|
|
26
28
|
stdout: (chunk: string) => void;
|
|
27
29
|
}
|
|
@@ -5,17 +5,14 @@ import { homedir } from "node:os";
|
|
|
5
5
|
import { basename, dirname, join } from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { confirm, isCancel } from "@clack/prompts";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { loadRegistry, REGISTRY_FILE_PATH, removeMissingRegistryEntries, } from "./repo-registry.js";
|
|
13
|
-
import { resolveSupportedShell } from "./shell.js";
|
|
14
|
-
import { executableExists, hasShellIntegration, resolveCompletionPath, resolveShellConfigPath, } from "./shell-setup.js";
|
|
15
|
-
import { loadSlots } from "./slots.js";
|
|
8
|
+
import { executableExists, hasShellIntegration, resolveCompletionPath, resolveShellConfigPath, } from "../../presentation/shell/setup.js";
|
|
9
|
+
import { resolveSupportedShell, } from "../../presentation/shell/shell.js";
|
|
10
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
11
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
16
12
|
const execFileAsync = promisify(execFile);
|
|
17
13
|
const MINIMUM_GIT_VERSION = { major: 2, minor: 17 };
|
|
18
14
|
export async function runDoctorCommand(options) {
|
|
15
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
19
16
|
if (options.yes && !options.fix) {
|
|
20
17
|
const message = "--yes requires --fix";
|
|
21
18
|
if (options.json) {
|
|
@@ -28,14 +25,14 @@ export async function runDoctorCommand(options) {
|
|
|
28
25
|
}
|
|
29
26
|
const home = options.home ?? homedir();
|
|
30
27
|
const shell = resolveSupportedShell(undefined, options.shell ?? process.env.SHELL);
|
|
31
|
-
let inspection = await collectDoctorInspection(options.cwd, home, shell);
|
|
28
|
+
let inspection = await collectDoctorInspection(options.cwd, home, shell, runtime);
|
|
32
29
|
let fixes = [];
|
|
33
30
|
if (options.fix) {
|
|
34
31
|
fixes = buildDoctorFixes(inspection.missingRegistryPaths);
|
|
35
32
|
if (fixes.length > 0) {
|
|
36
33
|
const approval = await requestFixApproval(fixes, options);
|
|
37
34
|
if (approval === "apply") {
|
|
38
|
-
fixes = await applyDoctorFixes(fixes, home);
|
|
35
|
+
fixes = await applyDoctorFixes(fixes, home, runtime.registry);
|
|
39
36
|
}
|
|
40
37
|
else {
|
|
41
38
|
fixes = fixes.map((fix) => ({
|
|
@@ -47,7 +44,7 @@ export async function runDoctorCommand(options) {
|
|
|
47
44
|
}));
|
|
48
45
|
}
|
|
49
46
|
}
|
|
50
|
-
inspection = await collectDoctorInspection(options.cwd, home, shell);
|
|
47
|
+
inspection = await collectDoctorInspection(options.cwd, home, shell, runtime);
|
|
51
48
|
}
|
|
52
49
|
const problems = inspection.checks.filter((check) => check.status === "fail").length;
|
|
53
50
|
if (options.json) {
|
|
@@ -64,14 +61,15 @@ export async function runDoctorCommand(options) {
|
|
|
64
61
|
}
|
|
65
62
|
return problems > 0 ? 1 : 0;
|
|
66
63
|
}
|
|
67
|
-
async function collectDoctorInspection(cwd, home, shell) {
|
|
68
|
-
const
|
|
64
|
+
async function collectDoctorInspection(cwd, home, shell, runtime) {
|
|
65
|
+
const { CONFIG_FILE_NAME, GLOBAL_CONFIG_FILE_PATH, KNOWN_CONFIG_KEYS, KNOWN_GLOBAL_CONFIG_KEYS, } = runtime.configStore;
|
|
66
|
+
const repository = await detectRepositoryOrSkip(cwd, runtime.repositoryContext.detectRepository);
|
|
69
67
|
const globalConfig = await inspectConfig(GLOBAL_CONFIG_FILE_PATH(home), "global", KNOWN_GLOBAL_CONFIG_KEYS);
|
|
70
68
|
const localConfig = repository
|
|
71
69
|
? await inspectConfig(join(repository.repoRoot, CONFIG_FILE_NAME), "local", KNOWN_CONFIG_KEYS)
|
|
72
70
|
: null;
|
|
73
71
|
const effectiveConfig = resolveEffectiveConfig(globalConfig.config, localConfig?.config ?? {}, repository?.repoRoot, home);
|
|
74
|
-
const registry = await inspectRegistry(home);
|
|
72
|
+
const registry = await inspectRegistry(home, runtime.registry);
|
|
75
73
|
return {
|
|
76
74
|
checks: [
|
|
77
75
|
await checkGitVersion(),
|
|
@@ -82,13 +80,13 @@ async function collectDoctorInspection(cwd, home, shell) {
|
|
|
82
80
|
skippedCheck("local-config", "local config not checked outside a Git repository"),
|
|
83
81
|
await checkWorktreeBase(repository, effectiveConfig, home),
|
|
84
82
|
registry.check,
|
|
85
|
-
await checkEditor(effectiveConfig),
|
|
86
|
-
await checkOrphanSlots(home),
|
|
83
|
+
await checkEditor(effectiveConfig, runtime.integrations.EDITORS),
|
|
84
|
+
await checkOrphanSlots(home, runtime.slots.loadSlots),
|
|
87
85
|
],
|
|
88
86
|
missingRegistryPaths: registry.missingPaths,
|
|
89
87
|
};
|
|
90
88
|
}
|
|
91
|
-
async function checkOrphanSlots(home) {
|
|
89
|
+
async function checkOrphanSlots(home, loadSlots) {
|
|
92
90
|
const slots = await loadSlots(home);
|
|
93
91
|
const missing = (await Promise.all(Object.keys(slots).map(async (path) => {
|
|
94
92
|
try {
|
|
@@ -142,10 +140,10 @@ function isDoctorInteractive(options) {
|
|
|
142
140
|
return options.interactive;
|
|
143
141
|
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
144
142
|
}
|
|
145
|
-
async function applyDoctorFixes(fixes, home) {
|
|
143
|
+
async function applyDoctorFixes(fixes, home, registry) {
|
|
146
144
|
return Promise.all(fixes.map(async (fix) => {
|
|
147
145
|
try {
|
|
148
|
-
const result = await removeMissingRegistryEntries(new Set(fix.paths ?? []), home);
|
|
146
|
+
const result = await registry.removeMissingRegistryEntries(new Set(fix.paths ?? []), home);
|
|
149
147
|
const removedCount = result.removedPaths.length;
|
|
150
148
|
const skippedCount = result.skippedPaths.length;
|
|
151
149
|
return {
|
|
@@ -168,7 +166,7 @@ async function applyDoctorFixes(fixes, home) {
|
|
|
168
166
|
}
|
|
169
167
|
}));
|
|
170
168
|
}
|
|
171
|
-
async function detectRepositoryOrSkip(cwd) {
|
|
169
|
+
async function detectRepositoryOrSkip(cwd, detectRepository) {
|
|
172
170
|
try {
|
|
173
171
|
return await detectRepository(cwd);
|
|
174
172
|
}
|
|
@@ -342,8 +340,8 @@ async function findNearestExistingPath(path) {
|
|
|
342
340
|
}
|
|
343
341
|
}
|
|
344
342
|
}
|
|
345
|
-
async function inspectRegistry(home) {
|
|
346
|
-
const entries = await loadRegistry(home);
|
|
343
|
+
async function inspectRegistry(home, registry) {
|
|
344
|
+
const entries = await registry.loadRegistry(home);
|
|
347
345
|
const missingEntries = await Promise.all(entries.map(async (entry) => ({
|
|
348
346
|
entry,
|
|
349
347
|
status: await inspectRegistryPath(entry.path),
|
|
@@ -368,7 +366,7 @@ async function inspectRegistry(home) {
|
|
|
368
366
|
}
|
|
369
367
|
return {
|
|
370
368
|
check: failedCheck("repo-registry", `${entries.length} repos registered, ${messageParts.join(", ")}`, missingCount > 0
|
|
371
|
-
? `remove confirmed stale entries from ${REGISTRY_FILE_PATH(home)}; check permissions for inaccessible paths`
|
|
369
|
+
? `remove confirmed stale entries from ${registry.REGISTRY_FILE_PATH(home)}; check permissions for inaccessible paths`
|
|
372
370
|
: "check permissions for inaccessible paths before removing registry entries"),
|
|
373
371
|
missingPaths,
|
|
374
372
|
};
|
|
@@ -399,7 +397,7 @@ async function pathExists(path) {
|
|
|
399
397
|
return false;
|
|
400
398
|
}
|
|
401
399
|
}
|
|
402
|
-
async function checkEditor(config) {
|
|
400
|
+
async function checkEditor(config, editors) {
|
|
403
401
|
const editor = config.editor;
|
|
404
402
|
if (typeof editor !== "string" || editor.length === 0) {
|
|
405
403
|
return skippedCheck("editor", "editor not configured (optional)");
|
|
@@ -407,7 +405,7 @@ async function checkEditor(config) {
|
|
|
407
405
|
if (await executableExists(editor)) {
|
|
408
406
|
return okCheck("editor", `editor "${editor}" found on PATH`);
|
|
409
407
|
}
|
|
410
|
-
const knownEditor =
|
|
408
|
+
const knownEditor = editors.some(({ cli }) => cli === editor);
|
|
411
409
|
return failedCheck("editor", `editor "${editor}" was not found on PATH`, knownEditor
|
|
412
410
|
? `install ${editor} or choose another editor with: gji open --save`
|
|
413
411
|
: "choose another editor with: gji open --save");
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface DoneCommandOptions {
|
|
2
3
|
branch?: string;
|
|
3
4
|
cwd: string;
|
|
4
5
|
force?: boolean;
|
|
5
6
|
json?: boolean;
|
|
6
7
|
keepBranch?: boolean;
|
|
8
|
+
runtime?: CliRuntime<"git" | "config" | "configStore" | "historyStore" | "slots" | "hooks" | "repositoryContext" | "worktrees" | "worktreeLifecycle">;
|
|
7
9
|
stderr: (chunk: string) => void;
|
|
8
10
|
stdout: (chunk: string) => void;
|
|
9
11
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { confirm, isCancel } from "@clack/prompts";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { isHeadless } from "
|
|
6
|
-
import { loadHistory } from "./history.js";
|
|
7
|
-
import { extractHooks, runHook } from "./hooks.js";
|
|
8
|
-
import { detectRepository, listWorktrees } from "./repo.js";
|
|
9
|
-
import { writeShellOutput } from "./shell-handoff.js";
|
|
10
|
-
import { releaseWorktreeSlot } from "./slots.js";
|
|
3
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
4
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
5
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
11
6
|
import { finalizeUndoOperation, recordUndoOperation } from "./undo.js";
|
|
12
|
-
import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, removeWorktree, } from "./worktree-management.js";
|
|
13
7
|
const DONE_OUTPUT_FILE_ENV = "GJI_DONE_OUTPUT_FILE";
|
|
14
8
|
export async function runDoneCommand(options) {
|
|
9
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
10
|
+
const { readWorktreeHealth, isBranchMergedInto, resolveRemoteDefaultBranch, runGit, } = runtime.git;
|
|
11
|
+
const { loadEffectiveConfig, resolveConfigString } = runtime.config;
|
|
12
|
+
const { loadHistory } = runtime.historyStore;
|
|
13
|
+
const { releaseWorktreeSlot } = runtime.slots;
|
|
14
|
+
const { extractHooks, runHook } = runtime.hooks;
|
|
15
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
16
|
+
const { listWorktrees } = runtime.worktrees;
|
|
17
|
+
const { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, removeWorktree, } = runtime.worktreeLifecycle;
|
|
15
18
|
const repository = await detectRepository(options.cwd);
|
|
16
19
|
if (!repository.isWorktree && !options.branch)
|
|
17
20
|
return doneError(options, "gji done: not inside a linked worktree");
|
|
@@ -24,12 +27,12 @@ export async function runDoneCommand(options) {
|
|
|
24
27
|
? `gji done: no linked worktree found for ${options.branch}`
|
|
25
28
|
: "gji done: not inside a linked worktree");
|
|
26
29
|
const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
27
|
-
await refreshUpstream(repository.repoRoot, target.path);
|
|
30
|
+
await refreshUpstream(repository.repoRoot, target.path, runGit);
|
|
28
31
|
const health = await readWorktreeHealth(target.path);
|
|
29
32
|
if (target.branch &&
|
|
30
33
|
!options.force &&
|
|
31
34
|
!options.keepBranch &&
|
|
32
|
-
!(await isSafeToComplete(repository.repoRoot, target.branch, health, config))) {
|
|
35
|
+
!(await isSafeToComplete(repository.repoRoot, target.branch, health, config, resolveConfigString, resolveRemoteDefaultBranch, isBranchMergedInto, runGit))) {
|
|
33
36
|
if (options.json || isHeadless())
|
|
34
37
|
return doneError(options, "branch is not merged and its upstream is not gone; use --force");
|
|
35
38
|
const choice = await confirm({
|
|
@@ -55,7 +58,7 @@ export async function runDoneCommand(options) {
|
|
|
55
58
|
}
|
|
56
59
|
let journal;
|
|
57
60
|
try {
|
|
58
|
-
journal = await recordUndoOperation("done", repository.repoRoot, [target]);
|
|
61
|
+
journal = await recordUndoOperation("done", repository.repoRoot, [target], undefined, runtime);
|
|
59
62
|
}
|
|
60
63
|
catch (error) {
|
|
61
64
|
return doneError(options, `could not write undo journal; no worktree was removed: ${toMessage(error)}`);
|
|
@@ -72,12 +75,12 @@ export async function runDoneCommand(options) {
|
|
|
72
75
|
}
|
|
73
76
|
catch (error) {
|
|
74
77
|
if (!isWorktreeForceRemovalError(error)) {
|
|
75
|
-
await finalizeUndoOperation(journal, []);
|
|
78
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
76
79
|
return doneError(options, `Failed to remove worktree at ${target.path}: ${toMessage(error)}`);
|
|
77
80
|
}
|
|
78
81
|
if (!options.force) {
|
|
79
82
|
if (options.json || isHeadless()) {
|
|
80
|
-
await finalizeUndoOperation(journal, []);
|
|
83
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
81
84
|
return doneError(options, "worktree requires force removal; use --force");
|
|
82
85
|
}
|
|
83
86
|
const choice = await confirm({
|
|
@@ -87,7 +90,7 @@ export async function runDoneCommand(options) {
|
|
|
87
90
|
initialValue: false,
|
|
88
91
|
});
|
|
89
92
|
if (isCancel(choice) || !choice) {
|
|
90
|
-
await finalizeUndoOperation(journal, []);
|
|
93
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
91
94
|
return doneError(options, "Aborted");
|
|
92
95
|
}
|
|
93
96
|
}
|
|
@@ -95,7 +98,7 @@ export async function runDoneCommand(options) {
|
|
|
95
98
|
await forceRemoveWorktree(repository.repoRoot, target.path);
|
|
96
99
|
}
|
|
97
100
|
catch (forceError) {
|
|
98
|
-
await finalizeUndoOperation(journal, []);
|
|
101
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
99
102
|
return doneError(options, `Failed to remove worktree at ${target.path}: ${toMessage(forceError)}`);
|
|
100
103
|
}
|
|
101
104
|
}
|
|
@@ -122,12 +125,12 @@ export async function runDoneCommand(options) {
|
|
|
122
125
|
}
|
|
123
126
|
}
|
|
124
127
|
}
|
|
125
|
-
await finalizeUndoOperation(journal, [target]);
|
|
128
|
+
await finalizeUndoOperation(journal, [target], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
126
129
|
await releaseWorktreeSlot(target.path);
|
|
127
130
|
await runGit(repository.repoRoot, ["worktree", "prune"]).catch(() => undefined);
|
|
128
131
|
const shouldMove = options.branch === undefined || target.path === repository.currentRoot;
|
|
129
132
|
const movedTo = shouldMove
|
|
130
|
-
? await resolveDoneDestination(repository.repoRoot, target.path)
|
|
133
|
+
? await resolveDoneDestination(repository.repoRoot, target.path, loadHistory, runGit)
|
|
131
134
|
: null;
|
|
132
135
|
if (!options.json && movedTo)
|
|
133
136
|
await writeShellOutput(DONE_OUTPUT_FILE_ENV, movedTo, options.stdout);
|
|
@@ -139,7 +142,7 @@ export async function runDoneCommand(options) {
|
|
|
139
142
|
options.stderr(`✓ removed ${target.branch ?? "detached worktree"} (worktree${branchDeleted ? " + branch" : ""})${movedTo ? ` → back at ${movedTo}` : ""} · undo: gji undo\n`);
|
|
140
143
|
return 0;
|
|
141
144
|
}
|
|
142
|
-
async function isSafeToComplete(repoRoot, branch, health, config) {
|
|
145
|
+
async function isSafeToComplete(repoRoot, branch, health, config, resolveConfigString, resolveRemoteDefaultBranch, isBranchMergedInto, runGit) {
|
|
143
146
|
if (health.upstreamGone)
|
|
144
147
|
return true;
|
|
145
148
|
const remote = resolveConfigString(config, "syncRemote") ?? "origin";
|
|
@@ -148,7 +151,7 @@ async function isSafeToComplete(repoRoot, branch, health, config) {
|
|
|
148
151
|
(await resolveRemoteDefaultBranch(repoRoot, remote).catch(() => null));
|
|
149
152
|
if (remoteDefault) {
|
|
150
153
|
for (const base of [`${remote}/${remoteDefault}`, remoteDefault]) {
|
|
151
|
-
if (await gitRefExists(repoRoot, base))
|
|
154
|
+
if (await gitRefExists(repoRoot, base, runGit))
|
|
152
155
|
return isBranchMergedInto(repoRoot, branch, base).catch(() => false);
|
|
153
156
|
}
|
|
154
157
|
return false;
|
|
@@ -162,7 +165,7 @@ async function isSafeToComplete(repoRoot, branch, health, config) {
|
|
|
162
165
|
? false
|
|
163
166
|
: isBranchMergedInto(repoRoot, branch, base).catch(() => false);
|
|
164
167
|
}
|
|
165
|
-
async function gitRefExists(repoRoot, ref) {
|
|
168
|
+
async function gitRefExists(repoRoot, ref, runGit) {
|
|
166
169
|
try {
|
|
167
170
|
await runGit(repoRoot, ["rev-parse", "--verify", ref]);
|
|
168
171
|
return true;
|
|
@@ -171,7 +174,7 @@ async function gitRefExists(repoRoot, ref) {
|
|
|
171
174
|
return false;
|
|
172
175
|
}
|
|
173
176
|
}
|
|
174
|
-
async function refreshUpstream(repoRoot, worktreePath) {
|
|
177
|
+
async function refreshUpstream(repoRoot, worktreePath, runGit) {
|
|
175
178
|
const upstream = await runGit(worktreePath, [
|
|
176
179
|
"rev-parse",
|
|
177
180
|
"--abbrev-ref",
|
|
@@ -183,7 +186,7 @@ async function refreshUpstream(repoRoot, worktreePath) {
|
|
|
183
186
|
return;
|
|
184
187
|
await runGit(repoRoot, ["fetch", "--prune", "--quiet", remote]).catch(() => undefined);
|
|
185
188
|
}
|
|
186
|
-
async function resolveDoneDestination(repoRoot, removedPath) {
|
|
189
|
+
async function resolveDoneDestination(repoRoot, removedPath, loadHistory, runGit) {
|
|
187
190
|
const history = await loadHistory();
|
|
188
191
|
for (const entry of history) {
|
|
189
192
|
if (entry.path !== removedPath && entry.path !== repoRoot) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type QueryWorktreePullRequests, type WorktreePromptEntry, type WorktreePromptScope } from "
|
|
1
|
+
import { type QueryWorktreePullRequests, type WorktreePromptEntry, type WorktreePromptScope } from "../../presentation/worktree/picker.js";
|
|
2
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
2
3
|
export interface GoCommandOptions {
|
|
3
4
|
branch?: string;
|
|
4
5
|
cwd: string;
|
|
@@ -8,11 +9,15 @@ export interface GoCommandOptions {
|
|
|
8
9
|
quiet?: boolean;
|
|
9
10
|
stderr: (chunk: string) => void;
|
|
10
11
|
stdout: (chunk: string) => void;
|
|
12
|
+
runtime?: GoRuntime;
|
|
11
13
|
}
|
|
14
|
+
type GoRuntime = CliRuntime<"bootstrap" | "config" | "contextCard" | "git" | "history" | "historyStore" | "hooks" | "integrations" | "repositoryContext" | "repositoryRefs" | "repositoryRegistry" | "slots" | "tasks" | "worktrees" | "worktreeCatalog">;
|
|
12
15
|
export interface GoCommandDependencies {
|
|
13
16
|
confirmBranchCreation: (branch: string) => Promise<boolean>;
|
|
14
17
|
promptForWorktree: (worktrees: WorktreePromptEntry[], scope?: WorktreePromptScope) => Promise<string | null>;
|
|
15
18
|
queryPullRequests: QueryWorktreePullRequests;
|
|
19
|
+
runtime: GoRuntime;
|
|
16
20
|
}
|
|
17
21
|
export declare function createGoCommand(dependencies?: Partial<GoCommandDependencies>): (options: GoCommandOptions) => Promise<number>;
|
|
18
22
|
export declare const runGoCommand: (options: GoCommandOptions) => Promise<number>;
|
|
23
|
+
export {};
|