git-sync-tui 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -34
- package/README.zh-CN.md +126 -0
- package/dist/cli.js +887 -196
- package/package.json +17 -3
package/dist/cli.js
CHANGED
|
@@ -1,22 +1,142 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
+
}) : x)(function(x) {
|
|
5
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
+
});
|
|
2
8
|
|
|
3
9
|
// src/cli.tsx
|
|
4
10
|
import { render } from "ink";
|
|
5
11
|
import meow from "meow";
|
|
6
12
|
|
|
7
13
|
// src/app.tsx
|
|
8
|
-
import { useState as
|
|
9
|
-
import { Box as
|
|
14
|
+
import { useState as useState7, useEffect as useEffect4, useRef as useRef2, useCallback as useCallback2 } from "react";
|
|
15
|
+
import { Box as Box9, useApp } from "ink";
|
|
16
|
+
import { Spinner as Spinner6 } from "@inkjs/ui";
|
|
10
17
|
|
|
11
|
-
// src/components/
|
|
18
|
+
// src/components/ui.tsx
|
|
19
|
+
import React from "react";
|
|
12
20
|
import { Box, Text } from "ink";
|
|
13
|
-
import {
|
|
21
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
22
|
+
var STEP_LABELS = ["Remote", "Branch", "Commits", "Confirm", "Sync"];
|
|
23
|
+
function StepProgress({ current }) {
|
|
24
|
+
return /* @__PURE__ */ jsx(Box, { children: STEP_LABELS.map((label, i) => {
|
|
25
|
+
const step = i + 1;
|
|
26
|
+
const isActive = step === current;
|
|
27
|
+
const isDone = step < current;
|
|
28
|
+
const isLast = i === STEP_LABELS.length - 1;
|
|
29
|
+
return /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
30
|
+
/* @__PURE__ */ jsxs(Text, { color: isActive ? "cyan" : isDone ? "green" : "gray", dimColor: !isActive && !isDone, children: [
|
|
31
|
+
isDone ? "\u25CF" : isActive ? "\u25C6" : "\u25CB",
|
|
32
|
+
" ",
|
|
33
|
+
isActive ? /* @__PURE__ */ jsx(Text, { bold: true, children: label }) : label
|
|
34
|
+
] }),
|
|
35
|
+
!isLast && /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: " \u2500 " })
|
|
36
|
+
] }, label);
|
|
37
|
+
}) });
|
|
38
|
+
}
|
|
39
|
+
function SectionHeader({ title, subtitle }) {
|
|
40
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
41
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
42
|
+
"\u25B8 ",
|
|
43
|
+
title
|
|
44
|
+
] }),
|
|
45
|
+
subtitle && /* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: true, children: [
|
|
46
|
+
" ",
|
|
47
|
+
subtitle
|
|
48
|
+
] })
|
|
49
|
+
] });
|
|
50
|
+
}
|
|
51
|
+
function KeyHints({ hints }) {
|
|
52
|
+
return /* @__PURE__ */ jsx(Box, { gap: 1, flexWrap: "wrap", children: hints.map(({ key, label }) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
53
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "[" }),
|
|
54
|
+
/* @__PURE__ */ jsx(Text, { color: "cyan", children: key }),
|
|
55
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "]" }),
|
|
56
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: true, children: [
|
|
57
|
+
" ",
|
|
58
|
+
label
|
|
59
|
+
] })
|
|
60
|
+
] }, key)) });
|
|
61
|
+
}
|
|
62
|
+
function InlineKeys({ hints }) {
|
|
63
|
+
return /* @__PURE__ */ jsx(Box, { gap: 1, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
64
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
65
|
+
"[",
|
|
66
|
+
key,
|
|
67
|
+
"]"
|
|
68
|
+
] }),
|
|
69
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
70
|
+
" ",
|
|
71
|
+
label
|
|
72
|
+
] }),
|
|
73
|
+
i < hints.length - 1 && /* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: " / " })
|
|
74
|
+
] }, key)) });
|
|
75
|
+
}
|
|
76
|
+
var PANEL_CONFIG = {
|
|
77
|
+
info: { icon: "\u25C6", color: "cyan", borderColor: "cyan" },
|
|
78
|
+
warn: { icon: "\u25B2", color: "yellow", borderColor: "yellow" },
|
|
79
|
+
error: { icon: "\u2716", color: "red", borderColor: "red" },
|
|
80
|
+
success: { icon: "\u2714", color: "green", borderColor: "green" }
|
|
81
|
+
};
|
|
82
|
+
function StatusPanel({ type, title, children }) {
|
|
83
|
+
const { icon, color, borderColor } = PANEL_CONFIG[type];
|
|
84
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor, paddingX: 1, children: [
|
|
85
|
+
/* @__PURE__ */ jsxs(Text, { bold: true, color, children: [
|
|
86
|
+
icon,
|
|
87
|
+
" ",
|
|
88
|
+
title
|
|
89
|
+
] }),
|
|
90
|
+
children
|
|
91
|
+
] });
|
|
92
|
+
}
|
|
93
|
+
function AppHeader({ step, stashed }) {
|
|
94
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
95
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
96
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: "cyan", children: "\u25C7 " }),
|
|
97
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "git-sync-tui" }),
|
|
98
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: " cherry-pick --no-commit" }),
|
|
99
|
+
stashed && /* @__PURE__ */ jsx(Text, { color: "yellow", children: " \u25AA stashed" })
|
|
100
|
+
] }),
|
|
101
|
+
/* @__PURE__ */ jsxs(Box, { children: [
|
|
102
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: " " }),
|
|
103
|
+
/* @__PURE__ */ jsx(StepProgress, { current: step })
|
|
104
|
+
] })
|
|
105
|
+
] });
|
|
106
|
+
}
|
|
14
107
|
|
|
15
|
-
// src/
|
|
16
|
-
import {
|
|
108
|
+
// src/components/stash-prompt.tsx
|
|
109
|
+
import { Box as Box2, Text as Text2, useInput } from "ink";
|
|
110
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
111
|
+
function StashPrompt({ onConfirm, onSkip }) {
|
|
112
|
+
useInput((input) => {
|
|
113
|
+
if (input === "y" || input === "Y") {
|
|
114
|
+
onConfirm();
|
|
115
|
+
} else if (input === "n" || input === "N") {
|
|
116
|
+
onSkip();
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", gap: 1, children: [
|
|
120
|
+
/* @__PURE__ */ jsx2(StatusPanel, { type: "warn", title: "\u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u7684\u53D8\u66F4", children: /* @__PURE__ */ jsx2(Text2, { color: "gray", children: " Cherry-pick \u64CD\u4F5C\u53EF\u80FD\u4F1A\u4E0E\u672A\u63D0\u4EA4\u7684\u5185\u5BB9\u51B2\u7A81" }) }),
|
|
121
|
+
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
122
|
+
/* @__PURE__ */ jsx2(Text2, { bold: true, children: "\u81EA\u52A8 stash \u4FDD\u5B58\u5F53\u524D\u53D8\u66F4? " }),
|
|
123
|
+
/* @__PURE__ */ jsx2(InlineKeys, { hints: [
|
|
124
|
+
{ key: "y", label: "\u662F" },
|
|
125
|
+
{ key: "n", label: "\u5426\uFF0C\u7EE7\u7EED" }
|
|
126
|
+
] })
|
|
127
|
+
] })
|
|
128
|
+
] });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// src/components/stash-recovery.tsx
|
|
132
|
+
import { useState, useEffect } from "react";
|
|
133
|
+
import { Box as Box3, Text as Text3, useInput as useInput2 } from "ink";
|
|
134
|
+
import { Spinner } from "@inkjs/ui";
|
|
17
135
|
|
|
18
136
|
// src/utils/git.ts
|
|
19
137
|
import simpleGit from "simple-git";
|
|
138
|
+
import { existsSync, writeFileSync, unlinkSync, readFileSync } from "fs";
|
|
139
|
+
import { join } from "path";
|
|
20
140
|
var gitInstance = null;
|
|
21
141
|
function getGit(cwd) {
|
|
22
142
|
if (!gitInstance || cwd) {
|
|
@@ -32,6 +152,10 @@ async function getRemotes() {
|
|
|
32
152
|
fetchUrl: r.refs.fetch
|
|
33
153
|
}));
|
|
34
154
|
}
|
|
155
|
+
async function addRemote(name, url) {
|
|
156
|
+
const git = getGit();
|
|
157
|
+
await git.addRemote(name, url);
|
|
158
|
+
}
|
|
35
159
|
async function getRemoteBranches(remote) {
|
|
36
160
|
const git = getGit();
|
|
37
161
|
try {
|
|
@@ -80,12 +204,29 @@ ${result.trim()}`);
|
|
|
80
204
|
return "(\u65E0\u6CD5\u83B7\u53D6 stat \u4FE1\u606F)";
|
|
81
205
|
}
|
|
82
206
|
}
|
|
83
|
-
async function
|
|
207
|
+
async function hasMergeCommits(hashes) {
|
|
208
|
+
const git = getGit();
|
|
209
|
+
try {
|
|
210
|
+
for (const hash of hashes) {
|
|
211
|
+
const result = await git.raw(["rev-list", "--merges", "-n", "1", hash]);
|
|
212
|
+
if (result.trim()) return true;
|
|
213
|
+
}
|
|
214
|
+
return false;
|
|
215
|
+
} catch {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
async function cherryPick(hashes, useMainline = false) {
|
|
84
220
|
const git = getGit();
|
|
85
221
|
try {
|
|
86
222
|
const orderedHashes = [...hashes].reverse();
|
|
87
223
|
for (const hash of orderedHashes) {
|
|
88
|
-
|
|
224
|
+
const args = ["cherry-pick", "--no-commit"];
|
|
225
|
+
if (useMainline) {
|
|
226
|
+
args.push("-m", "1");
|
|
227
|
+
}
|
|
228
|
+
args.push(hash);
|
|
229
|
+
await git.raw(args);
|
|
89
230
|
}
|
|
90
231
|
return { success: true };
|
|
91
232
|
} catch (err) {
|
|
@@ -111,10 +252,138 @@ async function getStagedStat() {
|
|
|
111
252
|
return "";
|
|
112
253
|
}
|
|
113
254
|
}
|
|
255
|
+
async function isWorkingDirClean() {
|
|
256
|
+
const git = getGit();
|
|
257
|
+
const status = await git.status();
|
|
258
|
+
return status.isClean();
|
|
259
|
+
}
|
|
260
|
+
async function stash() {
|
|
261
|
+
const git = getGit();
|
|
262
|
+
try {
|
|
263
|
+
await git.stash(["push", "--include-untracked", "-m", "Auto-stash by git-sync-tui"]);
|
|
264
|
+
return true;
|
|
265
|
+
} catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function stashPop() {
|
|
270
|
+
const git = getGit();
|
|
271
|
+
try {
|
|
272
|
+
await git.stash(["pop"]);
|
|
273
|
+
return true;
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
var STASH_GUARD_FILE = "git-sync-tui-stash-guard";
|
|
279
|
+
async function getGitDir() {
|
|
280
|
+
const git = getGit();
|
|
281
|
+
const dir = await git.revparse(["--git-dir"]);
|
|
282
|
+
return dir.trim();
|
|
283
|
+
}
|
|
284
|
+
async function writeStashGuard() {
|
|
285
|
+
try {
|
|
286
|
+
const gitDir = await getGitDir();
|
|
287
|
+
writeFileSync(join(gitDir, STASH_GUARD_FILE), (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function removeStashGuard() {
|
|
292
|
+
try {
|
|
293
|
+
const gitDir = await getGitDir();
|
|
294
|
+
const guardPath = join(gitDir, STASH_GUARD_FILE);
|
|
295
|
+
if (existsSync(guardPath)) {
|
|
296
|
+
unlinkSync(guardPath);
|
|
297
|
+
}
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function removeStashGuardSync() {
|
|
302
|
+
try {
|
|
303
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
304
|
+
const gitDir = String(execSync2("git rev-parse --git-dir", { encoding: "utf-8" })).trim();
|
|
305
|
+
const guardPath = join(gitDir, STASH_GUARD_FILE);
|
|
306
|
+
if (existsSync(guardPath)) {
|
|
307
|
+
unlinkSync(guardPath);
|
|
308
|
+
}
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async function checkStashGuard() {
|
|
313
|
+
try {
|
|
314
|
+
const gitDir = await getGitDir();
|
|
315
|
+
const guardPath = join(gitDir, STASH_GUARD_FILE);
|
|
316
|
+
if (existsSync(guardPath)) {
|
|
317
|
+
const timestamp = readFileSync(guardPath, "utf-8").trim();
|
|
318
|
+
return { exists: true, timestamp };
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
return { exists: false };
|
|
323
|
+
}
|
|
324
|
+
async function findStashEntry() {
|
|
325
|
+
const git = getGit();
|
|
326
|
+
try {
|
|
327
|
+
const result = await git.stash(["list"]);
|
|
328
|
+
const lines = result.trim().split("\n");
|
|
329
|
+
for (const line of lines) {
|
|
330
|
+
if (line.includes("Auto-stash by git-sync-tui")) {
|
|
331
|
+
return line;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
} catch {
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// src/components/stash-recovery.tsx
|
|
340
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
341
|
+
function StashRecovery({ timestamp, onRecover, onSkip }) {
|
|
342
|
+
const [stashEntry, setStashEntry] = useState(void 0);
|
|
343
|
+
useEffect(() => {
|
|
344
|
+
findStashEntry().then(setStashEntry);
|
|
345
|
+
}, []);
|
|
346
|
+
useInput2((input) => {
|
|
347
|
+
if (input === "y" || input === "Y") {
|
|
348
|
+
onRecover();
|
|
349
|
+
} else if (input === "n" || input === "N") {
|
|
350
|
+
onSkip();
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
if (stashEntry === void 0) {
|
|
354
|
+
return /* @__PURE__ */ jsx3(Spinner, { label: "\u68C0\u67E5 stash \u8BB0\u5F55..." });
|
|
355
|
+
}
|
|
356
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", gap: 1, children: [
|
|
357
|
+
/* @__PURE__ */ jsxs3(StatusPanel, { type: "warn", title: "\u68C0\u6D4B\u5230\u4E0A\u6B21\u8FD0\u884C\u4E2D\u65AD", children: [
|
|
358
|
+
timestamp && /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
|
|
359
|
+
" \u4E2D\u65AD\u65F6\u95F4: ",
|
|
360
|
+
timestamp
|
|
361
|
+
] }),
|
|
362
|
+
stashEntry && /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
|
|
363
|
+
" Stash: ",
|
|
364
|
+
stashEntry
|
|
365
|
+
] }),
|
|
366
|
+
!stashEntry && /* @__PURE__ */ jsx3(Text3, { color: "red", children: " \u672A\u627E\u5230\u5BF9\u5E94\u7684 stash \u6761\u76EE\uFF08\u53EF\u80FD\u5DF2\u624B\u52A8\u6062\u590D\uFF09" })
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
369
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: stashEntry ? "\u6062\u590D stash? " : "\u6E05\u9664\u4E2D\u65AD\u6807\u8BB0? " }),
|
|
370
|
+
/* @__PURE__ */ jsx3(InlineKeys, { hints: [
|
|
371
|
+
{ key: "y", label: "\u662F" },
|
|
372
|
+
{ key: "n", label: "\u8DF3\u8FC7" }
|
|
373
|
+
] })
|
|
374
|
+
] })
|
|
375
|
+
] });
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/components/remote-select.tsx
|
|
379
|
+
import { useState as useState3 } from "react";
|
|
380
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
381
|
+
import { Select, Spinner as Spinner2, TextInput } from "@inkjs/ui";
|
|
114
382
|
|
|
115
383
|
// src/hooks/use-git.ts
|
|
384
|
+
import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
|
|
116
385
|
function useAsync(fn, deps = []) {
|
|
117
|
-
const [state, setState] =
|
|
386
|
+
const [state, setState] = useState2({
|
|
118
387
|
data: null,
|
|
119
388
|
loading: true,
|
|
120
389
|
error: null
|
|
@@ -128,7 +397,7 @@ function useAsync(fn, deps = []) {
|
|
|
128
397
|
setState({ data: null, loading: false, error: err.message });
|
|
129
398
|
}
|
|
130
399
|
}, deps);
|
|
131
|
-
|
|
400
|
+
useEffect2(() => {
|
|
132
401
|
load();
|
|
133
402
|
}, [load]);
|
|
134
403
|
return { ...state, reload: load };
|
|
@@ -149,9 +418,9 @@ function useCommits(remote, branch, count = 30) {
|
|
|
149
418
|
);
|
|
150
419
|
}
|
|
151
420
|
function useCommitStat(hashes) {
|
|
152
|
-
const [stat, setStat] =
|
|
153
|
-
const [loading, setLoading] =
|
|
154
|
-
|
|
421
|
+
const [stat, setStat] = useState2("");
|
|
422
|
+
const [loading, setLoading] = useState2(false);
|
|
423
|
+
useEffect2(() => {
|
|
155
424
|
if (hashes.length === 0) {
|
|
156
425
|
setStat("");
|
|
157
426
|
return;
|
|
@@ -169,310 +438,729 @@ function useCommitStat(hashes) {
|
|
|
169
438
|
}
|
|
170
439
|
|
|
171
440
|
// src/components/remote-select.tsx
|
|
172
|
-
import { jsx, jsxs } from "react/jsx-runtime";
|
|
173
|
-
function
|
|
174
|
-
const
|
|
441
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
442
|
+
function extractRemoteName(url) {
|
|
443
|
+
const trimmed = url.trim().replace(/\/+$/, "").replace(/\.git$/, "");
|
|
444
|
+
const lastSegment = trimmed.split(/[/\\:]+/).filter(Boolean).pop() || "";
|
|
445
|
+
return lastSegment;
|
|
446
|
+
}
|
|
447
|
+
function RemoteSelect({ onSelect, onBack }) {
|
|
448
|
+
const { data: remotes, loading, error, reload } = useRemotes();
|
|
449
|
+
const [phase, setPhase] = useState3("list");
|
|
450
|
+
const [customUrl, setCustomUrl] = useState3("");
|
|
451
|
+
const [addError, setAddError] = useState3(null);
|
|
452
|
+
useInput3((_input, key) => {
|
|
453
|
+
if (key.escape) {
|
|
454
|
+
if (phase === "input-name") {
|
|
455
|
+
setPhase("input-url");
|
|
456
|
+
} else if (phase === "input-url") {
|
|
457
|
+
setPhase("list");
|
|
458
|
+
} else if (phase === "list") {
|
|
459
|
+
onBack?.();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
});
|
|
175
463
|
if (loading) {
|
|
176
|
-
return /* @__PURE__ */
|
|
464
|
+
return /* @__PURE__ */ jsx4(Spinner2, { label: "\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93..." });
|
|
177
465
|
}
|
|
178
466
|
if (error) {
|
|
179
|
-
return /* @__PURE__ */
|
|
180
|
-
"\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93\u5931\u8D25: ",
|
|
467
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
468
|
+
"\u2716 \u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93\u5931\u8D25: ",
|
|
181
469
|
error
|
|
182
470
|
] });
|
|
183
471
|
}
|
|
184
|
-
if (
|
|
185
|
-
return /* @__PURE__ */
|
|
472
|
+
if (phase === "adding") {
|
|
473
|
+
return /* @__PURE__ */ jsx4(Spinner2, { label: "\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93..." });
|
|
186
474
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
475
|
+
if (phase === "input-url") {
|
|
476
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
477
|
+
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93" }),
|
|
478
|
+
addError && /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
479
|
+
"\u2716 ",
|
|
480
|
+
addError
|
|
481
|
+
] }),
|
|
482
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
483
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "URL \u25B8 " }),
|
|
484
|
+
/* @__PURE__ */ jsx4(
|
|
485
|
+
TextInput,
|
|
486
|
+
{
|
|
487
|
+
placeholder: "https://github.com/user/repo.git",
|
|
488
|
+
onSubmit: (url) => {
|
|
489
|
+
if (!url.trim()) {
|
|
490
|
+
setAddError("\u5730\u5740\u4E0D\u80FD\u4E3A\u7A7A");
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
setCustomUrl(url.trim());
|
|
494
|
+
setPhase("input-name");
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
)
|
|
498
|
+
] }),
|
|
499
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: " \u652F\u6301 HTTPS / SSH \u5730\u5740" })
|
|
500
|
+
] });
|
|
501
|
+
}
|
|
502
|
+
if (phase === "input-name") {
|
|
503
|
+
const defaultName = extractRemoteName(customUrl);
|
|
504
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
505
|
+
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93", subtitle: customUrl }),
|
|
506
|
+
addError && /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
507
|
+
"\u2716 ",
|
|
508
|
+
addError
|
|
509
|
+
] }),
|
|
510
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
511
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "\u540D\u79F0 \u25B8 " }),
|
|
512
|
+
/* @__PURE__ */ jsx4(
|
|
513
|
+
TextInput,
|
|
514
|
+
{
|
|
515
|
+
placeholder: defaultName || "upstream",
|
|
516
|
+
defaultValue: defaultName,
|
|
517
|
+
onSubmit: async (name) => {
|
|
518
|
+
const remoteName = name.trim();
|
|
519
|
+
if (!remoteName) {
|
|
520
|
+
setAddError("\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A");
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
if (remotes?.some((r) => r.name === remoteName)) {
|
|
524
|
+
setAddError(`\u8FDC\u7A0B "${remoteName}" \u5DF2\u5B58\u5728`);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
setAddError(null);
|
|
528
|
+
setPhase("adding");
|
|
529
|
+
try {
|
|
530
|
+
await addRemote(remoteName, customUrl);
|
|
531
|
+
reload();
|
|
532
|
+
onSelect(remoteName);
|
|
533
|
+
} catch (err) {
|
|
534
|
+
setAddError(err.message);
|
|
535
|
+
setPhase("input-name");
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
)
|
|
540
|
+
] })
|
|
541
|
+
] });
|
|
542
|
+
}
|
|
543
|
+
const options = [
|
|
544
|
+
...(remotes || []).map((r) => ({
|
|
545
|
+
label: `${r.name} ${r.fetchUrl}`,
|
|
546
|
+
value: r.name
|
|
547
|
+
})),
|
|
548
|
+
{
|
|
549
|
+
label: "+ \u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93...",
|
|
550
|
+
value: "__add_custom__"
|
|
551
|
+
}
|
|
552
|
+
];
|
|
553
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
554
|
+
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u9009\u62E9\u8FDC\u7A0B\u4ED3\u5E93" }),
|
|
555
|
+
/* @__PURE__ */ jsx4(
|
|
556
|
+
Select,
|
|
557
|
+
{
|
|
558
|
+
options,
|
|
559
|
+
onChange: (value) => {
|
|
560
|
+
if (value === "__add_custom__") {
|
|
561
|
+
setPhase("input-url");
|
|
562
|
+
} else {
|
|
563
|
+
onSelect(value);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
)
|
|
194
568
|
] });
|
|
195
569
|
}
|
|
196
570
|
|
|
197
571
|
// src/components/branch-select.tsx
|
|
198
|
-
import { useState as
|
|
199
|
-
import { Box as
|
|
200
|
-
import { Select as Select2, Spinner as
|
|
201
|
-
import { jsx as
|
|
202
|
-
function BranchSelect({ remote, onSelect }) {
|
|
572
|
+
import { useState as useState4, useMemo } from "react";
|
|
573
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
574
|
+
import { Select as Select2, Spinner as Spinner3, TextInput as TextInput2 } from "@inkjs/ui";
|
|
575
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
576
|
+
function BranchSelect({ remote, onSelect, onBack }) {
|
|
203
577
|
const { data: branches, loading, error } = useBranches(remote);
|
|
204
|
-
const [filter, setFilter] =
|
|
578
|
+
const [filter, setFilter] = useState4("");
|
|
579
|
+
useInput4((_input, key) => {
|
|
580
|
+
if (key.escape) onBack?.();
|
|
581
|
+
});
|
|
205
582
|
const filteredOptions = useMemo(() => {
|
|
206
583
|
if (!branches) return [];
|
|
207
584
|
const filtered = filter ? branches.filter((b) => b.toLowerCase().includes(filter.toLowerCase())) : branches;
|
|
208
585
|
return filtered.map((b) => ({ label: b, value: b }));
|
|
209
586
|
}, [branches, filter]);
|
|
210
587
|
if (loading) {
|
|
211
|
-
return /* @__PURE__ */
|
|
588
|
+
return /* @__PURE__ */ jsx5(Spinner3, { label: `\u83B7\u53D6 ${remote} \u7684\u5206\u652F\u5217\u8868...` });
|
|
212
589
|
}
|
|
213
590
|
if (error) {
|
|
214
|
-
return /* @__PURE__ */
|
|
215
|
-
"\u83B7\u53D6\u5206\u652F\u5217\u8868\u5931\u8D25: ",
|
|
591
|
+
return /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
592
|
+
"\u2716 \u83B7\u53D6\u5206\u652F\u5217\u8868\u5931\u8D25: ",
|
|
216
593
|
error
|
|
217
594
|
] });
|
|
218
595
|
}
|
|
219
596
|
if (!branches || branches.length === 0) {
|
|
220
|
-
return /* @__PURE__ */
|
|
597
|
+
return /* @__PURE__ */ jsx5(Text5, { color: "red", children: "\u2716 \u672A\u627E\u5230\u8FDC\u7A0B\u5206\u652F" });
|
|
221
598
|
}
|
|
222
|
-
return /* @__PURE__ */
|
|
223
|
-
/* @__PURE__ */
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
/* @__PURE__ */ jsxs2(Box2, { children: [
|
|
229
|
-
/* @__PURE__ */ jsx2(Text2, { color: "gray", children: "\u641C\u7D22: " }),
|
|
230
|
-
/* @__PURE__ */ jsx2(
|
|
231
|
-
TextInput,
|
|
599
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 1, children: [
|
|
600
|
+
/* @__PURE__ */ jsx5(SectionHeader, { title: `\u9009\u62E9\u5206\u652F`, subtitle: `${remote} \xB7 ${branches.length} \u4E2A\u5206\u652F` }),
|
|
601
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
602
|
+
/* @__PURE__ */ jsx5(Text5, { color: "gray", children: "/ " }),
|
|
603
|
+
/* @__PURE__ */ jsx5(
|
|
604
|
+
TextInput2,
|
|
232
605
|
{
|
|
233
|
-
placeholder: "\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4
|
|
606
|
+
placeholder: "\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4...",
|
|
234
607
|
onChange: setFilter
|
|
235
608
|
}
|
|
236
|
-
)
|
|
609
|
+
),
|
|
610
|
+
filter && /* @__PURE__ */ jsxs5(Text5, { color: "gray", dimColor: true, children: [
|
|
611
|
+
" \xB7 \u5339\u914D ",
|
|
612
|
+
filteredOptions.length
|
|
613
|
+
] })
|
|
237
614
|
] }),
|
|
238
|
-
/* @__PURE__ */
|
|
239
|
-
"\u5171 ",
|
|
240
|
-
branches.length,
|
|
241
|
-
" \u4E2A\u5206\u652F",
|
|
242
|
-
filter ? `\uFF0C\u5339\u914D ${filteredOptions.length} \u4E2A` : ""
|
|
243
|
-
] }),
|
|
244
|
-
filteredOptions.length > 0 ? /* @__PURE__ */ jsx2(Select2, { options: filteredOptions, onChange: onSelect }) : /* @__PURE__ */ jsx2(Text2, { color: "yellow", children: "\u65E0\u5339\u914D\u5206\u652F" })
|
|
615
|
+
filteredOptions.length > 0 ? /* @__PURE__ */ jsx5(Select2, { options: filteredOptions, onChange: onSelect }) : /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B2 \u65E0\u5339\u914D\u5206\u652F" })
|
|
245
616
|
] });
|
|
246
617
|
}
|
|
247
618
|
|
|
248
619
|
// src/components/commit-list.tsx
|
|
249
|
-
import { useState as
|
|
250
|
-
import { Box as
|
|
251
|
-
import {
|
|
252
|
-
import { jsx as
|
|
253
|
-
function CommitList({ remote, branch, onSelect }) {
|
|
620
|
+
import { useState as useState5, useMemo as useMemo2, useRef } from "react";
|
|
621
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
622
|
+
import { Spinner as Spinner4 } from "@inkjs/ui";
|
|
623
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
624
|
+
function CommitList({ remote, branch, onSelect, onBack }) {
|
|
254
625
|
const { data: commits, loading, error } = useCommits(remote, branch, 30);
|
|
255
|
-
const [
|
|
256
|
-
const
|
|
626
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
627
|
+
const [selectedHashes, setSelectedHashes] = useState5(/* @__PURE__ */ new Set());
|
|
628
|
+
const [shiftMode, setShiftMode] = useState5(false);
|
|
629
|
+
const anchorIndexRef = useRef(null);
|
|
630
|
+
const selectedKey = useMemo2(() => Array.from(selectedHashes).sort().join(","), [selectedHashes]);
|
|
631
|
+
const selectedArray = useMemo2(() => Array.from(selectedHashes), [selectedKey]);
|
|
632
|
+
const { stat, loading: statLoading } = useCommitStat(selectedArray);
|
|
633
|
+
const toggleCurrent = () => {
|
|
634
|
+
if (!commits || commits.length === 0) return;
|
|
635
|
+
const hash = commits[selectedIndex].hash;
|
|
636
|
+
setSelectedHashes((prev) => {
|
|
637
|
+
const next = new Set(prev);
|
|
638
|
+
if (next.has(hash)) {
|
|
639
|
+
next.delete(hash);
|
|
640
|
+
anchorIndexRef.current = null;
|
|
641
|
+
} else {
|
|
642
|
+
next.add(hash);
|
|
643
|
+
anchorIndexRef.current = selectedIndex;
|
|
644
|
+
}
|
|
645
|
+
return next;
|
|
646
|
+
});
|
|
647
|
+
};
|
|
648
|
+
const selectRange = (anchor, current) => {
|
|
649
|
+
if (!commits) return;
|
|
650
|
+
const start = Math.min(anchor, current);
|
|
651
|
+
const end = Math.max(anchor, current);
|
|
652
|
+
setSelectedHashes((prev) => {
|
|
653
|
+
const next = new Set(prev);
|
|
654
|
+
for (let i = start; i <= end; i++) {
|
|
655
|
+
next.add(commits[i].hash);
|
|
656
|
+
}
|
|
657
|
+
return next;
|
|
658
|
+
});
|
|
659
|
+
};
|
|
660
|
+
const toggleAll = () => {
|
|
661
|
+
if (!commits || commits.length === 0) return;
|
|
662
|
+
setSelectedHashes((prev) => {
|
|
663
|
+
if (prev.size === commits.length) {
|
|
664
|
+
anchorIndexRef.current = null;
|
|
665
|
+
return /* @__PURE__ */ new Set();
|
|
666
|
+
}
|
|
667
|
+
return new Set(commits.map((c) => c.hash));
|
|
668
|
+
});
|
|
669
|
+
};
|
|
670
|
+
const invertSelection = () => {
|
|
671
|
+
if (!commits || commits.length === 0) return;
|
|
672
|
+
setSelectedHashes((prev) => {
|
|
673
|
+
const next = /* @__PURE__ */ new Set();
|
|
674
|
+
for (const c of commits) {
|
|
675
|
+
if (!prev.has(c.hash)) next.add(c.hash);
|
|
676
|
+
}
|
|
677
|
+
return next;
|
|
678
|
+
});
|
|
679
|
+
};
|
|
680
|
+
const selectToCurrent = () => {
|
|
681
|
+
if (!commits || commits.length === 0) return;
|
|
682
|
+
setSelectedHashes((prev) => {
|
|
683
|
+
const next = new Set(prev);
|
|
684
|
+
for (let i = 0; i <= selectedIndex; i++) {
|
|
685
|
+
next.add(commits[i].hash);
|
|
686
|
+
}
|
|
687
|
+
return next;
|
|
688
|
+
});
|
|
689
|
+
};
|
|
690
|
+
useInput5((input, key) => {
|
|
691
|
+
if (!commits || commits.length === 0) return;
|
|
692
|
+
if (key.shift) {
|
|
693
|
+
if (!shiftMode) {
|
|
694
|
+
setShiftMode(true);
|
|
695
|
+
if (anchorIndexRef.current === null) {
|
|
696
|
+
anchorIndexRef.current = selectedIndex;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
if (key.upArrow) {
|
|
700
|
+
const newIndex = Math.max(0, selectedIndex - 1);
|
|
701
|
+
setSelectedIndex(newIndex);
|
|
702
|
+
selectRange(anchorIndexRef.current, newIndex);
|
|
703
|
+
} else if (key.downArrow) {
|
|
704
|
+
const newIndex = Math.min(commits.length - 1, selectedIndex + 1);
|
|
705
|
+
setSelectedIndex(newIndex);
|
|
706
|
+
selectRange(anchorIndexRef.current, newIndex);
|
|
707
|
+
} else if (input === " ") {
|
|
708
|
+
if (anchorIndexRef.current !== null) {
|
|
709
|
+
selectRange(anchorIndexRef.current, selectedIndex);
|
|
710
|
+
} else {
|
|
711
|
+
toggleCurrent();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
if (shiftMode) setShiftMode(false);
|
|
717
|
+
if (key.upArrow) {
|
|
718
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
719
|
+
} else if (key.downArrow) {
|
|
720
|
+
setSelectedIndex((prev) => Math.min(commits.length - 1, prev + 1));
|
|
721
|
+
} else if (input === " ") {
|
|
722
|
+
toggleCurrent();
|
|
723
|
+
} else if (input === "a" || input === "A") {
|
|
724
|
+
toggleAll();
|
|
725
|
+
} else if (input === "i" || input === "I") {
|
|
726
|
+
invertSelection();
|
|
727
|
+
} else if (input === "r" || input === "R") {
|
|
728
|
+
selectToCurrent();
|
|
729
|
+
} else if (key.escape) {
|
|
730
|
+
onBack?.();
|
|
731
|
+
} else if (key.return) {
|
|
732
|
+
if (selectedHashes.size > 0) {
|
|
733
|
+
onSelect(Array.from(selectedHashes), commits);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
});
|
|
257
737
|
if (loading) {
|
|
258
|
-
return /* @__PURE__ */
|
|
738
|
+
return /* @__PURE__ */ jsx6(Spinner4, { label: `\u83B7\u53D6 ${remote}/${branch} \u7684 commit \u5217\u8868...` });
|
|
259
739
|
}
|
|
260
740
|
if (error) {
|
|
261
|
-
return /* @__PURE__ */
|
|
262
|
-
"\u83B7\u53D6 commit \u5217\u8868\u5931\u8D25: ",
|
|
741
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: "red", children: [
|
|
742
|
+
"\u2716 \u83B7\u53D6 commit \u5217\u8868\u5931\u8D25: ",
|
|
263
743
|
error
|
|
264
744
|
] });
|
|
265
745
|
}
|
|
266
746
|
if (!commits || commits.length === 0) {
|
|
267
|
-
return /* @__PURE__ */
|
|
747
|
+
return /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "\u25B2 \u8BE5\u5206\u652F\u6CA1\u6709 commit" });
|
|
268
748
|
}
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
/* @__PURE__ */
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
{
|
|
286
|
-
options,
|
|
287
|
-
onChange: setSelectedHashes,
|
|
288
|
-
onSubmit: (hashes) => {
|
|
289
|
-
if (hashes.length > 0) {
|
|
290
|
-
onSelect(hashes, commits);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
),
|
|
295
|
-
selectedHashes.length > 0 && /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", marginTop: 1, borderStyle: "single", borderColor: "gray", paddingX: 1, children: [
|
|
296
|
-
/* @__PURE__ */ jsxs3(Text3, { bold: true, color: "yellow", children: [
|
|
749
|
+
const visibleCount = 10;
|
|
750
|
+
const startIdx = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), commits.length - visibleCount));
|
|
751
|
+
const visibleCommits = commits.slice(startIdx, startIdx + visibleCount);
|
|
752
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", gap: 1, children: [
|
|
753
|
+
/* @__PURE__ */ jsx6(SectionHeader, { title: "\u9009\u62E9\u8981\u540C\u6B65\u7684 commit" }),
|
|
754
|
+
/* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
|
|
755
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
756
|
+
remote,
|
|
757
|
+
"/",
|
|
758
|
+
branch
|
|
759
|
+
] }),
|
|
760
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
761
|
+
commits.length,
|
|
762
|
+
" commits"
|
|
763
|
+
] }),
|
|
764
|
+
/* @__PURE__ */ jsxs6(Text6, { color: selectedHashes.size > 0 ? "cyan" : "gray", bold: selectedHashes.size > 0, children: [
|
|
297
765
|
"\u5DF2\u9009 ",
|
|
298
|
-
selectedHashes.
|
|
299
|
-
" \u4E2A commit \u2014 diff --stat \u9884\u89C8:"
|
|
766
|
+
selectedHashes.size
|
|
300
767
|
] }),
|
|
301
|
-
|
|
302
|
-
] })
|
|
768
|
+
shiftMode && /* @__PURE__ */ jsx6(Text6, { color: "yellow", bold: true, children: "SHIFT" })
|
|
769
|
+
] }),
|
|
770
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
771
|
+
startIdx > 0 && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
772
|
+
" \u2191 ",
|
|
773
|
+
startIdx,
|
|
774
|
+
" more"
|
|
775
|
+
] }),
|
|
776
|
+
visibleCommits.map((c, i) => {
|
|
777
|
+
const actualIdx = startIdx + i;
|
|
778
|
+
const isSelected = selectedHashes.has(c.hash);
|
|
779
|
+
const isCursor = actualIdx === selectedIndex;
|
|
780
|
+
const isAnchor = actualIdx === anchorIndexRef.current;
|
|
781
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
782
|
+
/* @__PURE__ */ jsxs6(Text6, { backgroundColor: isCursor ? "blue" : void 0, color: isSelected ? "green" : "white", children: [
|
|
783
|
+
isCursor ? "\u25B8 " : " ",
|
|
784
|
+
isAnchor ? "\u2693" : isSelected ? "\u25CF" : "\u25CB",
|
|
785
|
+
" "
|
|
786
|
+
] }),
|
|
787
|
+
/* @__PURE__ */ jsx6(Text6, { backgroundColor: isCursor ? "blue" : void 0, color: "yellow", children: c.shortHash }),
|
|
788
|
+
/* @__PURE__ */ jsxs6(Text6, { backgroundColor: isCursor ? "blue" : void 0, color: isSelected ? "green" : "white", children: [
|
|
789
|
+
" ",
|
|
790
|
+
c.message
|
|
791
|
+
] }),
|
|
792
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
793
|
+
" ",
|
|
794
|
+
c.author,
|
|
795
|
+
" \xB7 ",
|
|
796
|
+
c.date
|
|
797
|
+
] })
|
|
798
|
+
] }, c.hash);
|
|
799
|
+
}),
|
|
800
|
+
startIdx + visibleCount < commits.length && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
801
|
+
" \u2193 ",
|
|
802
|
+
commits.length - startIdx - visibleCount,
|
|
803
|
+
" more"
|
|
804
|
+
] })
|
|
805
|
+
] }),
|
|
806
|
+
/* @__PURE__ */ jsx6(KeyHints, { hints: [
|
|
807
|
+
{ key: "\u2191\u2193", label: "\u5BFC\u822A" },
|
|
808
|
+
{ key: "Space", label: "\u9009\u62E9" },
|
|
809
|
+
{ key: "a", label: "\u5168\u9009" },
|
|
810
|
+
{ key: "i", label: "\u53CD\u9009" },
|
|
811
|
+
{ key: "r", label: "\u9009\u81F3\u5F00\u5934" },
|
|
812
|
+
{ key: "Shift+\u2191\u2193", label: "\u8FDE\u9009" },
|
|
813
|
+
{ key: "Enter", label: "\u786E\u8BA4" },
|
|
814
|
+
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
815
|
+
] }),
|
|
816
|
+
selectedHashes.size > 0 && /* @__PURE__ */ jsx6(StatusPanel, { type: "info", title: `\u5DF2\u9009 ${selectedHashes.size} \u4E2A commit \xB7 diff --stat`, children: statLoading ? /* @__PURE__ */ jsx6(Spinner4, { label: "\u52A0\u8F7D\u4E2D..." }) : /* @__PURE__ */ jsx6(Text6, { color: "gray", children: stat || "(\u65E0\u53D8\u66F4)" }) })
|
|
303
817
|
] });
|
|
304
818
|
}
|
|
305
819
|
|
|
306
820
|
// src/components/confirm-panel.tsx
|
|
307
|
-
import { Box as
|
|
308
|
-
import { jsx as
|
|
309
|
-
function ConfirmPanel({ commits, selectedHashes, onConfirm, onCancel }) {
|
|
310
|
-
|
|
311
|
-
if (
|
|
821
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
822
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
823
|
+
function ConfirmPanel({ commits, selectedHashes, hasMerge, useMainline, onToggleMainline, onConfirm, onCancel }) {
|
|
824
|
+
useInput6((input, key) => {
|
|
825
|
+
if (key.escape) {
|
|
826
|
+
onCancel();
|
|
827
|
+
} else if (input === "y" || input === "Y") {
|
|
312
828
|
onConfirm();
|
|
313
829
|
} else if (input === "n" || input === "N" || input === "q") {
|
|
314
830
|
onCancel();
|
|
831
|
+
} else if (hasMerge && (input === "m" || input === "M")) {
|
|
832
|
+
onToggleMainline();
|
|
315
833
|
}
|
|
316
834
|
});
|
|
317
835
|
const selectedCommits = selectedHashes.map((hash) => commits.find((c) => c.hash === hash)).filter(Boolean);
|
|
318
|
-
return /* @__PURE__ */
|
|
319
|
-
/* @__PURE__ */
|
|
320
|
-
/* @__PURE__ */
|
|
321
|
-
/* @__PURE__ */
|
|
322
|
-
"
|
|
323
|
-
|
|
324
|
-
" \u4E2A commit:"
|
|
836
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
|
|
837
|
+
/* @__PURE__ */ jsx7(SectionHeader, { title: "\u786E\u8BA4\u6267\u884C" }),
|
|
838
|
+
/* @__PURE__ */ jsx7(StatusPanel, { type: "info", title: `cherry-pick --no-commit \xB7 ${selectedCommits.length} \u4E2A commit`, children: selectedCommits.map((c) => /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
839
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
840
|
+
" ",
|
|
841
|
+
c.shortHash
|
|
325
842
|
] }),
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
] })
|
|
340
|
-
|
|
843
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
844
|
+
" ",
|
|
845
|
+
c.message
|
|
846
|
+
] }),
|
|
847
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "gray", dimColor: true, children: [
|
|
848
|
+
" ",
|
|
849
|
+
c.author
|
|
850
|
+
] })
|
|
851
|
+
] }, c.hash)) }),
|
|
852
|
+
hasMerge && /* @__PURE__ */ jsxs7(StatusPanel, { type: "warn", title: "\u68C0\u6D4B\u5230 Merge Commit", children: [
|
|
853
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: " Cherry-pick \u5408\u5E76\u63D0\u4EA4\u9700\u8981\u6307\u5B9A\u7236\u8282\u70B9 (-m 1)" }),
|
|
854
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
855
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
856
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "[m]" }),
|
|
857
|
+
/* @__PURE__ */ jsx7(Text7, { children: " \u5207\u6362 -m 1: " }),
|
|
858
|
+
useMainline ? /* @__PURE__ */ jsx7(Text7, { color: "green", bold: true, children: "\u5DF2\u542F\u7528" }) : /* @__PURE__ */ jsx7(Text7, { color: "gray", children: "\u672A\u542F\u7528" })
|
|
859
|
+
] })
|
|
341
860
|
] }),
|
|
342
|
-
/* @__PURE__ */
|
|
343
|
-
/* @__PURE__ */
|
|
344
|
-
/* @__PURE__ */
|
|
861
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
862
|
+
/* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "\u25B2 " }),
|
|
863
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: "--no-commit \u6A21\u5F0F\uFF0C\u6539\u52A8\u5C06\u6682\u5B58\u5230\u5DE5\u4F5C\u533A\uFF0C\u9700\u624B\u52A8 commit" })
|
|
345
864
|
] }),
|
|
346
|
-
/* @__PURE__ */
|
|
347
|
-
/* @__PURE__ */
|
|
348
|
-
/* @__PURE__ */
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
865
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
866
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: "\u786E\u8BA4\u6267\u884C? " }),
|
|
867
|
+
/* @__PURE__ */ jsx7(InlineKeys, { hints: [
|
|
868
|
+
{ key: "y", label: "\u786E\u8BA4" },
|
|
869
|
+
{ key: "n", label: "\u53D6\u6D88" },
|
|
870
|
+
...hasMerge ? [{ key: "m", label: "\u5207\u6362 -m 1" }] : [],
|
|
871
|
+
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
872
|
+
] })
|
|
352
873
|
] })
|
|
353
874
|
] });
|
|
354
875
|
}
|
|
355
876
|
|
|
356
877
|
// src/components/result-panel.tsx
|
|
357
|
-
import { useState as
|
|
358
|
-
import { Box as
|
|
359
|
-
import { Spinner as
|
|
360
|
-
import { jsx as
|
|
361
|
-
function ResultPanel({ selectedHashes, onDone }) {
|
|
362
|
-
const [phase, setPhase] =
|
|
363
|
-
const [result, setResult] =
|
|
364
|
-
const [stagedStat, setStagedStat] =
|
|
878
|
+
import { useState as useState6, useEffect as useEffect3 } from "react";
|
|
879
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
880
|
+
import { Spinner as Spinner5 } from "@inkjs/ui";
|
|
881
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
882
|
+
function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, onDone }) {
|
|
883
|
+
const [phase, setPhase] = useState6("executing");
|
|
884
|
+
const [result, setResult] = useState6(null);
|
|
885
|
+
const [stagedStat, setStagedStat] = useState6("");
|
|
886
|
+
const [stashRestored, setStashRestored] = useState6(null);
|
|
887
|
+
const tryRestoreStash = async () => {
|
|
888
|
+
if (!stashed) return true;
|
|
889
|
+
setPhase("restoring");
|
|
890
|
+
const ok = await stashPop();
|
|
891
|
+
setStashRestored(ok);
|
|
892
|
+
if (ok) onStashRestored();
|
|
893
|
+
return ok;
|
|
894
|
+
};
|
|
365
895
|
useEffect3(() => {
|
|
366
896
|
async function run() {
|
|
367
|
-
const res = await cherryPick(selectedHashes);
|
|
897
|
+
const res = await cherryPick(selectedHashes, useMainline);
|
|
368
898
|
setResult(res);
|
|
369
899
|
if (res.success) {
|
|
370
900
|
const stat = await getStagedStat();
|
|
371
901
|
setStagedStat(stat);
|
|
902
|
+
await tryRestoreStash();
|
|
372
903
|
setPhase("done");
|
|
373
904
|
} else {
|
|
905
|
+
await tryRestoreStash();
|
|
374
906
|
setPhase("error");
|
|
375
907
|
}
|
|
376
908
|
}
|
|
377
909
|
run();
|
|
378
910
|
}, []);
|
|
379
911
|
if (phase === "executing") {
|
|
380
|
-
return /* @__PURE__ */
|
|
912
|
+
return /* @__PURE__ */ jsx8(Spinner5, { label: `cherry-pick --no-commit (${selectedHashes.length} \u4E2A commit)...` });
|
|
913
|
+
}
|
|
914
|
+
if (phase === "restoring") {
|
|
915
|
+
return /* @__PURE__ */ jsx8(Spinner5, { label: "\u6062\u590D\u5DE5\u4F5C\u533A (git stash pop)..." });
|
|
381
916
|
}
|
|
382
917
|
if (phase === "error" && result) {
|
|
383
|
-
return /* @__PURE__ */
|
|
384
|
-
/* @__PURE__ */
|
|
385
|
-
result.conflictFiles && result.conflictFiles.length > 0 && /* @__PURE__ */
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
918
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
|
|
919
|
+
/* @__PURE__ */ jsx8(SectionHeader, { title: "Cherry-pick \u9047\u5230\u51B2\u7A81" }),
|
|
920
|
+
result.conflictFiles && result.conflictFiles.length > 0 && /* @__PURE__ */ jsx8(StatusPanel, { type: "error", title: "\u51B2\u7A81\u6587\u4EF6", children: result.conflictFiles.map((f) => /* @__PURE__ */ jsxs8(Text8, { color: "red", children: [
|
|
921
|
+
" ",
|
|
922
|
+
f
|
|
923
|
+
] }, f)) }),
|
|
924
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
925
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 \u624B\u52A8\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add \u548C git commit" }),
|
|
926
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "\u25B8 \u6216\u6267\u884C git cherry-pick --abort \u653E\u5F03\u64CD\u4F5C" })
|
|
391
927
|
] }),
|
|
392
|
-
/* @__PURE__ */
|
|
393
|
-
/* @__PURE__ */
|
|
928
|
+
stashed && stashRestored === false && /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 stash \u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" }),
|
|
929
|
+
stashed && stashRestored === true && /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)" })
|
|
394
930
|
] });
|
|
395
931
|
}
|
|
396
|
-
return /* @__PURE__ */
|
|
397
|
-
/* @__PURE__ */
|
|
398
|
-
/* @__PURE__ */
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
932
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
|
|
933
|
+
/* @__PURE__ */ jsx8(SectionHeader, { title: "\u540C\u6B65\u5B8C\u6210" }),
|
|
934
|
+
/* @__PURE__ */ jsx8(StatusPanel, { type: "success", title: "\u6682\u5B58\u533A\u53D8\u66F4 (git diff --cached --stat)", children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: stagedStat || "(\u65E0\u53D8\u66F4)" }) }),
|
|
935
|
+
stashed && (stashRestored ? /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)" }) : /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 stash pop \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" })),
|
|
936
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
937
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)" }),
|
|
938
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: " \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:" }),
|
|
939
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
940
|
+
" git diff --cached ",
|
|
941
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u67E5\u770B\u8BE6\u7EC6 diff" })
|
|
942
|
+
] }),
|
|
943
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
944
|
+
' git commit -m "sync: ..." ',
|
|
945
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u63D0\u4EA4" })
|
|
946
|
+
] }),
|
|
947
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
948
|
+
" git reset HEAD ",
|
|
949
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u6216\u653E\u5F03" })
|
|
950
|
+
] })
|
|
951
|
+
] })
|
|
407
952
|
] });
|
|
408
953
|
}
|
|
409
954
|
|
|
410
955
|
// src/app.tsx
|
|
411
|
-
import {
|
|
956
|
+
import { execSync } from "child_process";
|
|
957
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
958
|
+
var STEP_NUMBER = {
|
|
959
|
+
checking: 0,
|
|
960
|
+
"stash-recovery": 0,
|
|
961
|
+
"stash-prompt": 0,
|
|
962
|
+
remote: 1,
|
|
963
|
+
branch: 2,
|
|
964
|
+
commits: 3,
|
|
965
|
+
confirm: 4,
|
|
966
|
+
result: 5
|
|
967
|
+
};
|
|
968
|
+
var STEP_DEBOUNCE = 100;
|
|
412
969
|
function App() {
|
|
413
|
-
const
|
|
414
|
-
const [
|
|
415
|
-
const [
|
|
416
|
-
const [
|
|
417
|
-
const [
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
970
|
+
const { exit } = useApp();
|
|
971
|
+
const [step, setStepRaw] = useState7("checking");
|
|
972
|
+
const [inputReady, setInputReady] = useState7(true);
|
|
973
|
+
const [remote, setRemote] = useState7("");
|
|
974
|
+
const [branch, setBranch] = useState7("");
|
|
975
|
+
const [selectedHashes, setSelectedHashes] = useState7([]);
|
|
976
|
+
const [commits, setCommits] = useState7([]);
|
|
977
|
+
const [hasMerge, setHasMerge] = useState7(false);
|
|
978
|
+
const [useMainline, setUseMainline] = useState7(false);
|
|
979
|
+
const [stashed, setStashed] = useState7(false);
|
|
980
|
+
const [guardTimestamp, setGuardTimestamp] = useState7();
|
|
981
|
+
const stashedRef = useRef2(false);
|
|
982
|
+
const stashRestoredRef = useRef2(false);
|
|
983
|
+
const mountedRef = useRef2(true);
|
|
984
|
+
const debounceTimer = useRef2(null);
|
|
985
|
+
const setStep = useCallback2((newStep) => {
|
|
986
|
+
setInputReady(false);
|
|
987
|
+
setStepRaw(newStep);
|
|
988
|
+
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
989
|
+
debounceTimer.current = setTimeout(() => {
|
|
990
|
+
if (mountedRef.current) setInputReady(true);
|
|
991
|
+
}, STEP_DEBOUNCE);
|
|
992
|
+
}, []);
|
|
993
|
+
const restoreStashSync = useCallback2(() => {
|
|
994
|
+
if (stashedRef.current && !stashRestoredRef.current) {
|
|
995
|
+
try {
|
|
996
|
+
execSync("git stash pop", { stdio: "ignore" });
|
|
997
|
+
stashRestoredRef.current = true;
|
|
998
|
+
removeStashGuardSync();
|
|
999
|
+
} catch {
|
|
1000
|
+
try {
|
|
1001
|
+
process.stderr.write("\n\u26A0 stash \u81EA\u52A8\u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: git stash pop\n");
|
|
1002
|
+
} catch {
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
}, []);
|
|
1007
|
+
const markStashRestored = useCallback2(() => {
|
|
1008
|
+
stashRestoredRef.current = true;
|
|
1009
|
+
removeStashGuard();
|
|
1010
|
+
}, []);
|
|
1011
|
+
useEffect4(() => {
|
|
1012
|
+
mountedRef.current = true;
|
|
1013
|
+
async function check() {
|
|
1014
|
+
const guard = await checkStashGuard();
|
|
1015
|
+
if (!mountedRef.current) return;
|
|
1016
|
+
if (guard.exists) {
|
|
1017
|
+
setGuardTimestamp(guard.timestamp);
|
|
1018
|
+
setStep("stash-recovery");
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
const clean = await isWorkingDirClean();
|
|
1022
|
+
if (!mountedRef.current) return;
|
|
1023
|
+
setStep(clean ? "remote" : "stash-prompt");
|
|
1024
|
+
}
|
|
1025
|
+
check();
|
|
1026
|
+
const onSignal = () => {
|
|
1027
|
+
restoreStashSync();
|
|
1028
|
+
process.exit(0);
|
|
1029
|
+
};
|
|
1030
|
+
process.on("SIGINT", onSignal);
|
|
1031
|
+
process.on("SIGTERM", onSignal);
|
|
1032
|
+
process.on("SIGHUP", onSignal);
|
|
1033
|
+
return () => {
|
|
1034
|
+
mountedRef.current = false;
|
|
1035
|
+
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
1036
|
+
process.off("SIGINT", onSignal);
|
|
1037
|
+
process.off("SIGTERM", onSignal);
|
|
1038
|
+
process.off("SIGHUP", onSignal);
|
|
1039
|
+
};
|
|
1040
|
+
}, [restoreStashSync, setStep]);
|
|
1041
|
+
const doStash = async () => {
|
|
1042
|
+
const ok = await stash();
|
|
1043
|
+
if (ok) {
|
|
1044
|
+
setStashed(true);
|
|
1045
|
+
stashedRef.current = true;
|
|
1046
|
+
await writeStashGuard();
|
|
1047
|
+
}
|
|
1048
|
+
if (mountedRef.current) setStep("remote");
|
|
1049
|
+
};
|
|
1050
|
+
const doStashRecover = async () => {
|
|
1051
|
+
const entry = await findStashEntry();
|
|
1052
|
+
if (entry) {
|
|
1053
|
+
await stashPop();
|
|
1054
|
+
}
|
|
1055
|
+
await removeStashGuard();
|
|
1056
|
+
if (!mountedRef.current) return;
|
|
1057
|
+
const clean = await isWorkingDirClean();
|
|
1058
|
+
if (mountedRef.current) setStep(clean ? "remote" : "stash-prompt");
|
|
1059
|
+
};
|
|
1060
|
+
const skipStashRecover = async () => {
|
|
1061
|
+
await removeStashGuard();
|
|
1062
|
+
if (!mountedRef.current) return;
|
|
1063
|
+
const clean = await isWorkingDirClean();
|
|
1064
|
+
if (mountedRef.current) setStep(clean ? "remote" : "stash-prompt");
|
|
1065
|
+
};
|
|
1066
|
+
const goBack = useCallback2((fromStep) => {
|
|
1067
|
+
const backMap = {
|
|
1068
|
+
branch: "remote",
|
|
1069
|
+
commits: "branch",
|
|
1070
|
+
confirm: "commits"
|
|
1071
|
+
};
|
|
1072
|
+
const prev = backMap[fromStep];
|
|
1073
|
+
if (prev) {
|
|
1074
|
+
setStep(prev);
|
|
1075
|
+
} else {
|
|
1076
|
+
restoreStashSync();
|
|
1077
|
+
exit();
|
|
1078
|
+
}
|
|
1079
|
+
}, [setStep, restoreStashSync, exit]);
|
|
1080
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1081
|
+
/* @__PURE__ */ jsx9(AppHeader, { step: STEP_NUMBER[step], stashed }),
|
|
1082
|
+
step === "checking" && /* @__PURE__ */ jsx9(Spinner6, { label: "\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001..." }),
|
|
1083
|
+
step === "stash-recovery" && inputReady && /* @__PURE__ */ jsx9(
|
|
1084
|
+
StashRecovery,
|
|
1085
|
+
{
|
|
1086
|
+
timestamp: guardTimestamp,
|
|
1087
|
+
onRecover: doStashRecover,
|
|
1088
|
+
onSkip: skipStashRecover
|
|
1089
|
+
}
|
|
1090
|
+
),
|
|
1091
|
+
step === "stash-prompt" && inputReady && /* @__PURE__ */ jsx9(
|
|
1092
|
+
StashPrompt,
|
|
1093
|
+
{
|
|
1094
|
+
onConfirm: doStash,
|
|
1095
|
+
onSkip: () => setStep("remote")
|
|
1096
|
+
}
|
|
1097
|
+
),
|
|
1098
|
+
step === "remote" && inputReady && /* @__PURE__ */ jsx9(
|
|
425
1099
|
RemoteSelect,
|
|
426
1100
|
{
|
|
427
1101
|
onSelect: (r) => {
|
|
428
1102
|
setRemote(r);
|
|
429
1103
|
setStep("branch");
|
|
430
|
-
}
|
|
1104
|
+
},
|
|
1105
|
+
onBack: () => goBack("remote")
|
|
431
1106
|
}
|
|
432
1107
|
),
|
|
433
|
-
step === "branch" && /* @__PURE__ */
|
|
1108
|
+
step === "branch" && inputReady && /* @__PURE__ */ jsx9(
|
|
434
1109
|
BranchSelect,
|
|
435
1110
|
{
|
|
436
1111
|
remote,
|
|
437
1112
|
onSelect: (b) => {
|
|
438
1113
|
setBranch(b);
|
|
439
1114
|
setStep("commits");
|
|
440
|
-
}
|
|
1115
|
+
},
|
|
1116
|
+
onBack: () => goBack("branch")
|
|
441
1117
|
}
|
|
442
1118
|
),
|
|
443
|
-
step === "commits" && /* @__PURE__ */
|
|
1119
|
+
step === "commits" && inputReady && /* @__PURE__ */ jsx9(
|
|
444
1120
|
CommitList,
|
|
445
1121
|
{
|
|
446
1122
|
remote,
|
|
447
1123
|
branch,
|
|
448
|
-
onSelect: (hashes, loadedCommits) => {
|
|
1124
|
+
onSelect: async (hashes, loadedCommits) => {
|
|
449
1125
|
setSelectedHashes(hashes);
|
|
450
1126
|
setCommits(loadedCommits);
|
|
1127
|
+
const merge = await hasMergeCommits(hashes);
|
|
1128
|
+
setHasMerge(merge);
|
|
451
1129
|
setStep("confirm");
|
|
452
|
-
}
|
|
1130
|
+
},
|
|
1131
|
+
onBack: () => goBack("commits")
|
|
453
1132
|
}
|
|
454
1133
|
),
|
|
455
|
-
step === "confirm" && /* @__PURE__ */
|
|
1134
|
+
step === "confirm" && inputReady && /* @__PURE__ */ jsx9(
|
|
456
1135
|
ConfirmPanel,
|
|
457
1136
|
{
|
|
458
1137
|
commits,
|
|
459
1138
|
selectedHashes,
|
|
1139
|
+
hasMerge,
|
|
1140
|
+
useMainline,
|
|
1141
|
+
onToggleMainline: () => setUseMainline((v) => !v),
|
|
460
1142
|
onConfirm: () => setStep("result"),
|
|
461
|
-
onCancel: () =>
|
|
1143
|
+
onCancel: () => goBack("confirm")
|
|
462
1144
|
}
|
|
463
1145
|
),
|
|
464
|
-
step === "result" && /* @__PURE__ */
|
|
1146
|
+
step === "result" && /* @__PURE__ */ jsx9(
|
|
465
1147
|
ResultPanel,
|
|
466
1148
|
{
|
|
467
1149
|
selectedHashes,
|
|
468
|
-
|
|
1150
|
+
useMainline,
|
|
1151
|
+
stashed,
|
|
1152
|
+
onStashRestored: markStashRestored,
|
|
1153
|
+
onDone: () => {
|
|
1154
|
+
restoreStashSync();
|
|
1155
|
+
exit();
|
|
1156
|
+
}
|
|
469
1157
|
}
|
|
470
1158
|
)
|
|
471
1159
|
] });
|
|
472
1160
|
}
|
|
473
1161
|
|
|
474
1162
|
// src/cli.tsx
|
|
475
|
-
import { jsx as
|
|
1163
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
476
1164
|
var cli = meow(
|
|
477
1165
|
`
|
|
478
1166
|
\u7528\u6CD5
|
|
@@ -487,13 +1175,16 @@ var cli = meow(
|
|
|
487
1175
|
\u4F7F\u7528 cherry-pick --no-commit \u6A21\u5F0F\uFF0C\u540C\u6B65\u540E\u53EF\u5BA1\u67E5\u518D\u63D0\u4EA4\u3002
|
|
488
1176
|
|
|
489
1177
|
\u5FEB\u6377\u952E
|
|
490
|
-
Space
|
|
491
|
-
|
|
492
|
-
\
|
|
493
|
-
|
|
1178
|
+
Space \u9009\u62E9/\u53D6\u6D88 commit
|
|
1179
|
+
Shift+\u2191/\u2193 \u8FDE\u7EED\u9009\u62E9
|
|
1180
|
+
a \u5168\u9009/\u53D6\u6D88\u5168\u9009
|
|
1181
|
+
i \u53CD\u9009
|
|
1182
|
+
r \u9009\u81F3\u5F00\u5934
|
|
1183
|
+
Enter \u786E\u8BA4\u9009\u62E9
|
|
1184
|
+
y/n \u786E\u8BA4/\u53D6\u6D88\u6267\u884C
|
|
494
1185
|
`,
|
|
495
1186
|
{
|
|
496
1187
|
importMeta: import.meta
|
|
497
1188
|
}
|
|
498
1189
|
);
|
|
499
|
-
render(/* @__PURE__ */
|
|
1190
|
+
render(/* @__PURE__ */ jsx10(App, {}));
|