@solaqua/gji 0.14.0 → 0.15.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/cli/commands/clean.d.ts +1 -0
- package/dist/cli/commands/clean.js +55 -16
- package/dist/cli/program.js +5 -0
- package/dist/domain/worktree/path-filter.d.ts +13 -0
- package/dist/domain/worktree/path-filter.js +72 -0
- package/dist/domain/worktree/policy.d.ts +1 -0
- package/dist/domain/worktree/policy.js +1 -1
- package/dist/gji-bundle.mjs +272 -161
- package/dist/presentation/shell/completion.js +7 -2
- package/man/man1/gji-back.1 +1 -1
- package/man/man1/gji-clean.1 +4 -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
|
@@ -5386,7 +5386,7 @@ var require_minimist = __commonJS({
|
|
|
5386
5386
|
var require_rc = __commonJS({
|
|
5387
5387
|
"node_modules/.pnpm/rc@1.2.8/node_modules/rc/index.js"(exports, module) {
|
|
5388
5388
|
var cc = require_utils();
|
|
5389
|
-
var
|
|
5389
|
+
var join17 = __require("path").join;
|
|
5390
5390
|
var deepExtend = require_deep_extend();
|
|
5391
5391
|
var etc = "/etc";
|
|
5392
5392
|
var win = process.platform === "win32";
|
|
@@ -5411,15 +5411,15 @@ var require_rc = __commonJS({
|
|
|
5411
5411
|
}
|
|
5412
5412
|
if (!win)
|
|
5413
5413
|
[
|
|
5414
|
-
|
|
5415
|
-
|
|
5414
|
+
join17(etc, name, "config"),
|
|
5415
|
+
join17(etc, name + "rc")
|
|
5416
5416
|
].forEach(addConfigFile);
|
|
5417
5417
|
if (home)
|
|
5418
5418
|
[
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5419
|
+
join17(home, ".config", name, "config"),
|
|
5420
|
+
join17(home, ".config", name),
|
|
5421
|
+
join17(home, "." + name, "config"),
|
|
5422
|
+
join17(home, "." + name + "rc")
|
|
5423
5423
|
].forEach(addConfigFile);
|
|
5424
5424
|
addConfigFile(cc.find("." + name + "rc"));
|
|
5425
5425
|
if (env5.config) addConfigFile(env5.config);
|
|
@@ -6757,11 +6757,11 @@ var require_util = __commonJS({
|
|
|
6757
6757
|
if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
|
|
6758
6758
|
return name2;
|
|
6759
6759
|
}
|
|
6760
|
-
const
|
|
6761
|
-
if (
|
|
6760
|
+
const dirname15 = path9.dirname(name2);
|
|
6761
|
+
if (dirname15 === name2) {
|
|
6762
6762
|
return original;
|
|
6763
6763
|
}
|
|
6764
|
-
return find(
|
|
6764
|
+
return find(dirname15, original);
|
|
6765
6765
|
} catch (error) {
|
|
6766
6766
|
if (name2 === original) {
|
|
6767
6767
|
if (error.code === "ENOENT") {
|
|
@@ -9519,7 +9519,7 @@ var retryifyAsync = (fn, options) => {
|
|
|
9519
9519
|
throw error;
|
|
9520
9520
|
const delay2 = Math.round(interval * Math.random());
|
|
9521
9521
|
if (delay2 > 0) {
|
|
9522
|
-
const delayPromise = new Promise((
|
|
9522
|
+
const delayPromise = new Promise((resolve13) => setTimeout(resolve13, delay2));
|
|
9523
9523
|
return delayPromise.then(() => attempt.apply(void 0, args));
|
|
9524
9524
|
} else {
|
|
9525
9525
|
return attempt.apply(void 0, args);
|
|
@@ -9778,14 +9778,14 @@ var Temp = {
|
|
|
9778
9778
|
}
|
|
9779
9779
|
},
|
|
9780
9780
|
truncate: (filePath) => {
|
|
9781
|
-
const
|
|
9782
|
-
if (
|
|
9781
|
+
const basename14 = path2.basename(filePath);
|
|
9782
|
+
if (basename14.length <= LIMIT_BASENAME_LENGTH)
|
|
9783
9783
|
return filePath;
|
|
9784
|
-
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(
|
|
9784
|
+
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename14);
|
|
9785
9785
|
if (!truncable)
|
|
9786
9786
|
return filePath;
|
|
9787
|
-
const truncationLength =
|
|
9788
|
-
return `${filePath.slice(0, -
|
|
9787
|
+
const truncationLength = basename14.length - LIMIT_BASENAME_LENGTH;
|
|
9788
|
+
return `${filePath.slice(0, -basename14.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`;
|
|
9789
9789
|
}
|
|
9790
9790
|
};
|
|
9791
9791
|
node_default(Temp.purgeSyncAll);
|
|
@@ -11085,14 +11085,14 @@ var TimeoutError = class extends Error {
|
|
|
11085
11085
|
|
|
11086
11086
|
// node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/timeout.js
|
|
11087
11087
|
async function timeout(request, init, abortController, options) {
|
|
11088
|
-
return new Promise((
|
|
11088
|
+
return new Promise((resolve13, reject) => {
|
|
11089
11089
|
const timeoutId = setTimeout(() => {
|
|
11090
11090
|
if (abortController) {
|
|
11091
11091
|
abortController.abort();
|
|
11092
11092
|
}
|
|
11093
11093
|
reject(new TimeoutError(request));
|
|
11094
11094
|
}, options.timeout);
|
|
11095
|
-
void options.fetch(request, init).then(
|
|
11095
|
+
void options.fetch(request, init).then(resolve13).catch(reject).then(() => {
|
|
11096
11096
|
clearTimeout(timeoutId);
|
|
11097
11097
|
});
|
|
11098
11098
|
});
|
|
@@ -11100,7 +11100,7 @@ async function timeout(request, init, abortController, options) {
|
|
|
11100
11100
|
|
|
11101
11101
|
// node_modules/.pnpm/ky@1.14.3/node_modules/ky/distribution/utils/delay.js
|
|
11102
11102
|
async function delay(ms, { signal }) {
|
|
11103
|
-
return new Promise((
|
|
11103
|
+
return new Promise((resolve13, reject) => {
|
|
11104
11104
|
if (signal) {
|
|
11105
11105
|
signal.throwIfAborted();
|
|
11106
11106
|
signal.addEventListener("abort", abortHandler, { once: true });
|
|
@@ -11111,7 +11111,7 @@ async function delay(ms, { signal }) {
|
|
|
11111
11111
|
}
|
|
11112
11112
|
const timeoutId = setTimeout(() => {
|
|
11113
11113
|
signal?.removeEventListener("abort", abortHandler);
|
|
11114
|
-
|
|
11114
|
+
resolve13();
|
|
11115
11115
|
}, ms);
|
|
11116
11116
|
});
|
|
11117
11117
|
}
|
|
@@ -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.15.0",
|
|
12814
12814
|
description: "Git worktree CLI for fast context switching.",
|
|
12815
12815
|
license: "MIT",
|
|
12816
12816
|
author: "sjquant",
|
|
@@ -13093,7 +13093,7 @@ async function ensureDestinationDirectory(root, path9) {
|
|
|
13093
13093
|
// src/infrastructure/process/command-runner.ts
|
|
13094
13094
|
import { spawn as spawn2 } from "node:child_process";
|
|
13095
13095
|
var runCommand = async (command, cwd, stderr, stdout3 = (chunk) => process.stdout.write(chunk), options) => {
|
|
13096
|
-
await new Promise((
|
|
13096
|
+
await new Promise((resolve13, reject) => {
|
|
13097
13097
|
const shell = options?.shell ?? true;
|
|
13098
13098
|
const [executable, args] = shell ? [command, []] : splitCommand(command);
|
|
13099
13099
|
const child = spawn2(executable, args, {
|
|
@@ -13112,7 +13112,7 @@ var runCommand = async (command, cwd, stderr, stdout3 = (chunk) => process.stdou
|
|
|
13112
13112
|
if (code !== 0) {
|
|
13113
13113
|
reject(new Error(`exited with code ${code}`));
|
|
13114
13114
|
} else {
|
|
13115
|
-
|
|
13115
|
+
resolve13();
|
|
13116
13116
|
}
|
|
13117
13117
|
});
|
|
13118
13118
|
child.on("error", reject);
|
|
@@ -14019,9 +14019,9 @@ var EDITORS = [
|
|
|
14019
14019
|
];
|
|
14020
14020
|
async function defaultSpawnEditor(cli, args) {
|
|
14021
14021
|
const child = spawn3(cli, args, { detached: true, stdio: "ignore" });
|
|
14022
|
-
await new Promise((
|
|
14022
|
+
await new Promise((resolve13, reject) => {
|
|
14023
14023
|
child.once("error", reject);
|
|
14024
|
-
child.once("spawn",
|
|
14024
|
+
child.once("spawn", resolve13);
|
|
14025
14025
|
});
|
|
14026
14026
|
child.unref();
|
|
14027
14027
|
}
|
|
@@ -14863,7 +14863,7 @@ async function runArgvHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
|
14863
14863
|
stderr("gji: hook argv command must include a non-empty command\n");
|
|
14864
14864
|
return;
|
|
14865
14865
|
}
|
|
14866
|
-
await new Promise((
|
|
14866
|
+
await new Promise((resolve13) => {
|
|
14867
14867
|
const child = spawn4(command, args, {
|
|
14868
14868
|
cwd,
|
|
14869
14869
|
shell: false,
|
|
@@ -14883,18 +14883,18 @@ async function runArgvHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
|
14883
14883
|
`
|
|
14884
14884
|
);
|
|
14885
14885
|
}
|
|
14886
|
-
|
|
14886
|
+
resolve13();
|
|
14887
14887
|
});
|
|
14888
14888
|
child.on("error", (err) => {
|
|
14889
14889
|
stderr(`gji: hook failed to start: ${err.message}
|
|
14890
14890
|
`);
|
|
14891
|
-
|
|
14891
|
+
resolve13();
|
|
14892
14892
|
});
|
|
14893
14893
|
});
|
|
14894
14894
|
}
|
|
14895
14895
|
async function runShellHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
14896
14896
|
const interpolated = interpolate(hookCmd, context);
|
|
14897
|
-
await new Promise((
|
|
14897
|
+
await new Promise((resolve13) => {
|
|
14898
14898
|
const child = spawn4(interpolated, {
|
|
14899
14899
|
cwd,
|
|
14900
14900
|
shell: true,
|
|
@@ -14912,12 +14912,12 @@ async function runShellHook(hookCmd, cwd, context, stderr, stdout3) {
|
|
|
14912
14912
|
stderr(`gji: hook exited with code ${code}: ${interpolated}
|
|
14913
14913
|
`);
|
|
14914
14914
|
}
|
|
14915
|
-
|
|
14915
|
+
resolve13();
|
|
14916
14916
|
});
|
|
14917
14917
|
child.on("error", (err) => {
|
|
14918
14918
|
stderr(`gji: hook failed to start: ${err.message}
|
|
14919
14919
|
`);
|
|
14920
|
-
|
|
14920
|
+
resolve13();
|
|
14921
14921
|
});
|
|
14922
14922
|
});
|
|
14923
14923
|
}
|
|
@@ -15082,8 +15082,8 @@ async function removeMissingRegistryEntries(paths, home = homedir4()) {
|
|
|
15082
15082
|
async function withRegistryLock(registryPath, operation) {
|
|
15083
15083
|
const previous = registryLocks.get(registryPath);
|
|
15084
15084
|
let release;
|
|
15085
|
-
const current = new Promise((
|
|
15086
|
-
release =
|
|
15085
|
+
const current = new Promise((resolve13) => {
|
|
15086
|
+
release = resolve13;
|
|
15087
15087
|
});
|
|
15088
15088
|
registryLocks.set(registryPath, current);
|
|
15089
15089
|
if (previous) await previous;
|
|
@@ -15690,6 +15690,10 @@ function formatAge(timestamp) {
|
|
|
15690
15690
|
return `${days}d ago`;
|
|
15691
15691
|
}
|
|
15692
15692
|
|
|
15693
|
+
// src/cli/commands/clean.ts
|
|
15694
|
+
import { realpath as realpath5 } from "node:fs/promises";
|
|
15695
|
+
import { basename as basename8, dirname as dirname10, join as join12, resolve as resolve11 } from "node:path";
|
|
15696
|
+
|
|
15693
15697
|
// node_modules/.pnpm/@clack+core@0.5.0/node_modules/@clack/core/dist/index.mjs
|
|
15694
15698
|
var import_sisteransi = __toESM(require_src(), 1);
|
|
15695
15699
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
@@ -16480,6 +16484,121 @@ function throwIfAborted2(signal) {
|
|
|
16480
16484
|
if (signal?.aborted) throw new Error("Operation cancelled");
|
|
16481
16485
|
}
|
|
16482
16486
|
|
|
16487
|
+
// src/domain/worktree/path-filter.ts
|
|
16488
|
+
function createWorktreePathFilter(patterns) {
|
|
16489
|
+
const matchers = patterns.map(createPathMatcher);
|
|
16490
|
+
if (matchers.length === 0) return () => true;
|
|
16491
|
+
return (worktreePath) => matchers.some((matches) => matches(worktreePath));
|
|
16492
|
+
}
|
|
16493
|
+
function findGlobIndex(pattern) {
|
|
16494
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
16495
|
+
const character = pattern[index];
|
|
16496
|
+
if (character === "\\") index += 1;
|
|
16497
|
+
else if (character === "*" || character === "?") return index;
|
|
16498
|
+
}
|
|
16499
|
+
return -1;
|
|
16500
|
+
}
|
|
16501
|
+
function escapePathPattern(path9) {
|
|
16502
|
+
return path9.replace(/[\\*?]/g, "\\$&");
|
|
16503
|
+
}
|
|
16504
|
+
function unescapePathPattern(pattern) {
|
|
16505
|
+
return pattern.replace(/\\(.)/g, "$1");
|
|
16506
|
+
}
|
|
16507
|
+
function createPathMatcher(pattern) {
|
|
16508
|
+
if (findGlobIndex(pattern) !== -1) {
|
|
16509
|
+
const regExp = globToRegExp(pattern);
|
|
16510
|
+
return (path9) => regExp.test(path9);
|
|
16511
|
+
}
|
|
16512
|
+
const literal = unescapePathPattern(pattern);
|
|
16513
|
+
const prefix = literal === "/" ? "/" : `${literal}/`;
|
|
16514
|
+
return (path9) => path9 === literal || path9.startsWith(prefix);
|
|
16515
|
+
}
|
|
16516
|
+
function globToRegExp(pattern) {
|
|
16517
|
+
let source = "";
|
|
16518
|
+
for (let index = 0; index < pattern.length; index += 1) {
|
|
16519
|
+
let character = pattern[index];
|
|
16520
|
+
if (character === "*") {
|
|
16521
|
+
if (pattern[index + 1] === "*") {
|
|
16522
|
+
index += 1;
|
|
16523
|
+
if (pattern[index + 1] === "/") {
|
|
16524
|
+
index += 1;
|
|
16525
|
+
source += "(?:.*/)?";
|
|
16526
|
+
} else {
|
|
16527
|
+
source += ".*";
|
|
16528
|
+
}
|
|
16529
|
+
} else {
|
|
16530
|
+
source += "[^/]*";
|
|
16531
|
+
}
|
|
16532
|
+
} else if (character === "?") {
|
|
16533
|
+
source += "[^/]";
|
|
16534
|
+
} else {
|
|
16535
|
+
if (character === "\\" && index + 1 < pattern.length) {
|
|
16536
|
+
index += 1;
|
|
16537
|
+
character = pattern[index];
|
|
16538
|
+
}
|
|
16539
|
+
source += character.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
16540
|
+
}
|
|
16541
|
+
}
|
|
16542
|
+
return new RegExp(`^${source}$`);
|
|
16543
|
+
}
|
|
16544
|
+
|
|
16545
|
+
// src/domain/worktree/policy.ts
|
|
16546
|
+
import { homedir as homedir5 } from "node:os";
|
|
16547
|
+
import { basename as basename7, dirname as dirname8, join as join10 } from "node:path";
|
|
16548
|
+
function resolveWorktreePath(repoRoot, branch, basePath) {
|
|
16549
|
+
const segments = branch.split("/").filter(Boolean);
|
|
16550
|
+
if (segments.length === 0) {
|
|
16551
|
+
throw new Error("Branch name must not be empty.");
|
|
16552
|
+
}
|
|
16553
|
+
if (segments.some((segment) => segment === "." || segment === "..")) {
|
|
16554
|
+
throw new Error(
|
|
16555
|
+
`Branch name '${branch}' contains an invalid path segment.`
|
|
16556
|
+
);
|
|
16557
|
+
}
|
|
16558
|
+
const base = basePath ? expandTildeInPath(basePath) : join10(dirname8(repoRoot), "worktrees", basename7(repoRoot));
|
|
16559
|
+
return join10(base, ...segments);
|
|
16560
|
+
}
|
|
16561
|
+
function validateBranchName(name) {
|
|
16562
|
+
if (name.length === 0) {
|
|
16563
|
+
return "Branch name must not be empty.";
|
|
16564
|
+
}
|
|
16565
|
+
if (/[\x00-\x1f\x7f ~^:?*[\\\s]/.test(name)) {
|
|
16566
|
+
return `Branch name '${name}' contains an invalid character.`;
|
|
16567
|
+
}
|
|
16568
|
+
if (name.startsWith("-")) {
|
|
16569
|
+
return `Branch name '${name}' must not start with a dash.`;
|
|
16570
|
+
}
|
|
16571
|
+
if (name.startsWith("/") || name.endsWith("/") || name.includes("//")) {
|
|
16572
|
+
return `Branch name '${name}' has invalid slash placement.`;
|
|
16573
|
+
}
|
|
16574
|
+
if (name.includes("..")) {
|
|
16575
|
+
return `Branch name '${name}' must not contain '..'.`;
|
|
16576
|
+
}
|
|
16577
|
+
if (name.endsWith(".")) {
|
|
16578
|
+
return `Branch name '${name}' must not end with '.'.`;
|
|
16579
|
+
}
|
|
16580
|
+
if (name.includes("@{")) {
|
|
16581
|
+
return `Branch name '${name}' must not contain '@{'.`;
|
|
16582
|
+
}
|
|
16583
|
+
if (name === "@") {
|
|
16584
|
+
return "Branch name cannot be '@'.";
|
|
16585
|
+
}
|
|
16586
|
+
for (const segment of name.split("/")) {
|
|
16587
|
+
if (segment.startsWith(".")) {
|
|
16588
|
+
return `Branch name '${name}' contains a path component starting with '.'.`;
|
|
16589
|
+
}
|
|
16590
|
+
if (segment.endsWith(".lock")) {
|
|
16591
|
+
return `Branch name '${name}' contains a path component ending with '.lock'.`;
|
|
16592
|
+
}
|
|
16593
|
+
}
|
|
16594
|
+
return null;
|
|
16595
|
+
}
|
|
16596
|
+
function expandTildeInPath(path9) {
|
|
16597
|
+
if (path9 === "~") return homedir5();
|
|
16598
|
+
if (path9.startsWith("~/")) return join10(homedir5(), path9.slice(2));
|
|
16599
|
+
return path9;
|
|
16600
|
+
}
|
|
16601
|
+
|
|
16483
16602
|
// src/presentation/worktree/picker.ts
|
|
16484
16603
|
import { env as env4, platform as platform2, stdin, stdout } from "node:process";
|
|
16485
16604
|
|
|
@@ -17489,21 +17608,21 @@ import {
|
|
|
17489
17608
|
unlink as unlink2,
|
|
17490
17609
|
writeFile as writeFile7
|
|
17491
17610
|
} from "node:fs/promises";
|
|
17492
|
-
import { homedir as
|
|
17493
|
-
import { dirname as
|
|
17611
|
+
import { homedir as homedir6 } from "node:os";
|
|
17612
|
+
import { dirname as dirname9, join as join11, resolve as resolve10 } from "node:path";
|
|
17494
17613
|
var MAX_UNDO_RECORDS = 20;
|
|
17495
17614
|
var UNDO_LOCK_STALE_AFTER_MS = 6e4;
|
|
17496
17615
|
var UNDO_LOCK_RETRY_DELAY_MS = 5;
|
|
17497
17616
|
var UNDO_LOCK_WAIT_TIMEOUT_MS = UNDO_LOCK_STALE_AFTER_MS + UNDO_LOCK_RETRY_DELAY_MS;
|
|
17498
17617
|
var undoLogQueues = /* @__PURE__ */ new Map();
|
|
17499
|
-
function undoLogPath(home =
|
|
17618
|
+
function undoLogPath(home = homedir6(), globalConfigDirectory = defaultCliDependencies.configStore.GLOBAL_CONFIG_DIRECTORY) {
|
|
17500
17619
|
const configured = process.env.GJI_CONFIG_DIR;
|
|
17501
|
-
return
|
|
17502
|
-
configured ? resolve10(configured) :
|
|
17620
|
+
return join11(
|
|
17621
|
+
configured ? resolve10(configured) : join11(home, globalConfigDirectory),
|
|
17503
17622
|
"undo-log.json"
|
|
17504
17623
|
);
|
|
17505
17624
|
}
|
|
17506
|
-
async function loadUndoRecords(home =
|
|
17625
|
+
async function loadUndoRecords(home = homedir6(), globalConfigDirectory = defaultCliDependencies.configStore.GLOBAL_CONFIG_DIRECTORY) {
|
|
17507
17626
|
try {
|
|
17508
17627
|
const parsed = JSON.parse(
|
|
17509
17628
|
await readFile7(undoLogPath(home, globalConfigDirectory), "utf8")
|
|
@@ -17513,7 +17632,7 @@ async function loadUndoRecords(home = homedir5(), globalConfigDirectory = defaul
|
|
|
17513
17632
|
return [];
|
|
17514
17633
|
}
|
|
17515
17634
|
}
|
|
17516
|
-
async function recordUndoOperation(op, repoRoot, worktrees, home =
|
|
17635
|
+
async function recordUndoOperation(op, repoRoot, worktrees, home = homedir6(), runtime = defaultCliDependencies) {
|
|
17517
17636
|
const globalConfigDirectory = runtime.configStore.GLOBAL_CONFIG_DIRECTORY;
|
|
17518
17637
|
const { isDirtyWorktree: isDirtyWorktree2, runGit: runGit2 } = runtime.git;
|
|
17519
17638
|
const entries = [];
|
|
@@ -17563,7 +17682,7 @@ async function recordUndoOperation(op, repoRoot, worktrees, home = homedir5(), r
|
|
|
17563
17682
|
globalConfigDirectory
|
|
17564
17683
|
);
|
|
17565
17684
|
}
|
|
17566
|
-
async function finalizeUndoOperation(record, removedWorktrees, home =
|
|
17685
|
+
async function finalizeUndoOperation(record, removedWorktrees, home = homedir6(), globalConfigDirectory = defaultCliDependencies.configStore.GLOBAL_CONFIG_DIRECTORY) {
|
|
17567
17686
|
const removedPaths = new Set(
|
|
17568
17687
|
removedWorktrees.map((worktree) => worktree.path)
|
|
17569
17688
|
);
|
|
@@ -17583,7 +17702,7 @@ async function finalizeUndoOperation(record, removedWorktrees, home = homedir5()
|
|
|
17583
17702
|
globalConfigDirectory
|
|
17584
17703
|
);
|
|
17585
17704
|
}
|
|
17586
|
-
async function restoreUndoRecord(record, home =
|
|
17705
|
+
async function restoreUndoRecord(record, home = homedir6(), runtime = defaultCliDependencies) {
|
|
17587
17706
|
const globalConfigDirectory = runtime.configStore.GLOBAL_CONFIG_DIRECTORY;
|
|
17588
17707
|
const restored = [];
|
|
17589
17708
|
const failed = [];
|
|
@@ -17628,7 +17747,7 @@ async function runUndoCommand(options) {
|
|
|
17628
17747
|
options.stderr(
|
|
17629
17748
|
"Warning: undo journal is invalid; starting with an empty journal\n"
|
|
17630
17749
|
);
|
|
17631
|
-
const records = await loadUndoRecords(
|
|
17750
|
+
const records = await loadUndoRecords(homedir6(), globalConfigDirectory);
|
|
17632
17751
|
if (options.list) {
|
|
17633
17752
|
if (options.json) options.stdout(`${JSON.stringify(records, null, 2)}
|
|
17634
17753
|
`);
|
|
@@ -17675,7 +17794,7 @@ async function runUndoCommand(options) {
|
|
|
17675
17794
|
}
|
|
17676
17795
|
if (!record)
|
|
17677
17796
|
return emitUndoError(options, `undo record not found: ${options.id}`);
|
|
17678
|
-
const result = await restoreUndoRecord(record,
|
|
17797
|
+
const result = await restoreUndoRecord(record, homedir6(), runtime);
|
|
17679
17798
|
if (options.json)
|
|
17680
17799
|
options.stdout(
|
|
17681
17800
|
`${JSON.stringify({ restored: result.restored.map((entry) => ({ branch: entry.branch, path: entry.path })), failed: result.failed.map((entry) => ({ branch: entry.branch, path: entry.path, error: entry.message })) }, null, 2)}
|
|
@@ -17703,7 +17822,7 @@ async function runUndoCommand(options) {
|
|
|
17703
17822
|
async function hasMalformedUndoLog(globalConfigDirectory = defaultCliDependencies.configStore.GLOBAL_CONFIG_DIRECTORY) {
|
|
17704
17823
|
try {
|
|
17705
17824
|
const parsed = JSON.parse(
|
|
17706
|
-
await readFile7(undoLogPath(
|
|
17825
|
+
await readFile7(undoLogPath(homedir6(), globalConfigDirectory), "utf8")
|
|
17707
17826
|
);
|
|
17708
17827
|
return !Array.isArray(parsed) || parsed.some((entry) => !isUndoRecord(entry));
|
|
17709
17828
|
} catch (error) {
|
|
@@ -17795,7 +17914,7 @@ async function assertCommitExists(repoRoot, headSha, runGit2) {
|
|
|
17795
17914
|
}
|
|
17796
17915
|
async function enqueueUndoLogWrite(home, operation, globalConfigDirectory = defaultCliDependencies.configStore.GLOBAL_CONFIG_DIRECTORY) {
|
|
17797
17916
|
const path9 = undoLogPath(home, globalConfigDirectory);
|
|
17798
|
-
await mkdir6(
|
|
17917
|
+
await mkdir6(dirname9(path9), { recursive: true });
|
|
17799
17918
|
const previous = undoLogQueues.get(path9) ?? Promise.resolve();
|
|
17800
17919
|
const runLocked = async () => {
|
|
17801
17920
|
const release = await acquireUndoLogLock(path9);
|
|
@@ -17836,7 +17955,7 @@ async function acquireUndoLogLock(path9) {
|
|
|
17836
17955
|
const remainingMs = deadline - Date.now();
|
|
17837
17956
|
if (remainingMs > 0)
|
|
17838
17957
|
await new Promise(
|
|
17839
|
-
(
|
|
17958
|
+
(resolve13) => setTimeout(resolve13, Math.min(UNDO_LOCK_RETRY_DELAY_MS, remainingMs))
|
|
17840
17959
|
);
|
|
17841
17960
|
}
|
|
17842
17961
|
}
|
|
@@ -17844,7 +17963,7 @@ async function acquireUndoLogLock(path9) {
|
|
|
17844
17963
|
}
|
|
17845
17964
|
async function writeUndoRecords(home, records, globalConfigDirectory = defaultCliDependencies.configStore.GLOBAL_CONFIG_DIRECTORY) {
|
|
17846
17965
|
const path9 = undoLogPath(home, globalConfigDirectory);
|
|
17847
|
-
await mkdir6(
|
|
17966
|
+
await mkdir6(dirname9(path9), { recursive: true });
|
|
17848
17967
|
const temporaryPath = `${path9}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
17849
17968
|
try {
|
|
17850
17969
|
await writeFile7(temporaryPath, `${JSON.stringify(records, null, 2)}
|
|
@@ -17936,7 +18055,28 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17936
18055
|
options.cwd,
|
|
17937
18056
|
sourceDependencies
|
|
17938
18057
|
);
|
|
17939
|
-
const
|
|
18058
|
+
const pathPatterns = await canonicalizePathPatterns(
|
|
18059
|
+
options.cwd,
|
|
18060
|
+
options.paths ?? []
|
|
18061
|
+
);
|
|
18062
|
+
const matchesPathFilter = createWorktreePathFilter(pathPatterns);
|
|
18063
|
+
const searchAllRepositories = pathPatterns.length > 0;
|
|
18064
|
+
let allSourceCount = 0;
|
|
18065
|
+
const emitNoCandidates = (sourceCount) => {
|
|
18066
|
+
if (pathPatterns.length > 0 && sourceCount === 0) {
|
|
18067
|
+
emitError(options, "No linked worktrees match --path");
|
|
18068
|
+
return 1;
|
|
18069
|
+
}
|
|
18070
|
+
if (options.stale) {
|
|
18071
|
+
emitNoStaleCandidates(options);
|
|
18072
|
+
return 0;
|
|
18073
|
+
}
|
|
18074
|
+
emitError(options, "No linked worktrees to clean");
|
|
18075
|
+
return 1;
|
|
18076
|
+
};
|
|
18077
|
+
const currentSources = linkedWorktrees.filter(
|
|
18078
|
+
(worktree) => worktree.path !== repository.currentRoot && matchesPathFilter(worktree.path)
|
|
18079
|
+
).map((worktree) => ({
|
|
17940
18080
|
repoName: repository.repoName,
|
|
17941
18081
|
repoRoot: repository.repoRoot,
|
|
17942
18082
|
worktree
|
|
@@ -17977,8 +18117,9 @@ function createCleanCommand(dependencies = {}) {
|
|
|
17977
18117
|
...currentSources,
|
|
17978
18118
|
...registered.sources
|
|
17979
18119
|
]).filter(
|
|
17980
|
-
(source) => source.repoRoot !== void 0 && source.worktree.path !== source.repoRoot && source.worktree.path !== repository.currentRoot
|
|
18120
|
+
(source) => source.repoRoot !== void 0 && source.worktree.path !== source.repoRoot && source.worktree.path !== repository.currentRoot && matchesPathFilter(source.worktree.path)
|
|
17981
18121
|
);
|
|
18122
|
+
allSourceCount = sources.length;
|
|
17982
18123
|
const otherRepositorySources = sources.filter(
|
|
17983
18124
|
(source) => source.repoRoot !== repository.repoRoot
|
|
17984
18125
|
);
|
|
@@ -18012,28 +18153,17 @@ function createCleanCommand(dependencies = {}) {
|
|
|
18012
18153
|
};
|
|
18013
18154
|
}
|
|
18014
18155
|
};
|
|
18015
|
-
if (currentCandidates.length === 0
|
|
18156
|
+
if (searchAllRepositories || interactiveSelection && currentCandidates.length === 0) {
|
|
18016
18157
|
const loadedAllCandidates = await loadAllCandidates();
|
|
18017
18158
|
if (loadedAllCandidates.length === 0) {
|
|
18018
|
-
|
|
18019
|
-
emitNoStaleCandidates(options);
|
|
18020
|
-
return 0;
|
|
18021
|
-
}
|
|
18022
|
-
emitError(options, "No linked worktrees to clean");
|
|
18023
|
-
return 1;
|
|
18159
|
+
return emitNoCandidates(allSourceCount);
|
|
18024
18160
|
}
|
|
18025
18161
|
currentScope = false;
|
|
18026
18162
|
activeCandidates = loadedAllCandidates;
|
|
18027
18163
|
scope.label = "all repositories";
|
|
18028
18164
|
scope.toggleLabel = "current repository";
|
|
18029
|
-
}
|
|
18030
|
-
|
|
18031
|
-
if (options.stale) {
|
|
18032
|
-
emitNoStaleCandidates(options);
|
|
18033
|
-
return 0;
|
|
18034
|
-
}
|
|
18035
|
-
emitError(options, "No linked worktrees to clean");
|
|
18036
|
-
return 1;
|
|
18165
|
+
} else if (currentCandidates.length === 0) {
|
|
18166
|
+
return emitNoCandidates(currentSources.length);
|
|
18037
18167
|
}
|
|
18038
18168
|
if (!options.dryRun && !options.force && (options.json || isHeadless())) {
|
|
18039
18169
|
const message = "--force is required";
|
|
@@ -18260,6 +18390,31 @@ function createCleanCommand(dependencies = {}) {
|
|
|
18260
18390
|
};
|
|
18261
18391
|
}
|
|
18262
18392
|
var runCleanCommand = createCleanCommand();
|
|
18393
|
+
async function canonicalizePathPatterns(cwd, patterns) {
|
|
18394
|
+
return Promise.all(
|
|
18395
|
+
patterns.map(
|
|
18396
|
+
(pattern) => canonicalizeLiteralPrefix(resolve11(cwd, expandTildeInPath(pattern)))
|
|
18397
|
+
)
|
|
18398
|
+
);
|
|
18399
|
+
}
|
|
18400
|
+
async function canonicalizeLiteralPrefix(pattern) {
|
|
18401
|
+
const globIndex = findGlobIndex(pattern);
|
|
18402
|
+
let prefix = globIndex === -1 ? pattern : dirname10(`${pattern.slice(0, globIndex)}x`);
|
|
18403
|
+
let suffix = pattern.slice(prefix.length);
|
|
18404
|
+
for (; ; ) {
|
|
18405
|
+
try {
|
|
18406
|
+
const canonicalPrefix = escapePathPattern(
|
|
18407
|
+
await realpath5(unescapePathPattern(prefix))
|
|
18408
|
+
);
|
|
18409
|
+
return `${canonicalPrefix}${suffix}`.replace(/^\/\//, "/");
|
|
18410
|
+
} catch {
|
|
18411
|
+
const parent = dirname10(prefix);
|
|
18412
|
+
if (parent === prefix) return pattern;
|
|
18413
|
+
suffix = join12("/", basename8(prefix), suffix);
|
|
18414
|
+
prefix = parent;
|
|
18415
|
+
}
|
|
18416
|
+
}
|
|
18417
|
+
}
|
|
18263
18418
|
async function resolveCleanupCandidates(options, dependencies) {
|
|
18264
18419
|
const { sources, stale, stderr, signal } = options;
|
|
18265
18420
|
const grouped = /* @__PURE__ */ new Map();
|
|
@@ -18761,7 +18916,11 @@ _gji_completion() {
|
|
|
18761
18916
|
COMPREPLY=( $(compgen -W "--compact --json --help" -- "$cur") )
|
|
18762
18917
|
;;
|
|
18763
18918
|
clean)
|
|
18764
|
-
|
|
18919
|
+
if [ "\${COMP_WORDS[COMP_CWORD-1]}" = "--path" ]; then
|
|
18920
|
+
COMPREPLY=( $(compgen -d -- "$cur") )
|
|
18921
|
+
else
|
|
18922
|
+
COMPREPLY=( $(compgen -W "-f --force --stale --path --dry-run --json --help" -- "$cur") )
|
|
18923
|
+
fi
|
|
18765
18924
|
;;
|
|
18766
18925
|
remove|rm)
|
|
18767
18926
|
COMPREPLY=( $(compgen -W "$(__gji_worktree_branches) -f --force --dry-run --json --help" -- "$cur") )
|
|
@@ -19004,6 +19163,7 @@ complete -c gji -n '__fish_seen_subcommand_from ls' -l json -d 'print active wor
|
|
|
19004
19163
|
|
|
19005
19164
|
complete -c gji -n '__fish_seen_subcommand_from clean' -s f -l force -d 'bypass prompts, force-remove dirty worktrees, and force-delete unmerged branches'
|
|
19006
19165
|
complete -c gji -n '__fish_seen_subcommand_from clean' -l stale -d 'only target clean worktrees whose upstream is gone and branch is merged into the default branch'
|
|
19166
|
+
complete -c gji -n '__fish_seen_subcommand_from clean' -l path -r -F -d 'only target worktrees under a directory or matching a path glob (repeatable)'
|
|
19007
19167
|
complete -c gji -n '__fish_seen_subcommand_from clean' -l dry-run -d 'show what would be deleted without removing anything'
|
|
19008
19168
|
complete -c gji -n '__fish_seen_subcommand_from clean' -l json -d 'emit JSON on success or error instead of human-readable output'
|
|
19009
19169
|
|
|
@@ -19164,7 +19324,7 @@ case "\${words[2]}" in
|
|
|
19164
19324
|
_arguments '--compact[show only branch and path columns]' '--json[print active worktrees as JSON]'
|
|
19165
19325
|
;;
|
|
19166
19326
|
clean)
|
|
19167
|
-
_arguments '(-f --force)'{-f,--force}'[bypass prompts, force-remove dirty worktrees, and force-delete unmerged branches]' '--stale[only target clean worktrees whose upstream is gone and branch is merged into the default branch]' '--dry-run[show what would be deleted without removing anything]' '--json[emit JSON on success or error instead of human-readable output]'
|
|
19327
|
+
_arguments '(-f --force)'{-f,--force}'[bypass prompts, force-remove dirty worktrees, and force-delete unmerged branches]' '--stale[only target clean worktrees whose upstream is gone and branch is merged into the default branch]' '*--path[only target worktrees under a directory or matching a path glob]:pattern:_files' '--dry-run[show what would be deleted without removing anything]' '--json[emit JSON on success or error instead of human-readable output]'
|
|
19168
19328
|
;;
|
|
19169
19329
|
remove|rm)
|
|
19170
19330
|
_arguments '(-f --force)'{-f,--force}'[bypass prompts, force-remove a dirty worktree, and force-delete an unmerged branch]' '--dry-run[show what would be deleted without removing anything]' '--json[emit JSON on success or error instead of human-readable output]' '2:branch:->worktrees'
|
|
@@ -19301,20 +19461,20 @@ function writeJson(stdout3, value) {
|
|
|
19301
19461
|
import { execFile as execFile8 } from "node:child_process";
|
|
19302
19462
|
import { constants as constants6 } from "node:fs";
|
|
19303
19463
|
import { access as access6, readFile as readFile8 } from "node:fs/promises";
|
|
19304
|
-
import { homedir as
|
|
19305
|
-
import { basename as
|
|
19464
|
+
import { homedir as homedir7 } from "node:os";
|
|
19465
|
+
import { basename as basename9, dirname as dirname11, join as join14 } from "node:path";
|
|
19306
19466
|
import { promisify as promisify9 } from "node:util";
|
|
19307
19467
|
|
|
19308
19468
|
// src/presentation/shell/setup.ts
|
|
19309
19469
|
import { constants as constants5 } from "node:fs";
|
|
19310
19470
|
import { access as access5 } from "node:fs/promises";
|
|
19311
|
-
import { delimiter, join as
|
|
19471
|
+
import { delimiter, join as join13 } from "node:path";
|
|
19312
19472
|
var SHELL_INTEGRATION_START_MARKER = "# >>> gji init >>>";
|
|
19313
19473
|
var SHELL_INTEGRATION_END_MARKER = "# <<< gji init <<<";
|
|
19314
19474
|
var ONBOARDING_SHELL_INTEGRATION_START_MARKER = "# >>> gji shell integration >>>";
|
|
19315
19475
|
var ONBOARDING_SHELL_INTEGRATION_END_MARKER = "# <<< gji shell integration <<<";
|
|
19316
19476
|
async function executableExists(command) {
|
|
19317
|
-
const candidates = command.includes("/") ? [command] : (process.env.PATH ?? "").split(delimiter).filter(Boolean).map((directory) =>
|
|
19477
|
+
const candidates = command.includes("/") ? [command] : (process.env.PATH ?? "").split(delimiter).filter(Boolean).map((directory) => join13(directory, command));
|
|
19318
19478
|
for (const path9 of candidates) {
|
|
19319
19479
|
try {
|
|
19320
19480
|
await access5(path9, constants5.X_OK);
|
|
@@ -19327,17 +19487,17 @@ async function executableExists(command) {
|
|
|
19327
19487
|
function resolveShellConfigPath(shell, home) {
|
|
19328
19488
|
switch (shell) {
|
|
19329
19489
|
case "bash":
|
|
19330
|
-
return
|
|
19490
|
+
return join13(home, ".bashrc");
|
|
19331
19491
|
case "fish":
|
|
19332
|
-
return
|
|
19492
|
+
return join13(home, ".config", "fish", "config.fish");
|
|
19333
19493
|
case "zsh":
|
|
19334
|
-
return
|
|
19494
|
+
return join13(home, ".zshrc");
|
|
19335
19495
|
}
|
|
19336
19496
|
}
|
|
19337
19497
|
function resolveCompletionPath(shell, home) {
|
|
19338
19498
|
switch (shell) {
|
|
19339
19499
|
case "bash":
|
|
19340
|
-
return
|
|
19500
|
+
return join13(
|
|
19341
19501
|
home,
|
|
19342
19502
|
".local",
|
|
19343
19503
|
"share",
|
|
@@ -19346,9 +19506,9 @@ function resolveCompletionPath(shell, home) {
|
|
|
19346
19506
|
"gji"
|
|
19347
19507
|
);
|
|
19348
19508
|
case "fish":
|
|
19349
|
-
return
|
|
19509
|
+
return join13(home, ".config", "fish", "completions", "gji.fish");
|
|
19350
19510
|
case "zsh":
|
|
19351
|
-
return
|
|
19511
|
+
return join13(home, ".zsh", "completions", "_gji");
|
|
19352
19512
|
}
|
|
19353
19513
|
}
|
|
19354
19514
|
function hasShellIntegration(config, shell) {
|
|
@@ -19380,7 +19540,7 @@ async function runDoctorCommand(options) {
|
|
|
19380
19540
|
}
|
|
19381
19541
|
return 1;
|
|
19382
19542
|
}
|
|
19383
|
-
const home = options.home ??
|
|
19543
|
+
const home = options.home ?? homedir7();
|
|
19384
19544
|
const shell = resolveSupportedShell(
|
|
19385
19545
|
void 0,
|
|
19386
19546
|
options.shell ?? process.env.SHELL
|
|
@@ -19445,7 +19605,7 @@ async function collectDoctorInspection(cwd, home, shell, runtime) {
|
|
|
19445
19605
|
KNOWN_GLOBAL_CONFIG_KEYS2
|
|
19446
19606
|
);
|
|
19447
19607
|
const localConfig = repository ? await inspectConfig(
|
|
19448
|
-
|
|
19608
|
+
join14(repository.repoRoot, CONFIG_FILE_NAME2),
|
|
19449
19609
|
"local",
|
|
19450
19610
|
KNOWN_CONFIG_KEYS2
|
|
19451
19611
|
) : null;
|
|
@@ -19624,7 +19784,7 @@ function resolvePerRepoConfig(repos, repoRoot, home) {
|
|
|
19624
19784
|
}
|
|
19625
19785
|
function expandTilde2(path9, home) {
|
|
19626
19786
|
if (path9 === "~") return home;
|
|
19627
|
-
if (path9.startsWith("~/")) return
|
|
19787
|
+
if (path9.startsWith("~/")) return join14(home, path9.slice(2));
|
|
19628
19788
|
return path9;
|
|
19629
19789
|
}
|
|
19630
19790
|
async function checkGitVersion() {
|
|
@@ -19751,7 +19911,7 @@ function resolveWorktreeBase(repoRoot, config, home) {
|
|
|
19751
19911
|
if (typeof configuredPath === "string" && (configuredPath.startsWith("/") || configuredPath.startsWith("~"))) {
|
|
19752
19912
|
return expandTilde2(configuredPath, home);
|
|
19753
19913
|
}
|
|
19754
|
-
return
|
|
19914
|
+
return join14(dirname11(repoRoot), "worktrees", basename9(repoRoot));
|
|
19755
19915
|
}
|
|
19756
19916
|
async function findNearestExistingPath(path9) {
|
|
19757
19917
|
let candidate = path9;
|
|
@@ -19760,7 +19920,7 @@ async function findNearestExistingPath(path9) {
|
|
|
19760
19920
|
await access6(candidate, constants6.F_OK);
|
|
19761
19921
|
return candidate;
|
|
19762
19922
|
} catch {
|
|
19763
|
-
const parent =
|
|
19923
|
+
const parent = dirname11(candidate);
|
|
19764
19924
|
if (parent === candidate) return null;
|
|
19765
19925
|
candidate = parent;
|
|
19766
19926
|
}
|
|
@@ -19911,7 +20071,7 @@ function statusSymbol(status) {
|
|
|
19911
20071
|
}
|
|
19912
20072
|
|
|
19913
20073
|
// src/cli/commands/done.ts
|
|
19914
|
-
import { basename as
|
|
20074
|
+
import { basename as basename10 } from "node:path";
|
|
19915
20075
|
var DONE_OUTPUT_FILE_ENV = "GJI_DONE_OUTPUT_FILE";
|
|
19916
20076
|
async function runDoneCommand(options) {
|
|
19917
20077
|
const runtime = options.runtime ?? defaultCliDependencies;
|
|
@@ -20019,7 +20179,7 @@ async function runDoneCommand(options) {
|
|
|
20019
20179
|
{
|
|
20020
20180
|
branch: target.branch ?? void 0,
|
|
20021
20181
|
path: target.path,
|
|
20022
|
-
repo:
|
|
20182
|
+
repo: basename10(repository.repoRoot)
|
|
20023
20183
|
},
|
|
20024
20184
|
options.stderr
|
|
20025
20185
|
);
|
|
@@ -20210,7 +20370,7 @@ function toMessage3(error) {
|
|
|
20210
20370
|
}
|
|
20211
20371
|
|
|
20212
20372
|
// src/cli/commands/go.ts
|
|
20213
|
-
import { basename as
|
|
20373
|
+
import { basename as basename11 } from "node:path";
|
|
20214
20374
|
|
|
20215
20375
|
// src/application/worktree/context-card.ts
|
|
20216
20376
|
async function loadContextCardModel(worktreePath, dependencies) {
|
|
@@ -20541,64 +20701,7 @@ function formatLastCommit2(info) {
|
|
|
20541
20701
|
|
|
20542
20702
|
// src/cli/commands/new.ts
|
|
20543
20703
|
import { access as access8, mkdir as mkdir7 } from "node:fs/promises";
|
|
20544
|
-
import { dirname as
|
|
20545
|
-
|
|
20546
|
-
// src/domain/worktree/policy.ts
|
|
20547
|
-
import { homedir as homedir7 } from "node:os";
|
|
20548
|
-
import { basename as basename9, dirname as dirname10, join as join13 } from "node:path";
|
|
20549
|
-
function resolveWorktreePath(repoRoot, branch, basePath) {
|
|
20550
|
-
const segments = branch.split("/").filter(Boolean);
|
|
20551
|
-
if (segments.length === 0) {
|
|
20552
|
-
throw new Error("Branch name must not be empty.");
|
|
20553
|
-
}
|
|
20554
|
-
if (segments.some((segment) => segment === "." || segment === "..")) {
|
|
20555
|
-
throw new Error(
|
|
20556
|
-
`Branch name '${branch}' contains an invalid path segment.`
|
|
20557
|
-
);
|
|
20558
|
-
}
|
|
20559
|
-
const base = basePath ? expandTildeInPath(basePath) : join13(dirname10(repoRoot), "worktrees", basename9(repoRoot));
|
|
20560
|
-
return join13(base, ...segments);
|
|
20561
|
-
}
|
|
20562
|
-
function validateBranchName(name) {
|
|
20563
|
-
if (name.length === 0) {
|
|
20564
|
-
return "Branch name must not be empty.";
|
|
20565
|
-
}
|
|
20566
|
-
if (/[\x00-\x1f\x7f ~^:?*[\\\s]/.test(name)) {
|
|
20567
|
-
return `Branch name '${name}' contains an invalid character.`;
|
|
20568
|
-
}
|
|
20569
|
-
if (name.startsWith("-")) {
|
|
20570
|
-
return `Branch name '${name}' must not start with a dash.`;
|
|
20571
|
-
}
|
|
20572
|
-
if (name.startsWith("/") || name.endsWith("/") || name.includes("//")) {
|
|
20573
|
-
return `Branch name '${name}' has invalid slash placement.`;
|
|
20574
|
-
}
|
|
20575
|
-
if (name.includes("..")) {
|
|
20576
|
-
return `Branch name '${name}' must not contain '..'.`;
|
|
20577
|
-
}
|
|
20578
|
-
if (name.endsWith(".")) {
|
|
20579
|
-
return `Branch name '${name}' must not end with '.'.`;
|
|
20580
|
-
}
|
|
20581
|
-
if (name.includes("@{")) {
|
|
20582
|
-
return `Branch name '${name}' must not contain '@{'.`;
|
|
20583
|
-
}
|
|
20584
|
-
if (name === "@") {
|
|
20585
|
-
return "Branch name cannot be '@'.";
|
|
20586
|
-
}
|
|
20587
|
-
for (const segment of name.split("/")) {
|
|
20588
|
-
if (segment.startsWith(".")) {
|
|
20589
|
-
return `Branch name '${name}' contains a path component starting with '.'.`;
|
|
20590
|
-
}
|
|
20591
|
-
if (segment.endsWith(".lock")) {
|
|
20592
|
-
return `Branch name '${name}' contains a path component ending with '.lock'.`;
|
|
20593
|
-
}
|
|
20594
|
-
}
|
|
20595
|
-
return null;
|
|
20596
|
-
}
|
|
20597
|
-
function expandTildeInPath(path9) {
|
|
20598
|
-
if (path9 === "~") return homedir7();
|
|
20599
|
-
if (path9.startsWith("~/")) return join13(homedir7(), path9.slice(2));
|
|
20600
|
-
return path9;
|
|
20601
|
-
}
|
|
20704
|
+
import { dirname as dirname12, resolve as resolve12 } from "node:path";
|
|
20602
20705
|
|
|
20603
20706
|
// src/presentation/bootstrap/output.ts
|
|
20604
20707
|
function createBootstrapReporter(write, json) {
|
|
@@ -20908,7 +21011,7 @@ ${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
|
20908
21011
|
}
|
|
20909
21012
|
return 0;
|
|
20910
21013
|
}
|
|
20911
|
-
await mkdir7(
|
|
21014
|
+
await mkdir7(dirname12(worktreePath), { recursive: true });
|
|
20912
21015
|
const startPoint = options.fromCurrent && !options.detached ? await resolveCurrentWorktreeHead(options.cwd, runtime.git.runGit) : void 0;
|
|
20913
21016
|
const takeStartPoint = options.take ? await resolveCurrentWorktreeHead(options.cwd, runtime.git.runGit) : void 0;
|
|
20914
21017
|
const changedTakeFiles = options.take ? await listTakeFiles(options.cwd, runtime.git.runGitRaw) : [];
|
|
@@ -21500,7 +21603,7 @@ async function detectInProgressGitState(cwd, runGit2) {
|
|
|
21500
21603
|
for (const [marker, name] of markers) {
|
|
21501
21604
|
try {
|
|
21502
21605
|
const gitPath = await runGit2(cwd, ["rev-parse", "--git-path", marker]);
|
|
21503
|
-
await access8(
|
|
21606
|
+
await access8(resolve12(cwd, gitPath));
|
|
21504
21607
|
return name;
|
|
21505
21608
|
} catch {
|
|
21506
21609
|
}
|
|
@@ -21652,7 +21755,7 @@ function emitNewError(options, message, details) {
|
|
|
21652
21755
|
|
|
21653
21756
|
// src/cli/commands/pr.ts
|
|
21654
21757
|
import { mkdir as mkdir8 } from "node:fs/promises";
|
|
21655
|
-
import { dirname as
|
|
21758
|
+
import { dirname as dirname13 } from "node:path";
|
|
21656
21759
|
var PR_OUTPUT_FILE_ENV = "GJI_PR_OUTPUT_FILE";
|
|
21657
21760
|
function createPrCommand(dependencies = {}) {
|
|
21658
21761
|
const prompt = dependencies.promptForPathConflict ?? promptForPathConflict;
|
|
@@ -21791,7 +21894,7 @@ ${formatDependencyBootstrapPreview(dryRunDependencyBootstrap)}`
|
|
|
21791
21894
|
}
|
|
21792
21895
|
return 1;
|
|
21793
21896
|
}
|
|
21794
|
-
await mkdir8(
|
|
21897
|
+
await mkdir8(dirname13(worktreePath), { recursive: true });
|
|
21795
21898
|
const branchAlreadyExists = await localBranchExists2(
|
|
21796
21899
|
repository.repoRoot,
|
|
21797
21900
|
branchName,
|
|
@@ -22200,7 +22303,7 @@ async function navigateToExistingWorktree(options, path9, worktree, runtime) {
|
|
|
22200
22303
|
{
|
|
22201
22304
|
branch: worktree?.branch ?? void 0,
|
|
22202
22305
|
path: path9,
|
|
22203
|
-
repo:
|
|
22306
|
+
repo: basename11(repository.repoRoot),
|
|
22204
22307
|
slot: await runtime.slots.getWorktreeSlot(path9)
|
|
22205
22308
|
},
|
|
22206
22309
|
options.stderr
|
|
@@ -22340,7 +22443,7 @@ async function runHistoryCommand(options) {
|
|
|
22340
22443
|
}
|
|
22341
22444
|
|
|
22342
22445
|
// src/cli/commands/hub.ts
|
|
22343
|
-
import { basename as
|
|
22446
|
+
import { basename as basename12 } from "node:path";
|
|
22344
22447
|
import { stdin as stdin2, stdout as stdout2 } from "node:process";
|
|
22345
22448
|
var MAX_HUB_REPOSITORY_CONCURRENCY = 4;
|
|
22346
22449
|
async function buildHubData(cwd, dependencies = {}, runtime = defaultCliDependencies) {
|
|
@@ -22365,7 +22468,7 @@ async function buildHubData(cwd, dependencies = {}, runtime = defaultCliDependen
|
|
|
22365
22468
|
existing.sources.push(source);
|
|
22366
22469
|
} else {
|
|
22367
22470
|
groups.set(repoRoot, {
|
|
22368
|
-
name: source.repoName ||
|
|
22471
|
+
name: source.repoName || basename12(repoRoot),
|
|
22369
22472
|
sources: [source]
|
|
22370
22473
|
});
|
|
22371
22474
|
}
|
|
@@ -22616,7 +22719,7 @@ function formatHumanText(value, maxLength) {
|
|
|
22616
22719
|
import { constants as constants8 } from "node:fs";
|
|
22617
22720
|
import { access as access9, mkdir as mkdir9, readFile as readFile9, writeFile as writeFile8 } from "node:fs/promises";
|
|
22618
22721
|
import { homedir as homedir8 } from "node:os";
|
|
22619
|
-
import { dirname as
|
|
22722
|
+
import { dirname as dirname14 } from "node:path";
|
|
22620
22723
|
var ZSH_COMPLETION_PATH_LINE = "fpath=(~/.zsh/completions $fpath)";
|
|
22621
22724
|
var SHELL_WRAPPED_COMMANDS = [
|
|
22622
22725
|
{
|
|
@@ -22741,7 +22844,7 @@ async function runDefaultOnboarding(context, runtime) {
|
|
|
22741
22844
|
const installCompletion = await promptForCompletion(shell, completionPath);
|
|
22742
22845
|
if (installCompletion === null) return abortDefaultOnboarding();
|
|
22743
22846
|
if (installCompletion) {
|
|
22744
|
-
await mkdir9(
|
|
22847
|
+
await mkdir9(dirname14(completionPath), { recursive: true });
|
|
22745
22848
|
await writeFile8(completionPath, renderShellCompletion(shell), "utf8");
|
|
22746
22849
|
}
|
|
22747
22850
|
if (shell === "zsh" && (completionAlreadyInstalled || installCompletion)) {
|
|
@@ -22847,7 +22950,7 @@ async function applyOnboardingResult(result, home, runtime) {
|
|
|
22847
22950
|
runtime
|
|
22848
22951
|
);
|
|
22849
22952
|
if (result.installCompletion) {
|
|
22850
|
-
await mkdir9(
|
|
22953
|
+
await mkdir9(dirname14(completionPath), { recursive: true });
|
|
22851
22954
|
await writeFile8(
|
|
22852
22955
|
completionPath,
|
|
22853
22956
|
renderShellCompletion(result.shell),
|
|
@@ -22867,7 +22970,7 @@ async function applyOnboardingResult(result, home, runtime) {
|
|
|
22867
22970
|
}
|
|
22868
22971
|
async function installOnboardingShellIntegration(shell, home) {
|
|
22869
22972
|
const rcPath = resolveShellConfigPath(shell, home);
|
|
22870
|
-
await mkdir9(
|
|
22973
|
+
await mkdir9(dirname14(rcPath), { recursive: true });
|
|
22871
22974
|
const current = await readExistingConfig(rcPath);
|
|
22872
22975
|
if (shell === "fish") {
|
|
22873
22976
|
await writeFile8(
|
|
@@ -22955,7 +23058,7 @@ async function runLegacyInitCommand(options, runtime) {
|
|
|
22955
23058
|
return 0;
|
|
22956
23059
|
}
|
|
22957
23060
|
const rcPath = resolveShellConfigPath(shell, home);
|
|
22958
|
-
await mkdir9(
|
|
23061
|
+
await mkdir9(dirname14(rcPath), { recursive: true });
|
|
22959
23062
|
const current = await readExistingConfig(rcPath);
|
|
22960
23063
|
const next = upsertShellIntegration(current, script);
|
|
22961
23064
|
await writeFile8(rcPath, next, "utf8");
|
|
@@ -23313,7 +23416,7 @@ function sortWorktrees(worktrees) {
|
|
|
23313
23416
|
// src/cli/commands/open.ts
|
|
23314
23417
|
import { execFile as execFile9 } from "node:child_process";
|
|
23315
23418
|
import { access as access10, writeFile as writeFile9 } from "node:fs/promises";
|
|
23316
|
-
import { join as
|
|
23419
|
+
import { join as join15 } from "node:path";
|
|
23317
23420
|
import { promisify as promisify10 } from "node:util";
|
|
23318
23421
|
var execFileAsync9 = promisify10(execFile9);
|
|
23319
23422
|
function createOpenCommand(dependencies = {}) {
|
|
@@ -23492,7 +23595,7 @@ async function defaultPromptForEditor(editors) {
|
|
|
23492
23595
|
return choice;
|
|
23493
23596
|
}
|
|
23494
23597
|
async function ensureWorkspaceFile(worktreePath, repoName) {
|
|
23495
|
-
const workspacePath =
|
|
23598
|
+
const workspacePath = join15(worktreePath, `${repoName}.code-workspace`);
|
|
23496
23599
|
try {
|
|
23497
23600
|
await access10(workspacePath);
|
|
23498
23601
|
return workspacePath;
|
|
@@ -23879,7 +23982,7 @@ function formatError(error) {
|
|
|
23879
23982
|
}
|
|
23880
23983
|
|
|
23881
23984
|
// src/cli/commands/remove.ts
|
|
23882
|
-
import { basename as
|
|
23985
|
+
import { basename as basename13 } from "node:path";
|
|
23883
23986
|
var REMOVE_OUTPUT_FILE_ENV = "GJI_REMOVE_OUTPUT_FILE";
|
|
23884
23987
|
function createRemoveCommand(dependencies = {}) {
|
|
23885
23988
|
const promptForWorktree2 = dependencies.promptForWorktree ?? defaultPromptForWorktree3;
|
|
@@ -24008,7 +24111,7 @@ function createRemoveCommand(dependencies = {}) {
|
|
|
24008
24111
|
{
|
|
24009
24112
|
branch: worktree.branch ?? void 0,
|
|
24010
24113
|
path: worktree.path,
|
|
24011
|
-
repo:
|
|
24114
|
+
repo: basename13(repository.repoRoot)
|
|
24012
24115
|
},
|
|
24013
24116
|
options.stderr
|
|
24014
24117
|
);
|
|
@@ -24447,7 +24550,7 @@ function resolveConfiguredString2(value) {
|
|
|
24447
24550
|
|
|
24448
24551
|
// src/cli/commands/sync-files-command.ts
|
|
24449
24552
|
import { homedir as homedir9 } from "node:os";
|
|
24450
|
-
import { join as
|
|
24553
|
+
import { join as join16 } from "node:path";
|
|
24451
24554
|
async function runSyncFilesCommand(options) {
|
|
24452
24555
|
const runtime = options.runtime ?? defaultCliDependencies;
|
|
24453
24556
|
const { validateSyncFilePattern: validateSyncFilePattern2 } = runtime.filesystem;
|
|
@@ -24601,7 +24704,7 @@ function writeError(options, message) {
|
|
|
24601
24704
|
}
|
|
24602
24705
|
function expandTilde3(value, home) {
|
|
24603
24706
|
if (value === "~") return home;
|
|
24604
|
-
if (value.startsWith("~/")) return
|
|
24707
|
+
if (value.startsWith("~/")) return join16(home, value.slice(2));
|
|
24605
24708
|
return value;
|
|
24606
24709
|
}
|
|
24607
24710
|
|
|
@@ -24969,6 +25072,10 @@ function registerCommands(program2) {
|
|
|
24969
25072
|
).option(
|
|
24970
25073
|
"--stale",
|
|
24971
25074
|
"only target clean worktrees whose upstream is gone and branch is merged into the default branch"
|
|
25075
|
+
).option(
|
|
25076
|
+
"--path <pattern>",
|
|
25077
|
+
"only target worktrees under a directory or matching a path glob (repeatable)",
|
|
25078
|
+
collectOption
|
|
24972
25079
|
).option("--dry-run", "show what would be deleted without removing anything").option(
|
|
24973
25080
|
"--json",
|
|
24974
25081
|
"emit JSON on success or error instead of human-readable output"
|
|
@@ -25336,6 +25443,7 @@ function attachCommandActions(program2, options) {
|
|
|
25336
25443
|
dryRun: commandOptions2.dryRun,
|
|
25337
25444
|
force: commandOptions2.force,
|
|
25338
25445
|
json: commandOptions2.json,
|
|
25446
|
+
paths: commandOptions2.path,
|
|
25339
25447
|
stale: commandOptions2.stale,
|
|
25340
25448
|
runtime: options.dependencies,
|
|
25341
25449
|
stderr: options.stderr,
|
|
@@ -25463,6 +25571,9 @@ function requireCommand(parent, name) {
|
|
|
25463
25571
|
}
|
|
25464
25572
|
return command;
|
|
25465
25573
|
}
|
|
25574
|
+
function collectOption(value, previous = []) {
|
|
25575
|
+
return [...previous, value];
|
|
25576
|
+
}
|
|
25466
25577
|
function notImplemented(commandName) {
|
|
25467
25578
|
return () => {
|
|
25468
25579
|
throw new Error(`'${commandName}' is not implemented yet.`);
|