@solaqua/gji 0.7.1 → 0.7.2
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/clean.d.ts +2 -1
- package/dist/clean.js +8 -16
- package/dist/gji-bundle.mjs +466 -189
- package/dist/go.d.ts +2 -4
- package/dist/go.js +18 -47
- package/dist/history.d.ts +1 -0
- package/dist/history.js +12 -5
- package/dist/new.js +70 -8
- package/dist/open.d.ts +2 -2
- package/dist/open.js +22 -18
- package/dist/pr.js +3 -3
- package/dist/remove.d.ts +3 -2
- package/dist/remove.js +7 -12
- package/dist/repo.d.ts +0 -1
- package/dist/repo.js +0 -9
- package/dist/warp.js +24 -53
- package/dist/worktree-info.d.ts +0 -1
- package/dist/worktree-info.js +17 -11
- package/dist/worktree-picker.d.ts +14 -0
- package/dist/worktree-picker.js +228 -0
- 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-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-warp.1 +1 -1
- package/man/man1/gji.1 +1 -1
- package/package.json +3 -7
package/dist/gji-bundle.mjs
CHANGED
|
@@ -13074,15 +13074,21 @@ async function loadHistory(home = homedir2()) {
|
|
|
13074
13074
|
async function appendHistory(path9, branch, home = homedir2()) {
|
|
13075
13075
|
const historyPath = HISTORY_FILE_PATH(home);
|
|
13076
13076
|
const existing = await loadHistory(home);
|
|
13077
|
-
if (existing.length > 0 && existing[0].path === path9) {
|
|
13078
|
-
return;
|
|
13079
|
-
}
|
|
13080
13077
|
const entry = { branch, path: path9, timestamp: Date.now() };
|
|
13081
|
-
const next = [
|
|
13078
|
+
const next = [
|
|
13079
|
+
entry,
|
|
13080
|
+
...existing.filter((existingEntry) => existingEntry.path !== path9)
|
|
13081
|
+
].slice(0, MAX_HISTORY_ENTRIES);
|
|
13082
13082
|
await mkdir2(dirname2(historyPath), { recursive: true });
|
|
13083
13083
|
await writeFile2(historyPath, `${JSON.stringify(next, null, 2)}
|
|
13084
13084
|
`, "utf8");
|
|
13085
13085
|
}
|
|
13086
|
+
async function recordWorktreeUsage(path9, branch, home = homedir2()) {
|
|
13087
|
+
try {
|
|
13088
|
+
await appendHistory(path9, branch, home);
|
|
13089
|
+
} catch {
|
|
13090
|
+
}
|
|
13091
|
+
}
|
|
13086
13092
|
function isHistoryEntry(value) {
|
|
13087
13093
|
return typeof value === "object" && value !== null && "path" in value && typeof value.path === "string" && "timestamp" in value && typeof value.timestamp === "number";
|
|
13088
13094
|
}
|
|
@@ -13381,13 +13387,6 @@ async function listWorktrees(cwd) {
|
|
|
13381
13387
|
};
|
|
13382
13388
|
});
|
|
13383
13389
|
}
|
|
13384
|
-
function sortByCurrentFirst(worktrees) {
|
|
13385
|
-
return [...worktrees].sort((a, b3) => {
|
|
13386
|
-
if (a.isCurrent && !b3.isCurrent) return -1;
|
|
13387
|
-
if (!a.isCurrent && b3.isCurrent) return 1;
|
|
13388
|
-
return 0;
|
|
13389
|
-
});
|
|
13390
|
-
}
|
|
13391
13390
|
function findPorcelainValue(block, key) {
|
|
13392
13391
|
const value = findOptionalPorcelainValue(block, key);
|
|
13393
13392
|
if (!value) {
|
|
@@ -13821,43 +13820,63 @@ var dD = class extends x {
|
|
|
13821
13820
|
});
|
|
13822
13821
|
}
|
|
13823
13822
|
};
|
|
13823
|
+
var mD = Object.defineProperty;
|
|
13824
|
+
var bD = (e2, u2, t) => u2 in e2 ? mD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
|
|
13825
|
+
var Z = (e2, u2, t) => (bD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
|
|
13826
|
+
var q = (e2, u2, t) => {
|
|
13827
|
+
if (!u2.has(e2)) throw TypeError("Cannot " + t);
|
|
13828
|
+
};
|
|
13829
|
+
var T = (e2, u2, t) => (q(e2, u2, "read from private field"), t ? t.call(e2) : u2.get(e2));
|
|
13830
|
+
var wD = (e2, u2, t) => {
|
|
13831
|
+
if (u2.has(e2)) throw TypeError("Cannot add the same private member more than once");
|
|
13832
|
+
u2 instanceof WeakSet ? u2.add(e2) : u2.set(e2, t);
|
|
13833
|
+
};
|
|
13834
|
+
var yD = (e2, u2, t, F2) => (q(e2, u2, "write to private field"), F2 ? F2.call(e2, t) : u2.set(e2, t), t);
|
|
13824
13835
|
var A;
|
|
13825
|
-
|
|
13826
|
-
var kD = Object.defineProperty;
|
|
13827
|
-
var $D = (e2, u2, t) => u2 in e2 ? kD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
|
|
13828
|
-
var H = (e2, u2, t) => ($D(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
|
|
13829
|
-
var SD = class extends x {
|
|
13836
|
+
var _D = class extends x {
|
|
13830
13837
|
constructor(u2) {
|
|
13831
|
-
super(u2, false),
|
|
13832
|
-
|
|
13833
|
-
}), this.on("cursor", (
|
|
13834
|
-
switch (
|
|
13838
|
+
super(u2, false), Z(this, "options"), Z(this, "cursor", 0), wD(this, A, void 0);
|
|
13839
|
+
const { options: t } = u2;
|
|
13840
|
+
yD(this, A, u2.selectableGroups !== false), this.options = Object.entries(t).flatMap(([F2, s]) => [{ value: F2, group: true, label: F2 }, ...s.map((i) => ({ ...i, group: F2 }))]), this.value = [...u2.initialValues ?? []], this.cursor = Math.max(this.options.findIndex(({ value: F2 }) => F2 === u2.cursorAt), T(this, A) ? 0 : 1), this.on("cursor", (F2) => {
|
|
13841
|
+
switch (F2) {
|
|
13835
13842
|
case "left":
|
|
13836
|
-
case "up":
|
|
13843
|
+
case "up": {
|
|
13837
13844
|
this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1;
|
|
13845
|
+
const s = this.options[this.cursor]?.group === true;
|
|
13846
|
+
!T(this, A) && s && (this.cursor = this.cursor === 0 ? this.options.length - 1 : this.cursor - 1);
|
|
13838
13847
|
break;
|
|
13848
|
+
}
|
|
13839
13849
|
case "down":
|
|
13840
|
-
case "right":
|
|
13850
|
+
case "right": {
|
|
13841
13851
|
this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1;
|
|
13852
|
+
const s = this.options[this.cursor]?.group === true;
|
|
13853
|
+
!T(this, A) && s && (this.cursor = this.cursor === this.options.length - 1 ? 0 : this.cursor + 1);
|
|
13842
13854
|
break;
|
|
13855
|
+
}
|
|
13843
13856
|
case "space":
|
|
13844
13857
|
this.toggleValue();
|
|
13845
13858
|
break;
|
|
13846
13859
|
}
|
|
13847
13860
|
});
|
|
13848
13861
|
}
|
|
13849
|
-
|
|
13850
|
-
return this.options
|
|
13862
|
+
getGroupItems(u2) {
|
|
13863
|
+
return this.options.filter((t) => t.group === u2);
|
|
13851
13864
|
}
|
|
13852
|
-
|
|
13853
|
-
|
|
13854
|
-
this.value = u2 ? [] : this.options.map((t) => t.value);
|
|
13865
|
+
isGroupSelected(u2) {
|
|
13866
|
+
return this.getGroupItems(u2).every((t) => this.value.includes(t.value));
|
|
13855
13867
|
}
|
|
13856
13868
|
toggleValue() {
|
|
13857
|
-
const u2 = this.
|
|
13858
|
-
|
|
13869
|
+
const u2 = this.options[this.cursor];
|
|
13870
|
+
if (u2.group === true) {
|
|
13871
|
+
const t = u2.value, F2 = this.getGroupItems(t);
|
|
13872
|
+
this.isGroupSelected(t) ? this.value = this.value.filter((s) => F2.findIndex((i) => i.value === s) === -1) : this.value = [...this.value, ...F2.map((s) => s.value)], this.value = Array.from(new Set(this.value));
|
|
13873
|
+
} else {
|
|
13874
|
+
const t = this.value.includes(u2.value);
|
|
13875
|
+
this.value = t ? this.value.filter((F2) => F2 !== u2.value) : [...this.value, u2.value];
|
|
13876
|
+
}
|
|
13859
13877
|
}
|
|
13860
13878
|
};
|
|
13879
|
+
A = /* @__PURE__ */ new WeakMap();
|
|
13861
13880
|
var OD = Object.defineProperty;
|
|
13862
13881
|
var PD = (e2, u2, t) => u2 in e2 ? OD(e2, u2, { enumerable: true, configurable: true, writable: true, value: t }) : e2[u2] = t;
|
|
13863
13882
|
var J = (e2, u2, t) => (PD(e2, typeof u2 != "symbol" ? u2 + "" : u2, t), t);
|
|
@@ -13920,14 +13939,14 @@ var d2 = u("\u2514", "\u2014");
|
|
|
13920
13939
|
var k2 = u("\u25CF", ">");
|
|
13921
13940
|
var P2 = u("\u25CB", " ");
|
|
13922
13941
|
var A2 = u("\u25FB", "[\u2022]");
|
|
13923
|
-
var
|
|
13942
|
+
var T2 = u("\u25FC", "[+]");
|
|
13924
13943
|
var F = u("\u25FB", "[ ]");
|
|
13925
13944
|
var $e = u("\u25AA", "\u2022");
|
|
13926
13945
|
var _2 = u("\u2500", "-");
|
|
13927
13946
|
var me = u("\u256E", "+");
|
|
13928
13947
|
var de = u("\u251C", "+");
|
|
13929
13948
|
var pe = u("\u256F", "+");
|
|
13930
|
-
var
|
|
13949
|
+
var q2 = u("\u25CF", "\u2022");
|
|
13931
13950
|
var D = u("\u25C6", "*");
|
|
13932
13951
|
var U = u("\u25B2", "!");
|
|
13933
13952
|
var K2 = u("\u25A0", "x");
|
|
@@ -14026,40 +14045,54 @@ ${import_picocolors2.default.cyan(d2)}
|
|
|
14026
14045
|
}
|
|
14027
14046
|
} }).prompt();
|
|
14028
14047
|
};
|
|
14029
|
-
var
|
|
14030
|
-
const n = (
|
|
14031
|
-
const
|
|
14032
|
-
|
|
14048
|
+
var be = (t) => {
|
|
14049
|
+
const { selectableGroups: n = true } = t, r2 = (i, s, c = []) => {
|
|
14050
|
+
const a = i.label ?? String(i.value), l2 = typeof i.group == "string", $2 = l2 && (c[c.indexOf(i) + 1] ?? { group: true }), g = l2 && $2.group === true, p2 = l2 ? n ? `${g ? d2 : o} ` : " " : "";
|
|
14051
|
+
if (s === "active") return `${import_picocolors2.default.dim(p2)}${import_picocolors2.default.cyan(A2)} ${a} ${i.hint ? import_picocolors2.default.dim(`(${i.hint})`) : ""}`;
|
|
14052
|
+
if (s === "group-active") return `${p2}${import_picocolors2.default.cyan(A2)} ${import_picocolors2.default.dim(a)}`;
|
|
14053
|
+
if (s === "group-active-selected") return `${p2}${import_picocolors2.default.green(T2)} ${import_picocolors2.default.dim(a)}`;
|
|
14054
|
+
if (s === "selected") {
|
|
14055
|
+
const f = l2 || n ? import_picocolors2.default.green(T2) : "";
|
|
14056
|
+
return `${import_picocolors2.default.dim(p2)}${f} ${import_picocolors2.default.dim(a)} ${i.hint ? import_picocolors2.default.dim(`(${i.hint})`) : ""}`;
|
|
14057
|
+
}
|
|
14058
|
+
if (s === "cancelled") return `${import_picocolors2.default.strikethrough(import_picocolors2.default.dim(a))}`;
|
|
14059
|
+
if (s === "active-selected") return `${import_picocolors2.default.dim(p2)}${import_picocolors2.default.green(T2)} ${a} ${i.hint ? import_picocolors2.default.dim(`(${i.hint})`) : ""}`;
|
|
14060
|
+
if (s === "submitted") return `${import_picocolors2.default.dim(a)}`;
|
|
14061
|
+
const v2 = l2 || n ? import_picocolors2.default.dim(F) : "";
|
|
14062
|
+
return `${import_picocolors2.default.dim(p2)}${v2} ${import_picocolors2.default.dim(a)}`;
|
|
14033
14063
|
};
|
|
14034
|
-
return new
|
|
14035
|
-
if (this.required &&
|
|
14064
|
+
return new _D({ options: t.options, initialValues: t.initialValues, required: t.required ?? true, cursorAt: t.cursorAt, selectableGroups: n, validate(i) {
|
|
14065
|
+
if (this.required && i.length === 0) return `Please select at least one option.
|
|
14036
14066
|
${import_picocolors2.default.reset(import_picocolors2.default.dim(`Press ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" space ")))} to select, ${import_picocolors2.default.gray(import_picocolors2.default.bgWhite(import_picocolors2.default.inverse(" enter ")))} to submit`))}`;
|
|
14037
14067
|
}, render() {
|
|
14038
|
-
const
|
|
14068
|
+
const i = `${import_picocolors2.default.gray(o)}
|
|
14039
14069
|
${b2(this.state)} ${t.message}
|
|
14040
|
-
|
|
14041
|
-
const a = this.value.includes(s.value);
|
|
14042
|
-
return c && a ? n(s, "active-selected") : a ? n(s, "selected") : n(s, c ? "active" : "inactive");
|
|
14043
|
-
};
|
|
14070
|
+
`;
|
|
14044
14071
|
switch (this.state) {
|
|
14045
14072
|
case "submit":
|
|
14046
|
-
return `${
|
|
14073
|
+
return `${i}${import_picocolors2.default.gray(o)} ${this.options.filter(({ value: s }) => this.value.includes(s)).map((s) => r2(s, "submitted")).join(import_picocolors2.default.dim(", "))}`;
|
|
14047
14074
|
case "cancel": {
|
|
14048
|
-
const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) =>
|
|
14049
|
-
return `${
|
|
14075
|
+
const s = this.options.filter(({ value: c }) => this.value.includes(c)).map((c) => r2(c, "cancelled")).join(import_picocolors2.default.dim(", "));
|
|
14076
|
+
return `${i}${import_picocolors2.default.gray(o)} ${s.trim() ? `${s}
|
|
14050
14077
|
${import_picocolors2.default.gray(o)}` : ""}`;
|
|
14051
14078
|
}
|
|
14052
14079
|
case "error": {
|
|
14053
14080
|
const s = this.error.split(`
|
|
14054
14081
|
`).map((c, a) => a === 0 ? `${import_picocolors2.default.yellow(d2)} ${import_picocolors2.default.yellow(c)}` : ` ${c}`).join(`
|
|
14055
14082
|
`);
|
|
14056
|
-
return `${
|
|
14083
|
+
return `${i}${import_picocolors2.default.yellow(o)} ${this.options.map((c, a, l2) => {
|
|
14084
|
+
const $2 = this.value.includes(c.value) || c.group === true && this.isGroupSelected(`${c.value}`), g = a === this.cursor;
|
|
14085
|
+
return !g && typeof c.group == "string" && this.options[this.cursor].value === c.group ? r2(c, $2 ? "group-active-selected" : "group-active", l2) : g && $2 ? r2(c, "active-selected", l2) : $2 ? r2(c, "selected", l2) : r2(c, g ? "active" : "inactive", l2);
|
|
14086
|
+
}).join(`
|
|
14057
14087
|
${import_picocolors2.default.yellow(o)} `)}
|
|
14058
14088
|
${s}
|
|
14059
14089
|
`;
|
|
14060
14090
|
}
|
|
14061
14091
|
default:
|
|
14062
|
-
return `${
|
|
14092
|
+
return `${i}${import_picocolors2.default.cyan(o)} ${this.options.map((s, c, a) => {
|
|
14093
|
+
const l2 = this.value.includes(s.value) || s.group === true && this.isGroupSelected(`${s.value}`), $2 = c === this.cursor;
|
|
14094
|
+
return !$2 && typeof s.group == "string" && this.options[this.cursor].value === s.group ? r2(s, l2 ? "group-active-selected" : "group-active", a) : $2 && l2 ? r2(s, "active-selected", a) : l2 ? r2(s, "selected", a) : r2(s, $2 ? "active" : "inactive", a);
|
|
14095
|
+
}).join(`
|
|
14063
14096
|
${import_picocolors2.default.cyan(o)} `)}
|
|
14064
14097
|
${import_picocolors2.default.cyan(d2)}
|
|
14065
14098
|
`;
|
|
@@ -14084,8 +14117,29 @@ function isHeadless() {
|
|
|
14084
14117
|
}
|
|
14085
14118
|
|
|
14086
14119
|
// src/worktree-info.ts
|
|
14120
|
+
var MAX_WORKTREE_INFO_READ_CONCURRENCY = 8;
|
|
14087
14121
|
async function readWorktreeInfos(worktrees) {
|
|
14088
|
-
return
|
|
14122
|
+
return mapWithConcurrency(
|
|
14123
|
+
worktrees,
|
|
14124
|
+
MAX_WORKTREE_INFO_READ_CONCURRENCY,
|
|
14125
|
+
readWorktreeInfo
|
|
14126
|
+
);
|
|
14127
|
+
}
|
|
14128
|
+
async function mapWithConcurrency(items, limit, mapper) {
|
|
14129
|
+
const results = new Array(items.length);
|
|
14130
|
+
let nextIndex = 0;
|
|
14131
|
+
async function readNext() {
|
|
14132
|
+
for (; ; ) {
|
|
14133
|
+
const index = nextIndex;
|
|
14134
|
+
nextIndex += 1;
|
|
14135
|
+
if (index >= items.length) return;
|
|
14136
|
+
results[index] = await mapper(items[index]);
|
|
14137
|
+
}
|
|
14138
|
+
}
|
|
14139
|
+
await Promise.all(
|
|
14140
|
+
Array.from({ length: Math.min(limit, items.length) }, () => readNext())
|
|
14141
|
+
);
|
|
14142
|
+
return results;
|
|
14089
14143
|
}
|
|
14090
14144
|
async function readWorktreeInfo(worktree) {
|
|
14091
14145
|
const [healthResult, lastCommitResult] = await Promise.allSettled([
|
|
@@ -14129,16 +14183,6 @@ function serializeWorktreeInfo(info) {
|
|
|
14129
14183
|
upstream: info.upstream
|
|
14130
14184
|
};
|
|
14131
14185
|
}
|
|
14132
|
-
function formatWorktreeHint(info) {
|
|
14133
|
-
const details = [
|
|
14134
|
-
`status: ${info.status}`,
|
|
14135
|
-
`upstream: ${formatUpstreamState(info.upstream)}`
|
|
14136
|
-
];
|
|
14137
|
-
if (info.lastCommitTimestamp !== null) {
|
|
14138
|
-
details.push(`last: ${formatRelativeAge(info.lastCommitTimestamp)}`);
|
|
14139
|
-
}
|
|
14140
|
-
return `${info.path} (${details.join(", ")})`;
|
|
14141
|
-
}
|
|
14142
14186
|
function formatUpstreamState(upstream) {
|
|
14143
14187
|
if (upstream.kind === "detached") {
|
|
14144
14188
|
return "n/a";
|
|
@@ -14239,6 +14283,225 @@ function hasStderr(error) {
|
|
|
14239
14283
|
return error instanceof Error && "stderr" in error && typeof error.stderr === "string";
|
|
14240
14284
|
}
|
|
14241
14285
|
|
|
14286
|
+
// src/worktree-picker.ts
|
|
14287
|
+
async function buildWorktreePromptEntries(sources) {
|
|
14288
|
+
const [history, infos] = await Promise.all([
|
|
14289
|
+
loadHistory(),
|
|
14290
|
+
readWorktreeInfos(sources.map((source) => source.worktree))
|
|
14291
|
+
]);
|
|
14292
|
+
const historyByPath = new Map(history.map((entry) => [entry.path, entry]));
|
|
14293
|
+
const entries = sources.map(
|
|
14294
|
+
(source, index) => buildWorktreePromptEntry(
|
|
14295
|
+
source,
|
|
14296
|
+
infos[index],
|
|
14297
|
+
historyByPath.get(source.worktree.path)?.timestamp ?? null,
|
|
14298
|
+
Date.now()
|
|
14299
|
+
)
|
|
14300
|
+
);
|
|
14301
|
+
return entries.sort(comparePromptEntries).map(
|
|
14302
|
+
({ lastActivityTimestamp: _lastActivityTimestamp, ...entry }) => entry
|
|
14303
|
+
);
|
|
14304
|
+
}
|
|
14305
|
+
function resolveWorktreeQuery(sources, query) {
|
|
14306
|
+
const normalizedQuery = normalizeQuery(query);
|
|
14307
|
+
if (normalizedQuery === null) return null;
|
|
14308
|
+
const matches = findWorktreePromptSourceMatches(sources, normalizedQuery);
|
|
14309
|
+
if (isAmbiguousRepoOnlyQuery(matches, normalizedQuery)) return null;
|
|
14310
|
+
return matches[0]?.source ?? null;
|
|
14311
|
+
}
|
|
14312
|
+
function findWorktreePromptSourceMatches(sources, normalizedQuery) {
|
|
14313
|
+
return sources.flatMap((source) => {
|
|
14314
|
+
const matchScore = scoreWorktreeMatch(
|
|
14315
|
+
{
|
|
14316
|
+
...source.worktree,
|
|
14317
|
+
repoName: source.repoName
|
|
14318
|
+
},
|
|
14319
|
+
normalizedQuery
|
|
14320
|
+
);
|
|
14321
|
+
return matchScore === null ? [] : [{ matchScore, source }];
|
|
14322
|
+
}).sort(compareQueryMatches);
|
|
14323
|
+
}
|
|
14324
|
+
function isAmbiguousRepoOnlyQuery(matches, query) {
|
|
14325
|
+
if (matches[0]?.matchScore === 1e3) return false;
|
|
14326
|
+
return matches.filter((match) => match.source.repoName.toLowerCase() === query).length > 1;
|
|
14327
|
+
}
|
|
14328
|
+
async function promptForSingleWorktree(message, worktrees) {
|
|
14329
|
+
const choice = await ve({
|
|
14330
|
+
message,
|
|
14331
|
+
options: worktrees.map((worktree) => ({
|
|
14332
|
+
label: worktree.label,
|
|
14333
|
+
value: worktree.path
|
|
14334
|
+
})),
|
|
14335
|
+
maxItems: 12
|
|
14336
|
+
});
|
|
14337
|
+
return pD(choice) ? null : choice;
|
|
14338
|
+
}
|
|
14339
|
+
async function promptForMultipleWorktrees(message, worktrees) {
|
|
14340
|
+
const choice = await be({
|
|
14341
|
+
message,
|
|
14342
|
+
options: groupPromptEntries(worktrees),
|
|
14343
|
+
required: true,
|
|
14344
|
+
selectableGroups: false
|
|
14345
|
+
});
|
|
14346
|
+
return pD(choice) ? null : choice;
|
|
14347
|
+
}
|
|
14348
|
+
function compareQueryMatches(a, b3) {
|
|
14349
|
+
if (a.matchScore !== b3.matchScore) {
|
|
14350
|
+
return b3.matchScore - a.matchScore;
|
|
14351
|
+
}
|
|
14352
|
+
if (a.source.worktree.isCurrent && !b3.source.worktree.isCurrent) return -1;
|
|
14353
|
+
if (!a.source.worktree.isCurrent && b3.source.worktree.isCurrent) return 1;
|
|
14354
|
+
return a.source.repoName.localeCompare(b3.source.repoName) || (a.source.worktree.branch ?? "").localeCompare(
|
|
14355
|
+
b3.source.worktree.branch ?? ""
|
|
14356
|
+
) || a.source.worktree.path.localeCompare(b3.source.worktree.path);
|
|
14357
|
+
}
|
|
14358
|
+
function groupPromptEntries(worktrees) {
|
|
14359
|
+
const groups = {};
|
|
14360
|
+
for (const worktree of worktrees) {
|
|
14361
|
+
const group = worktree.group === "recent" ? "Recent worktrees" : "Other worktrees";
|
|
14362
|
+
groups[group] ??= [];
|
|
14363
|
+
groups[group].push({
|
|
14364
|
+
label: worktree.label,
|
|
14365
|
+
value: worktree.path
|
|
14366
|
+
});
|
|
14367
|
+
}
|
|
14368
|
+
return groups;
|
|
14369
|
+
}
|
|
14370
|
+
function buildWorktreePromptEntry(source, info, lastUsedTimestamp, now) {
|
|
14371
|
+
const lastWorkedTimestamp = info.lastCommitTimestamp === null ? null : info.lastCommitTimestamp * 1e3;
|
|
14372
|
+
const lastActivityTimestamp = lastUsedTimestamp ?? lastWorkedTimestamp;
|
|
14373
|
+
const lastActivityType = lastUsedTimestamp !== null ? "used" : lastWorkedTimestamp !== null ? "worked" : null;
|
|
14374
|
+
const branch = source.worktree.branch ?? "(detached)";
|
|
14375
|
+
const badges = buildStatusBadges(info);
|
|
14376
|
+
const recency = formatPromptRecency(
|
|
14377
|
+
lastActivityTimestamp,
|
|
14378
|
+
lastActivityType,
|
|
14379
|
+
now
|
|
14380
|
+
);
|
|
14381
|
+
const status = badges.length > 0 ? badges.map((badge) => `[${badge}]`).join(" ") : null;
|
|
14382
|
+
const path9 = middleEllipsize(source.worktree.path, 76);
|
|
14383
|
+
const label = [
|
|
14384
|
+
middleEllipsize(source.repoName, 22),
|
|
14385
|
+
middleEllipsize(branch, 34),
|
|
14386
|
+
status,
|
|
14387
|
+
recency,
|
|
14388
|
+
path9
|
|
14389
|
+
].filter((part) => part !== null && part.length > 0).join(" \xB7 ");
|
|
14390
|
+
return {
|
|
14391
|
+
...source.worktree,
|
|
14392
|
+
group: lastUsedTimestamp !== null ? "recent" : "other",
|
|
14393
|
+
label,
|
|
14394
|
+
lastActivityTimestamp,
|
|
14395
|
+
repoName: source.repoName
|
|
14396
|
+
};
|
|
14397
|
+
}
|
|
14398
|
+
function buildStatusBadges(info) {
|
|
14399
|
+
const badges = [];
|
|
14400
|
+
if (info.isCurrent) {
|
|
14401
|
+
badges.push("current");
|
|
14402
|
+
}
|
|
14403
|
+
if (info.branch === null) {
|
|
14404
|
+
badges.push("detached");
|
|
14405
|
+
}
|
|
14406
|
+
if (info.status === "dirty") {
|
|
14407
|
+
badges.push("dirty");
|
|
14408
|
+
}
|
|
14409
|
+
if (info.upstream.kind === "stale") {
|
|
14410
|
+
badges.push("stale", "gone");
|
|
14411
|
+
}
|
|
14412
|
+
if (isUpToDate(info.upstream)) {
|
|
14413
|
+
badges.push("up to date");
|
|
14414
|
+
}
|
|
14415
|
+
return badges;
|
|
14416
|
+
}
|
|
14417
|
+
function isUpToDate(upstream) {
|
|
14418
|
+
return upstream.kind === "tracked" && upstream.ahead === 0 && upstream.behind === 0;
|
|
14419
|
+
}
|
|
14420
|
+
function formatPromptRecency(timestamp, type, now) {
|
|
14421
|
+
if (timestamp === null || type === null) {
|
|
14422
|
+
return "last used: never";
|
|
14423
|
+
}
|
|
14424
|
+
const label = type === "used" ? "last used" : "last worked";
|
|
14425
|
+
return `${label}: ${formatPickerAge(timestamp, now)}`;
|
|
14426
|
+
}
|
|
14427
|
+
function formatPickerAge(timestamp, now) {
|
|
14428
|
+
const ageSeconds = Math.max(0, Math.floor((now - timestamp) / 1e3));
|
|
14429
|
+
if (ageSeconds < 60) {
|
|
14430
|
+
return "now";
|
|
14431
|
+
}
|
|
14432
|
+
if (ageSeconds < 60 * 60) {
|
|
14433
|
+
return `${Math.floor(ageSeconds / 60)}m ago`;
|
|
14434
|
+
}
|
|
14435
|
+
if (ageSeconds < 24 * 60 * 60) {
|
|
14436
|
+
return `${Math.floor(ageSeconds / (60 * 60))}h ago`;
|
|
14437
|
+
}
|
|
14438
|
+
if (isYesterday(timestamp, now)) {
|
|
14439
|
+
return "yesterday";
|
|
14440
|
+
}
|
|
14441
|
+
return new Intl.DateTimeFormat("en-US", {
|
|
14442
|
+
day: "numeric",
|
|
14443
|
+
month: "short"
|
|
14444
|
+
}).format(new Date(timestamp));
|
|
14445
|
+
}
|
|
14446
|
+
function isYesterday(timestamp, now) {
|
|
14447
|
+
const date = new Date(timestamp);
|
|
14448
|
+
const yesterday = new Date(now);
|
|
14449
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
14450
|
+
return date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === yesterday.getDate();
|
|
14451
|
+
}
|
|
14452
|
+
function buildSearchText(repoName, worktree) {
|
|
14453
|
+
return [
|
|
14454
|
+
repoName,
|
|
14455
|
+
worktree.branch ?? "detached",
|
|
14456
|
+
worktree.path,
|
|
14457
|
+
`${repoName}/${worktree.branch ?? "detached"}`
|
|
14458
|
+
].join(" ").toLowerCase();
|
|
14459
|
+
}
|
|
14460
|
+
function normalizeQuery(query) {
|
|
14461
|
+
const normalized = query?.trim().toLowerCase();
|
|
14462
|
+
return normalized && normalized.length > 0 ? normalized : null;
|
|
14463
|
+
}
|
|
14464
|
+
function comparePromptEntries(a, b3) {
|
|
14465
|
+
if (a.isCurrent && !b3.isCurrent) return -1;
|
|
14466
|
+
if (!a.isCurrent && b3.isCurrent) return 1;
|
|
14467
|
+
if (a.group !== b3.group) {
|
|
14468
|
+
return groupRank(a.group) - groupRank(b3.group);
|
|
14469
|
+
}
|
|
14470
|
+
const aRecent = a.lastActivityTimestamp ?? 0;
|
|
14471
|
+
const bRecent = b3.lastActivityTimestamp ?? 0;
|
|
14472
|
+
if (aRecent !== bRecent) {
|
|
14473
|
+
return bRecent - aRecent;
|
|
14474
|
+
}
|
|
14475
|
+
return a.repoName.localeCompare(b3.repoName) || (a.branch ?? "").localeCompare(b3.branch ?? "") || a.path.localeCompare(b3.path);
|
|
14476
|
+
}
|
|
14477
|
+
function groupRank(group) {
|
|
14478
|
+
return group === "recent" ? 0 : 1;
|
|
14479
|
+
}
|
|
14480
|
+
function scoreWorktreeMatch(entry, query) {
|
|
14481
|
+
const branch = entry.branch ?? "detached";
|
|
14482
|
+
const exactCandidates = [
|
|
14483
|
+
branch,
|
|
14484
|
+
entry.path,
|
|
14485
|
+
`${entry.repoName}/${branch}`
|
|
14486
|
+
].map((candidate) => candidate.toLowerCase());
|
|
14487
|
+
if (exactCandidates.includes(query)) {
|
|
14488
|
+
return 1e3;
|
|
14489
|
+
}
|
|
14490
|
+
return buildSearchText(entry.repoName, entry).includes(query) ? 1 : null;
|
|
14491
|
+
}
|
|
14492
|
+
function middleEllipsize(value, maxLength) {
|
|
14493
|
+
if (value.length <= maxLength) {
|
|
14494
|
+
return value;
|
|
14495
|
+
}
|
|
14496
|
+
if (maxLength <= 1) {
|
|
14497
|
+
return "\u2026";
|
|
14498
|
+
}
|
|
14499
|
+
const keep = maxLength - 1;
|
|
14500
|
+
const start = Math.ceil(keep / 2);
|
|
14501
|
+
const end = Math.floor(keep / 2);
|
|
14502
|
+
return `${value.slice(0, start)}\u2026${value.slice(value.length - end)}`;
|
|
14503
|
+
}
|
|
14504
|
+
|
|
14242
14505
|
// src/worktree-prompts.ts
|
|
14243
14506
|
async function defaultConfirmForceRemoveWorktree(worktreePath) {
|
|
14244
14507
|
const choice = await ye({
|
|
@@ -14299,7 +14562,14 @@ function createCleanCommand(dependencies = {}) {
|
|
|
14299
14562
|
return 1;
|
|
14300
14563
|
}
|
|
14301
14564
|
const shouldSelectAll = options.force || options.dryRun && (options.stale || options.json || isHeadless());
|
|
14302
|
-
const selections = shouldSelectAll ? cleanupCandidates.map((w2) => w2.path) : await promptForWorktrees(
|
|
14565
|
+
const selections = shouldSelectAll ? cleanupCandidates.map((w2) => w2.path) : await promptForWorktrees(
|
|
14566
|
+
await buildWorktreePromptEntries(
|
|
14567
|
+
cleanupCandidates.map((worktree) => ({
|
|
14568
|
+
repoName: repository.repoName,
|
|
14569
|
+
worktree
|
|
14570
|
+
}))
|
|
14571
|
+
)
|
|
14572
|
+
);
|
|
14303
14573
|
if (!selections || selections.length === 0) {
|
|
14304
14574
|
options.stderr("Aborted\n");
|
|
14305
14575
|
return 1;
|
|
@@ -14534,19 +14804,7 @@ function toMessage(error) {
|
|
|
14534
14804
|
return error instanceof Error ? error.message : String(error);
|
|
14535
14805
|
}
|
|
14536
14806
|
async function defaultPromptForWorktrees(worktrees) {
|
|
14537
|
-
|
|
14538
|
-
const choice = await fe({
|
|
14539
|
-
message: "Choose worktrees to clean",
|
|
14540
|
-
options: worktrees.map((worktree, i) => {
|
|
14541
|
-
return {
|
|
14542
|
-
hint: formatWorktreeHint(infos[i]),
|
|
14543
|
-
label: worktree.branch ?? "(detached)",
|
|
14544
|
-
value: worktree.path
|
|
14545
|
-
};
|
|
14546
|
-
}),
|
|
14547
|
-
required: true
|
|
14548
|
-
});
|
|
14549
|
-
return pD(choice) ? null : choice;
|
|
14807
|
+
return promptForMultipleWorktrees("Choose worktrees to clean", worktrees);
|
|
14550
14808
|
}
|
|
14551
14809
|
async function defaultConfirmRemoval(worktrees) {
|
|
14552
14810
|
const branchCount = worktrees.filter(
|
|
@@ -15567,7 +15825,7 @@ function createNewCommand(dependencies = {}) {
|
|
|
15567
15825
|
} else {
|
|
15568
15826
|
const choice = await prompt(worktreePath);
|
|
15569
15827
|
if (choice === "reuse") {
|
|
15570
|
-
|
|
15828
|
+
await recordWorktreeUsage(worktreePath, worktreeName);
|
|
15571
15829
|
await writeOutput(worktreePath, options.stdout);
|
|
15572
15830
|
return 0;
|
|
15573
15831
|
}
|
|
@@ -15635,7 +15893,7 @@ function createNewCommand(dependencies = {}) {
|
|
|
15635
15893
|
`
|
|
15636
15894
|
);
|
|
15637
15895
|
} else {
|
|
15638
|
-
await
|
|
15896
|
+
await recordWorktreeUsage(worktreePath, worktreeName);
|
|
15639
15897
|
await writeOutput(worktreePath, options.stdout);
|
|
15640
15898
|
}
|
|
15641
15899
|
if (options.open) {
|
|
@@ -15672,7 +15930,32 @@ function generateBranchPlaceholder(random = Math.random) {
|
|
|
15672
15930
|
"newton",
|
|
15673
15931
|
"lovelace",
|
|
15674
15932
|
"nietzsche",
|
|
15675
|
-
"kafka"
|
|
15933
|
+
"kafka",
|
|
15934
|
+
"sappho",
|
|
15935
|
+
"aristotle",
|
|
15936
|
+
"pythagoras",
|
|
15937
|
+
"artemis",
|
|
15938
|
+
"apollo",
|
|
15939
|
+
"minerva",
|
|
15940
|
+
"persephone",
|
|
15941
|
+
"icarus",
|
|
15942
|
+
"odysseus",
|
|
15943
|
+
"murasaki",
|
|
15944
|
+
"shakespeare",
|
|
15945
|
+
"frida",
|
|
15946
|
+
"davinci",
|
|
15947
|
+
"kepler",
|
|
15948
|
+
"copernicus",
|
|
15949
|
+
"faraday",
|
|
15950
|
+
"noether",
|
|
15951
|
+
"hopper",
|
|
15952
|
+
"boole",
|
|
15953
|
+
"shannon",
|
|
15954
|
+
"gauss",
|
|
15955
|
+
"ramanujan",
|
|
15956
|
+
"austen",
|
|
15957
|
+
"borges",
|
|
15958
|
+
"zeno"
|
|
15676
15959
|
];
|
|
15677
15960
|
const antics = [
|
|
15678
15961
|
"borrowed-a-bike",
|
|
@@ -15689,9 +15972,50 @@ function generateBranchPlaceholder(random = Math.random) {
|
|
|
15689
15972
|
"watered-the-plants",
|
|
15690
15973
|
"washed-the-dishes",
|
|
15691
15974
|
"folded-the-laundry",
|
|
15692
|
-
"took-a-nap"
|
|
15975
|
+
"took-a-nap",
|
|
15976
|
+
"lost-a-sock",
|
|
15977
|
+
"patched-the-boat",
|
|
15978
|
+
"alphabetized-the-spoons",
|
|
15979
|
+
"argued-with-the-calendar",
|
|
15980
|
+
"misplaced-the-moon",
|
|
15981
|
+
"painted-the-fence",
|
|
15982
|
+
"overcooked-the-rice",
|
|
15983
|
+
"packed-the-snacks",
|
|
15984
|
+
"dropped-the-spoon",
|
|
15985
|
+
"hid-the-remote",
|
|
15986
|
+
"untangled-the-cables",
|
|
15987
|
+
"rebooted-the-kettle",
|
|
15988
|
+
"indexed-the-attic",
|
|
15989
|
+
"forgot-the-password",
|
|
15990
|
+
"sorted-the-buttons",
|
|
15991
|
+
"mopped-the-ceiling",
|
|
15992
|
+
"polished-the-doorknob",
|
|
15993
|
+
"misread-the-map",
|
|
15994
|
+
"reheated-the-tea",
|
|
15995
|
+
"fixed-the-squeak",
|
|
15996
|
+
"labeled-the-drawer",
|
|
15997
|
+
"stacked-the-chairs",
|
|
15998
|
+
"overslept-the-standup",
|
|
15999
|
+
"claimed-the-last-bagel",
|
|
16000
|
+
"debugged-the-toaster"
|
|
15693
16001
|
];
|
|
15694
|
-
|
|
16002
|
+
const root = pickRandom(roots, random);
|
|
16003
|
+
const antic = pickRandom(antics, random);
|
|
16004
|
+
const suffix = generateBranchPlaceholderSuffix(random);
|
|
16005
|
+
return `${root}-${antic}-${suffix}`;
|
|
16006
|
+
}
|
|
16007
|
+
function pickRandom(values, random) {
|
|
16008
|
+
const index = Math.floor(random() * values.length);
|
|
16009
|
+
return values[Math.min(index, values.length - 1)];
|
|
16010
|
+
}
|
|
16011
|
+
function generateBranchPlaceholderSuffix(random) {
|
|
16012
|
+
const characters = "abcdefghijklmnopqrstuvwxyz0123456789";
|
|
16013
|
+
let suffix = "";
|
|
16014
|
+
for (let index = 0; index < 3; index += 1) {
|
|
16015
|
+
const characterIndex = Math.floor(random() * characters.length);
|
|
16016
|
+
suffix += characters[Math.min(characterIndex, characters.length - 1)];
|
|
16017
|
+
}
|
|
16018
|
+
return suffix;
|
|
15695
16019
|
}
|
|
15696
16020
|
function applyConfiguredBranchPrefix(branch, branchPrefix) {
|
|
15697
16021
|
if (typeof branchPrefix !== "string" || branchPrefix.length === 0) {
|
|
@@ -15732,10 +16056,6 @@ async function defaultPromptForBranch(placeholder) {
|
|
|
15732
16056
|
}
|
|
15733
16057
|
return choice.trim();
|
|
15734
16058
|
}
|
|
15735
|
-
function pickRandom(values, random) {
|
|
15736
|
-
const index = Math.floor(random() * values.length);
|
|
15737
|
-
return values[Math.min(index, values.length - 1)];
|
|
15738
|
-
}
|
|
15739
16059
|
async function localBranchExists(repoRoot, branchName) {
|
|
15740
16060
|
try {
|
|
15741
16061
|
await execFileAsync3(
|
|
@@ -15892,7 +16212,7 @@ async function runWarpNavigate(options) {
|
|
|
15892
16212
|
);
|
|
15893
16213
|
return 0;
|
|
15894
16214
|
}
|
|
15895
|
-
|
|
16215
|
+
await recordWorktreeUsage(target.path, target.branch);
|
|
15896
16216
|
await writeShellOutput(WARP_OUTPUT_FILE_ENV, target.path, options.stdout);
|
|
15897
16217
|
return 0;
|
|
15898
16218
|
}
|
|
@@ -15975,18 +16295,6 @@ async function canonicalizeRepoPath2(repoPath) {
|
|
|
15975
16295
|
return resolve5(repoPath);
|
|
15976
16296
|
}
|
|
15977
16297
|
}
|
|
15978
|
-
function findByQuery(items, query) {
|
|
15979
|
-
const slashIdx = query.indexOf("/");
|
|
15980
|
-
if (slashIdx !== -1) {
|
|
15981
|
-
const repoQuery = query.slice(0, slashIdx);
|
|
15982
|
-
const branchQuery = query.slice(slashIdx + 1);
|
|
15983
|
-
const match = items.find(
|
|
15984
|
-
(item) => item.repoName === repoQuery && item.worktree.branch === branchQuery
|
|
15985
|
-
);
|
|
15986
|
-
if (match) return match;
|
|
15987
|
-
}
|
|
15988
|
-
return items.find((item) => item.worktree.branch === query) ?? null;
|
|
15989
|
-
}
|
|
15990
16298
|
async function resolveWarpTarget(options) {
|
|
15991
16299
|
const cmd = options.commandName ?? "gji";
|
|
15992
16300
|
const emitError4 = (message, hint) => {
|
|
@@ -16000,6 +16308,7 @@ async function resolveWarpTarget(options) {
|
|
|
16000
16308
|
}
|
|
16001
16309
|
};
|
|
16002
16310
|
const registry = await loadRegistry();
|
|
16311
|
+
const currentRoot = await detectRepository(options.cwd).then((repository) => repository.currentRoot).catch(() => null);
|
|
16003
16312
|
if (registry.length === 0) {
|
|
16004
16313
|
emitError4(
|
|
16005
16314
|
"not in a git repository and no repos registered yet.",
|
|
@@ -16018,57 +16327,42 @@ async function resolveWarpTarget(options) {
|
|
|
16018
16327
|
if (result.status === "rejected") continue;
|
|
16019
16328
|
const { repoName, worktrees } = result.value;
|
|
16020
16329
|
for (const worktree of worktrees) {
|
|
16021
|
-
allItems.push({
|
|
16330
|
+
allItems.push({
|
|
16331
|
+
repoName,
|
|
16332
|
+
worktree: {
|
|
16333
|
+
...worktree,
|
|
16334
|
+
isCurrent: currentRoot !== null && worktree.path === currentRoot
|
|
16335
|
+
}
|
|
16336
|
+
});
|
|
16022
16337
|
}
|
|
16023
16338
|
}
|
|
16024
16339
|
if (allItems.length === 0) {
|
|
16025
16340
|
emitError4("no accessible worktrees found in any registered repo.");
|
|
16026
16341
|
return null;
|
|
16027
16342
|
}
|
|
16343
|
+
const promptSources = allItems.map((item) => ({
|
|
16344
|
+
repoName: item.repoName,
|
|
16345
|
+
worktree: item.worktree
|
|
16346
|
+
}));
|
|
16028
16347
|
if (options.branch) {
|
|
16029
|
-
const match =
|
|
16348
|
+
const match = resolveWorktreeQuery(promptSources, options.branch);
|
|
16030
16349
|
if (!match) {
|
|
16031
16350
|
emitError4(`no worktree found matching: ${options.branch}`);
|
|
16032
16351
|
return null;
|
|
16033
16352
|
}
|
|
16034
16353
|
return { branch: match.worktree.branch, path: match.worktree.path };
|
|
16035
16354
|
}
|
|
16036
|
-
const
|
|
16355
|
+
const promptEntries = await buildWorktreePromptEntries(promptSources);
|
|
16356
|
+
const path9 = await promptForWarpTarget(promptEntries);
|
|
16037
16357
|
if (!path9) {
|
|
16038
16358
|
options.stderr("Aborted\n");
|
|
16039
16359
|
return null;
|
|
16040
16360
|
}
|
|
16041
|
-
const chosen =
|
|
16042
|
-
return { branch: chosen?.
|
|
16361
|
+
const chosen = promptEntries.find((item) => item.path === path9);
|
|
16362
|
+
return { branch: chosen?.branch ?? null, path: path9 };
|
|
16043
16363
|
}
|
|
16044
16364
|
async function promptForWarpTarget(items) {
|
|
16045
|
-
|
|
16046
|
-
items.map((item) => readWorktreeHealth(item.worktree.path))
|
|
16047
|
-
);
|
|
16048
|
-
const choice = await ve({
|
|
16049
|
-
message: "Warp to a worktree",
|
|
16050
|
-
options: items.map((item, i) => {
|
|
16051
|
-
const health = healthResults[i].status === "fulfilled" ? healthResults[i].value : null;
|
|
16052
|
-
const upstream = health ? formatHint(item.worktree.branch, health) : null;
|
|
16053
|
-
const label = `${item.repoName} \u203A ${item.worktree.branch ?? "(detached)"}`;
|
|
16054
|
-
const pathHint = item.worktree.isCurrent ? `${item.worktree.path} (current)` : item.worktree.path;
|
|
16055
|
-
const hint = upstream ? `${upstream} \xB7 ${pathHint}` : pathHint;
|
|
16056
|
-
return { hint, label, value: item.worktree.path };
|
|
16057
|
-
})
|
|
16058
|
-
});
|
|
16059
|
-
if (pD(choice)) {
|
|
16060
|
-
return null;
|
|
16061
|
-
}
|
|
16062
|
-
return choice;
|
|
16063
|
-
}
|
|
16064
|
-
function formatHint(branch, health) {
|
|
16065
|
-
if (branch === null) return null;
|
|
16066
|
-
if (!health.hasUpstream) return "no upstream";
|
|
16067
|
-
if (health.upstreamGone) return "upstream gone";
|
|
16068
|
-
if (health.ahead === 0 && health.behind === 0) return "up to date";
|
|
16069
|
-
if (health.ahead === 0) return `behind ${health.behind}`;
|
|
16070
|
-
if (health.behind === 0) return `ahead ${health.ahead}`;
|
|
16071
|
-
return `ahead ${health.ahead}, behind ${health.behind}`;
|
|
16365
|
+
return promptForSingleWorktree("Warp to a worktree", items);
|
|
16072
16366
|
}
|
|
16073
16367
|
|
|
16074
16368
|
// src/go.ts
|
|
@@ -16095,7 +16389,7 @@ function createGoCommand(dependencies = {}) {
|
|
|
16095
16389
|
commandName: "gji go"
|
|
16096
16390
|
});
|
|
16097
16391
|
if (!target) return 1;
|
|
16098
|
-
|
|
16392
|
+
await recordWorktreeUsage(target.path, target.branch);
|
|
16099
16393
|
await writeShellOutput(GO_OUTPUT_FILE_ENV, target.path, options.stdout);
|
|
16100
16394
|
return 0;
|
|
16101
16395
|
}
|
|
@@ -16105,8 +16399,14 @@ function createGoCommand(dependencies = {}) {
|
|
|
16105
16399
|
);
|
|
16106
16400
|
return 1;
|
|
16107
16401
|
}
|
|
16108
|
-
const
|
|
16109
|
-
|
|
16402
|
+
const promptSources = worktrees.map((worktree) => ({
|
|
16403
|
+
repoName: repository.repoName,
|
|
16404
|
+
worktree
|
|
16405
|
+
}));
|
|
16406
|
+
const promptEntries = options.branch ? [] : await buildWorktreePromptEntries(promptSources);
|
|
16407
|
+
const queried = options.branch ? resolveWorktreeQuery(promptSources, options.branch) : null;
|
|
16408
|
+
const prompted = options.branch ? null : await prompt(promptEntries);
|
|
16409
|
+
const resolvedPath = options.branch ? queried?.worktree.path : prompted ?? void 0;
|
|
16110
16410
|
if (!resolvedPath) {
|
|
16111
16411
|
if (options.branch) {
|
|
16112
16412
|
options.stderr(`No worktree found for branch: ${options.branch}
|
|
@@ -16135,44 +16435,14 @@ function createGoCommand(dependencies = {}) {
|
|
|
16135
16435
|
},
|
|
16136
16436
|
options.stderr
|
|
16137
16437
|
);
|
|
16138
|
-
|
|
16139
|
-
() => void 0
|
|
16140
|
-
);
|
|
16438
|
+
await recordWorktreeUsage(resolvedPath, chosenWorktree?.branch ?? null);
|
|
16141
16439
|
await writeShellOutput(GO_OUTPUT_FILE_ENV, resolvedPath, options.stdout);
|
|
16142
16440
|
return 0;
|
|
16143
16441
|
};
|
|
16144
16442
|
}
|
|
16145
16443
|
var runGoCommand = createGoCommand();
|
|
16146
16444
|
async function promptForWorktree(worktrees) {
|
|
16147
|
-
|
|
16148
|
-
worktrees.map((w2) => readWorktreeHealth(w2.path))
|
|
16149
|
-
);
|
|
16150
|
-
const choice = await ve({
|
|
16151
|
-
message: "Choose a worktree",
|
|
16152
|
-
options: worktrees.map((worktree, i) => {
|
|
16153
|
-
const health = healthResults[i].status === "fulfilled" ? healthResults[i].value : null;
|
|
16154
|
-
const pathHint = worktree.isCurrent ? `${worktree.path} (current)` : worktree.path;
|
|
16155
|
-
const upstream = health ? formatUpstreamHint(worktree.branch, health) : null;
|
|
16156
|
-
return {
|
|
16157
|
-
value: worktree.path,
|
|
16158
|
-
label: worktree.branch ?? "(detached)",
|
|
16159
|
-
hint: upstream ? `${upstream} \xB7 ${pathHint}` : pathHint
|
|
16160
|
-
};
|
|
16161
|
-
})
|
|
16162
|
-
});
|
|
16163
|
-
if (pD(choice)) {
|
|
16164
|
-
return null;
|
|
16165
|
-
}
|
|
16166
|
-
return choice;
|
|
16167
|
-
}
|
|
16168
|
-
function formatUpstreamHint(branch, health) {
|
|
16169
|
-
if (branch === null) return null;
|
|
16170
|
-
if (!health.hasUpstream) return "no upstream";
|
|
16171
|
-
if (health.upstreamGone) return "upstream gone";
|
|
16172
|
-
if (health.ahead === 0 && health.behind === 0) return "up to date";
|
|
16173
|
-
if (health.ahead === 0) return `behind ${health.behind}`;
|
|
16174
|
-
if (health.behind === 0) return `ahead ${health.ahead}`;
|
|
16175
|
-
return `ahead ${health.ahead}, behind ${health.behind}`;
|
|
16445
|
+
return promptForSingleWorktree("Choose a worktree", worktrees);
|
|
16176
16446
|
}
|
|
16177
16447
|
|
|
16178
16448
|
// src/history-command.ts
|
|
@@ -16623,27 +16893,43 @@ function createOpenCommand(dependencies = {}) {
|
|
|
16623
16893
|
detectRepository(options.cwd)
|
|
16624
16894
|
]);
|
|
16625
16895
|
let targetPath;
|
|
16896
|
+
let targetWorktree;
|
|
16626
16897
|
if (options.branch) {
|
|
16627
|
-
const
|
|
16628
|
-
|
|
16898
|
+
const match = resolveWorktreeQuery(
|
|
16899
|
+
worktrees.map((worktree) => ({
|
|
16900
|
+
repoName: repository.repoName,
|
|
16901
|
+
worktree
|
|
16902
|
+
})),
|
|
16903
|
+
options.branch
|
|
16904
|
+
);
|
|
16905
|
+
if (!match) {
|
|
16629
16906
|
options.stderr(
|
|
16630
|
-
`gji open: no worktree found
|
|
16907
|
+
`gji open: no worktree found matching: ${options.branch}
|
|
16631
16908
|
`
|
|
16632
16909
|
);
|
|
16633
16910
|
options.stderr(`Hint: Use 'gji ls' to see available worktrees
|
|
16634
16911
|
`);
|
|
16635
16912
|
return 1;
|
|
16636
16913
|
}
|
|
16637
|
-
targetPath =
|
|
16914
|
+
targetPath = match.worktree.path;
|
|
16915
|
+
targetWorktree = match.worktree;
|
|
16638
16916
|
} else if (isHeadless()) {
|
|
16639
|
-
|
|
16917
|
+
targetWorktree = worktrees.find((w2) => w2.isCurrent);
|
|
16918
|
+
targetPath = targetWorktree?.path ?? options.cwd;
|
|
16640
16919
|
} else {
|
|
16641
|
-
const
|
|
16920
|
+
const entries = await buildWorktreePromptEntries(
|
|
16921
|
+
worktrees.map((worktree) => ({
|
|
16922
|
+
repoName: repository.repoName,
|
|
16923
|
+
worktree
|
|
16924
|
+
}))
|
|
16925
|
+
);
|
|
16926
|
+
const chosen = await promptForWorktree2(entries);
|
|
16642
16927
|
if (!chosen) {
|
|
16643
16928
|
options.stderr("Aborted\n");
|
|
16644
16929
|
return 1;
|
|
16645
16930
|
}
|
|
16646
16931
|
targetPath = chosen;
|
|
16932
|
+
targetWorktree = worktrees.find((w2) => w2.path === chosen);
|
|
16647
16933
|
}
|
|
16648
16934
|
const config = await loadEffectiveConfig(
|
|
16649
16935
|
repository.repoRoot,
|
|
@@ -16708,6 +16994,7 @@ function createOpenCommand(dependencies = {}) {
|
|
|
16708
16994
|
return 1;
|
|
16709
16995
|
}
|
|
16710
16996
|
const displayName = editorDef?.name ?? editorCli;
|
|
16997
|
+
await recordWorktreeUsage(targetPath, targetWorktree?.branch ?? null);
|
|
16711
16998
|
options.stdout(`Opened ${targetPath} in ${displayName}
|
|
16712
16999
|
`);
|
|
16713
17000
|
return 0;
|
|
@@ -16732,16 +17019,7 @@ async function isCommandAvailable(command) {
|
|
|
16732
17019
|
}
|
|
16733
17020
|
}
|
|
16734
17021
|
async function defaultPromptForWorktree(worktrees) {
|
|
16735
|
-
|
|
16736
|
-
message: "Choose a worktree to open",
|
|
16737
|
-
options: worktrees.map((w2) => ({
|
|
16738
|
-
value: w2.path,
|
|
16739
|
-
label: w2.branch ?? "(detached)",
|
|
16740
|
-
hint: w2.isCurrent ? `${w2.path} (current)` : w2.path
|
|
16741
|
-
}))
|
|
16742
|
-
});
|
|
16743
|
-
if (pD(choice)) return null;
|
|
16744
|
-
return choice;
|
|
17022
|
+
return promptForSingleWorktree("Choose a worktree to open", worktrees);
|
|
16745
17023
|
}
|
|
16746
17024
|
async function defaultPromptForEditor(editors) {
|
|
16747
17025
|
const choice = await ve({
|
|
@@ -16835,7 +17113,7 @@ function createPrCommand(dependencies = {}) {
|
|
|
16835
17113
|
}
|
|
16836
17114
|
const choice = await prompt(worktreePath);
|
|
16837
17115
|
if (choice === "reuse") {
|
|
16838
|
-
|
|
17116
|
+
await recordWorktreeUsage(worktreePath, branchName);
|
|
16839
17117
|
await writeOutput2(worktreePath, options.stdout);
|
|
16840
17118
|
return 0;
|
|
16841
17119
|
}
|
|
@@ -16926,7 +17204,7 @@ function createPrCommand(dependencies = {}) {
|
|
|
16926
17204
|
`
|
|
16927
17205
|
);
|
|
16928
17206
|
} else {
|
|
16929
|
-
await
|
|
17207
|
+
await recordWorktreeUsage(worktreePath, branchName);
|
|
16930
17208
|
await writeOutput2(worktreePath, options.stdout);
|
|
16931
17209
|
}
|
|
16932
17210
|
return 0;
|
|
@@ -17026,7 +17304,14 @@ function createRemoveCommand(dependencies = {}) {
|
|
|
17026
17304
|
}
|
|
17027
17305
|
return 1;
|
|
17028
17306
|
}
|
|
17029
|
-
const selection = options.branch ?? await promptForWorktree2(
|
|
17307
|
+
const selection = options.branch ?? await promptForWorktree2(
|
|
17308
|
+
await buildWorktreePromptEntries(
|
|
17309
|
+
linkedWorktrees.map((worktree2) => ({
|
|
17310
|
+
repoName: repository.repoName,
|
|
17311
|
+
worktree: worktree2
|
|
17312
|
+
}))
|
|
17313
|
+
)
|
|
17314
|
+
);
|
|
17030
17315
|
if (!selection) {
|
|
17031
17316
|
options.stderr("Aborted\n");
|
|
17032
17317
|
return 1;
|
|
@@ -17140,15 +17425,7 @@ function createRemoveCommand(dependencies = {}) {
|
|
|
17140
17425
|
}
|
|
17141
17426
|
var runRemoveCommand = createRemoveCommand();
|
|
17142
17427
|
async function defaultPromptForWorktree2(worktrees) {
|
|
17143
|
-
|
|
17144
|
-
message: "Choose a worktree to finish",
|
|
17145
|
-
options: worktrees.map((worktree) => ({
|
|
17146
|
-
hint: worktree.isCurrent ? `${worktree.path} (current)` : worktree.path,
|
|
17147
|
-
label: worktree.branch ?? "(detached)",
|
|
17148
|
-
value: worktree.path
|
|
17149
|
-
}))
|
|
17150
|
-
});
|
|
17151
|
-
return pD(choice) ? null : choice;
|
|
17428
|
+
return promptForSingleWorktree("Choose a worktree to finish", worktrees);
|
|
17152
17429
|
}
|
|
17153
17430
|
async function defaultConfirmRemoval2(worktree) {
|
|
17154
17431
|
const choice = await ye({
|