git-sync-tui 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +852 -369
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,17 +1,113 @@
|
|
|
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
|
|
10
|
-
import { Spinner 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";
|
|
11
17
|
|
|
12
|
-
// src/components/
|
|
13
|
-
import
|
|
18
|
+
// src/components/ui.tsx
|
|
19
|
+
import React from "react";
|
|
20
|
+
import { Box, Text } from "ink";
|
|
14
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
|
+
}
|
|
107
|
+
|
|
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";
|
|
15
111
|
function StashPrompt({ onConfirm, onSkip }) {
|
|
16
112
|
useInput((input) => {
|
|
17
113
|
if (input === "y" || input === "Y") {
|
|
@@ -20,31 +116,27 @@ function StashPrompt({ onConfirm, onSkip }) {
|
|
|
20
116
|
onSkip();
|
|
21
117
|
}
|
|
22
118
|
});
|
|
23
|
-
return /* @__PURE__ */
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
|
|
26
|
-
/* @__PURE__ */
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
/* @__PURE__ */ jsx(Text, { children: " \u662F / " }),
|
|
32
|
-
/* @__PURE__ */ jsx(Text, { color: "red", children: "[n]" }),
|
|
33
|
-
/* @__PURE__ */ jsx(Text, { children: " \u5426\uFF0C\u7EE7\u7EED\u64CD\u4F5C" })
|
|
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
|
+
] })
|
|
34
127
|
] })
|
|
35
128
|
] });
|
|
36
129
|
}
|
|
37
130
|
|
|
38
|
-
// src/components/
|
|
39
|
-
import { useState
|
|
40
|
-
import { Box as
|
|
41
|
-
import {
|
|
42
|
-
|
|
43
|
-
// src/hooks/use-git.ts
|
|
44
|
-
import { useState, useEffect, useCallback } from "react";
|
|
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";
|
|
45
135
|
|
|
46
136
|
// src/utils/git.ts
|
|
47
137
|
import simpleGit from "simple-git";
|
|
138
|
+
import { existsSync, writeFileSync, unlinkSync, readFileSync } from "fs";
|
|
139
|
+
import { join } from "path";
|
|
48
140
|
var gitInstance = null;
|
|
49
141
|
function getGit(cwd) {
|
|
50
142
|
if (!gitInstance || cwd) {
|
|
@@ -64,38 +156,53 @@ async function addRemote(name, url) {
|
|
|
64
156
|
const git = getGit();
|
|
65
157
|
await git.addRemote(name, url);
|
|
66
158
|
}
|
|
67
|
-
async function getRemoteBranches(
|
|
159
|
+
async function getRemoteBranches(remote2) {
|
|
68
160
|
const git = getGit();
|
|
161
|
+
let fetchOk = false;
|
|
69
162
|
try {
|
|
70
|
-
await git.fetch(
|
|
163
|
+
await git.fetch(remote2);
|
|
164
|
+
fetchOk = true;
|
|
71
165
|
} catch {
|
|
72
166
|
}
|
|
73
167
|
const result = await git.branch(["-r"]);
|
|
74
|
-
const prefix = `${
|
|
75
|
-
|
|
168
|
+
const prefix = `${remote2}/`;
|
|
169
|
+
const branches = result.all.filter((b) => b.startsWith(prefix) && !b.includes("HEAD")).map((b) => b.replace(prefix, "")).sort();
|
|
170
|
+
if (branches.length > 0) return branches;
|
|
171
|
+
try {
|
|
172
|
+
const lsResult = await git.raw(["ls-remote", "--heads", remote2]);
|
|
173
|
+
if (!lsResult.trim()) return [];
|
|
174
|
+
return lsResult.trim().split("\n").map((line) => line.replace(/^.*refs\/heads\//, "")).filter(Boolean).sort();
|
|
175
|
+
} catch {
|
|
176
|
+
if (!fetchOk) {
|
|
177
|
+
throw new Error(`\u65E0\u6CD5\u8FDE\u63A5\u8FDC\u7A0B\u4ED3\u5E93 '${remote2}'\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u4ED3\u5E93\u5730\u5740`);
|
|
178
|
+
}
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
76
181
|
}
|
|
77
|
-
async function getCommits(
|
|
182
|
+
async function getCommits(remote2, branch2, count2 = 30) {
|
|
78
183
|
const git = getGit();
|
|
79
|
-
const ref = `${
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
message: "%s",
|
|
88
|
-
author: "%an",
|
|
89
|
-
date: "%ar"
|
|
184
|
+
const ref = `${remote2}/${branch2}`;
|
|
185
|
+
try {
|
|
186
|
+
await git.raw(["rev-parse", "--verify", ref]);
|
|
187
|
+
} catch {
|
|
188
|
+
try {
|
|
189
|
+
await git.fetch(remote2, branch2);
|
|
190
|
+
} catch {
|
|
191
|
+
throw new Error(`\u65E0\u6CD5\u83B7\u53D6 ${ref}\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u4ED3\u5E93\u8FDE\u63A5`);
|
|
90
192
|
}
|
|
193
|
+
}
|
|
194
|
+
const result = await git.raw([
|
|
195
|
+
"log",
|
|
196
|
+
ref,
|
|
197
|
+
`--max-count=${count2}`,
|
|
198
|
+
"--format=%H%n%h%n%s%n%an%n%ar%n---"
|
|
199
|
+
]);
|
|
200
|
+
if (!result.trim()) return [];
|
|
201
|
+
const entries = result.trim().split("\n---\n").filter(Boolean);
|
|
202
|
+
return entries.map((block) => {
|
|
203
|
+
const [hash, shortHash, message, author, date] = block.split("\n");
|
|
204
|
+
return { hash, shortHash, message: message || "", author: author || "", date: date || "" };
|
|
91
205
|
});
|
|
92
|
-
return log.all.map((entry) => ({
|
|
93
|
-
hash: entry.hash,
|
|
94
|
-
shortHash: entry.hash.substring(0, 7),
|
|
95
|
-
message: entry.message || "",
|
|
96
|
-
author: entry.author || "",
|
|
97
|
-
date: entry.date || ""
|
|
98
|
-
}));
|
|
99
206
|
}
|
|
100
207
|
async function getMultiCommitStat(hashes) {
|
|
101
208
|
if (hashes.length === 0) return "";
|
|
@@ -183,10 +290,115 @@ async function stashPop() {
|
|
|
183
290
|
return false;
|
|
184
291
|
}
|
|
185
292
|
}
|
|
293
|
+
var STASH_GUARD_FILE = "git-sync-tui-stash-guard";
|
|
294
|
+
async function getGitDir() {
|
|
295
|
+
const git = getGit();
|
|
296
|
+
const dir = await git.revparse(["--git-dir"]);
|
|
297
|
+
return dir.trim();
|
|
298
|
+
}
|
|
299
|
+
async function writeStashGuard() {
|
|
300
|
+
try {
|
|
301
|
+
const gitDir = await getGitDir();
|
|
302
|
+
writeFileSync(join(gitDir, STASH_GUARD_FILE), (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
|
|
303
|
+
} catch {
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
async function removeStashGuard() {
|
|
307
|
+
try {
|
|
308
|
+
const gitDir = await getGitDir();
|
|
309
|
+
const guardPath = join(gitDir, STASH_GUARD_FILE);
|
|
310
|
+
if (existsSync(guardPath)) {
|
|
311
|
+
unlinkSync(guardPath);
|
|
312
|
+
}
|
|
313
|
+
} catch {
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
function removeStashGuardSync() {
|
|
317
|
+
try {
|
|
318
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
319
|
+
const gitDir = String(execSync2("git rev-parse --git-dir", { encoding: "utf-8" })).trim();
|
|
320
|
+
const guardPath = join(gitDir, STASH_GUARD_FILE);
|
|
321
|
+
if (existsSync(guardPath)) {
|
|
322
|
+
unlinkSync(guardPath);
|
|
323
|
+
}
|
|
324
|
+
} catch {
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
async function checkStashGuard() {
|
|
328
|
+
try {
|
|
329
|
+
const gitDir = await getGitDir();
|
|
330
|
+
const guardPath = join(gitDir, STASH_GUARD_FILE);
|
|
331
|
+
if (existsSync(guardPath)) {
|
|
332
|
+
const timestamp = readFileSync(guardPath, "utf-8").trim();
|
|
333
|
+
return { exists: true, timestamp };
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
}
|
|
337
|
+
return { exists: false };
|
|
338
|
+
}
|
|
339
|
+
async function findStashEntry() {
|
|
340
|
+
const git = getGit();
|
|
341
|
+
try {
|
|
342
|
+
const result = await git.stash(["list"]);
|
|
343
|
+
const lines = result.trim().split("\n");
|
|
344
|
+
for (const line of lines) {
|
|
345
|
+
if (line.includes("Auto-stash by git-sync-tui")) {
|
|
346
|
+
return line;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch {
|
|
350
|
+
}
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/components/stash-recovery.tsx
|
|
355
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
356
|
+
function StashRecovery({ timestamp, onRecover, onSkip }) {
|
|
357
|
+
const [stashEntry, setStashEntry] = useState(void 0);
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
findStashEntry().then(setStashEntry);
|
|
360
|
+
}, []);
|
|
361
|
+
useInput2((input) => {
|
|
362
|
+
if (input === "y" || input === "Y") {
|
|
363
|
+
onRecover();
|
|
364
|
+
} else if (input === "n" || input === "N") {
|
|
365
|
+
onSkip();
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
if (stashEntry === void 0) {
|
|
369
|
+
return /* @__PURE__ */ jsx3(Spinner, { label: "\u68C0\u67E5 stash \u8BB0\u5F55..." });
|
|
370
|
+
}
|
|
371
|
+
return /* @__PURE__ */ jsxs3(Box3, { flexDirection: "column", gap: 1, children: [
|
|
372
|
+
/* @__PURE__ */ jsxs3(StatusPanel, { type: "warn", title: "\u68C0\u6D4B\u5230\u4E0A\u6B21\u8FD0\u884C\u4E2D\u65AD", children: [
|
|
373
|
+
timestamp && /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
|
|
374
|
+
" \u4E2D\u65AD\u65F6\u95F4: ",
|
|
375
|
+
timestamp
|
|
376
|
+
] }),
|
|
377
|
+
stashEntry && /* @__PURE__ */ jsxs3(Text3, { color: "gray", children: [
|
|
378
|
+
" Stash: ",
|
|
379
|
+
stashEntry
|
|
380
|
+
] }),
|
|
381
|
+
!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" })
|
|
382
|
+
] }),
|
|
383
|
+
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
384
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: stashEntry ? "\u6062\u590D stash? " : "\u6E05\u9664\u4E2D\u65AD\u6807\u8BB0? " }),
|
|
385
|
+
/* @__PURE__ */ jsx3(InlineKeys, { hints: [
|
|
386
|
+
{ key: "y", label: "\u662F" },
|
|
387
|
+
{ key: "n", label: "\u8DF3\u8FC7" }
|
|
388
|
+
] })
|
|
389
|
+
] })
|
|
390
|
+
] });
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/components/remote-select.tsx
|
|
394
|
+
import { useState as useState3 } from "react";
|
|
395
|
+
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
396
|
+
import { Select, Spinner as Spinner2, TextInput } from "@inkjs/ui";
|
|
186
397
|
|
|
187
398
|
// src/hooks/use-git.ts
|
|
399
|
+
import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
|
|
188
400
|
function useAsync(fn, deps = []) {
|
|
189
|
-
const [state, setState] =
|
|
401
|
+
const [state, setState] = useState2({
|
|
190
402
|
data: null,
|
|
191
403
|
loading: true,
|
|
192
404
|
error: null
|
|
@@ -200,7 +412,7 @@ function useAsync(fn, deps = []) {
|
|
|
200
412
|
setState({ data: null, loading: false, error: err.message });
|
|
201
413
|
}
|
|
202
414
|
}, deps);
|
|
203
|
-
|
|
415
|
+
useEffect2(() => {
|
|
204
416
|
load();
|
|
205
417
|
}, [load]);
|
|
206
418
|
return { ...state, reload: load };
|
|
@@ -208,22 +420,22 @@ function useAsync(fn, deps = []) {
|
|
|
208
420
|
function useRemotes() {
|
|
209
421
|
return useAsync(() => getRemotes(), []);
|
|
210
422
|
}
|
|
211
|
-
function useBranches(
|
|
423
|
+
function useBranches(remote2) {
|
|
212
424
|
return useAsync(
|
|
213
|
-
() =>
|
|
214
|
-
[
|
|
425
|
+
() => remote2 ? getRemoteBranches(remote2) : Promise.resolve([]),
|
|
426
|
+
[remote2]
|
|
215
427
|
);
|
|
216
428
|
}
|
|
217
|
-
function useCommits(
|
|
429
|
+
function useCommits(remote2, branch2, count2 = 30) {
|
|
218
430
|
return useAsync(
|
|
219
|
-
() =>
|
|
220
|
-
[
|
|
431
|
+
() => remote2 && branch2 ? getCommits(remote2, branch2, count2) : Promise.resolve([]),
|
|
432
|
+
[remote2, branch2, count2]
|
|
221
433
|
);
|
|
222
434
|
}
|
|
223
435
|
function useCommitStat(hashes) {
|
|
224
|
-
const [stat, setStat] =
|
|
225
|
-
const [loading, setLoading] =
|
|
226
|
-
|
|
436
|
+
const [stat, setStat] = useState2("");
|
|
437
|
+
const [loading, setLoading] = useState2(false);
|
|
438
|
+
useEffect2(() => {
|
|
227
439
|
if (hashes.length === 0) {
|
|
228
440
|
setStat("");
|
|
229
441
|
return;
|
|
@@ -241,31 +453,50 @@ function useCommitStat(hashes) {
|
|
|
241
453
|
}
|
|
242
454
|
|
|
243
455
|
// src/components/remote-select.tsx
|
|
244
|
-
import { jsx as
|
|
245
|
-
function
|
|
246
|
-
const
|
|
247
|
-
const [
|
|
248
|
-
|
|
249
|
-
|
|
456
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
457
|
+
function extractRemoteName(url) {
|
|
458
|
+
const trimmed = url.trim().replace(/\/+$/, "").replace(/\.git$/, "");
|
|
459
|
+
const lastSegment = trimmed.split(/[/\\:]+/).filter(Boolean).pop() || "";
|
|
460
|
+
return lastSegment;
|
|
461
|
+
}
|
|
462
|
+
function RemoteSelect({ onSelect, onBack }) {
|
|
463
|
+
const { data: remotes, loading, error: error2, reload } = useRemotes();
|
|
464
|
+
const [phase, setPhase] = useState3("list");
|
|
465
|
+
const [customUrl, setCustomUrl] = useState3("");
|
|
466
|
+
const [addError, setAddError] = useState3(null);
|
|
467
|
+
useInput3((_input, key) => {
|
|
468
|
+
if (key.escape) {
|
|
469
|
+
if (phase === "input-name") {
|
|
470
|
+
setPhase("input-url");
|
|
471
|
+
} else if (phase === "input-url") {
|
|
472
|
+
setPhase("list");
|
|
473
|
+
} else if (phase === "list") {
|
|
474
|
+
onBack?.();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
});
|
|
250
478
|
if (loading) {
|
|
251
|
-
return /* @__PURE__ */
|
|
479
|
+
return /* @__PURE__ */ jsx4(Spinner2, { label: "\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93..." });
|
|
252
480
|
}
|
|
253
|
-
if (
|
|
254
|
-
return /* @__PURE__ */
|
|
255
|
-
"\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93\u5931\u8D25: ",
|
|
256
|
-
|
|
481
|
+
if (error2) {
|
|
482
|
+
return /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
483
|
+
"\u2716 \u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93\u5931\u8D25: ",
|
|
484
|
+
error2
|
|
257
485
|
] });
|
|
258
486
|
}
|
|
259
487
|
if (phase === "adding") {
|
|
260
|
-
return /* @__PURE__ */
|
|
488
|
+
return /* @__PURE__ */ jsx4(Spinner2, { label: "\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93..." });
|
|
261
489
|
}
|
|
262
490
|
if (phase === "input-url") {
|
|
263
|
-
return /* @__PURE__ */
|
|
264
|
-
/* @__PURE__ */
|
|
265
|
-
addError && /* @__PURE__ */
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
491
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
492
|
+
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93" }),
|
|
493
|
+
addError && /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
494
|
+
"\u2716 ",
|
|
495
|
+
addError
|
|
496
|
+
] }),
|
|
497
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
498
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "URL \u25B8 " }),
|
|
499
|
+
/* @__PURE__ */ jsx4(
|
|
269
500
|
TextInput,
|
|
270
501
|
{
|
|
271
502
|
placeholder: "https://github.com/user/repo.git",
|
|
@@ -280,23 +511,24 @@ function RemoteSelect({ onSelect }) {
|
|
|
280
511
|
}
|
|
281
512
|
)
|
|
282
513
|
] }),
|
|
283
|
-
/* @__PURE__ */
|
|
514
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: " \u652F\u6301 HTTPS / SSH \u5730\u5740" })
|
|
284
515
|
] });
|
|
285
516
|
}
|
|
286
517
|
if (phase === "input-name") {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
/* @__PURE__ */
|
|
290
|
-
|
|
291
|
-
|
|
518
|
+
const defaultName = extractRemoteName(customUrl);
|
|
519
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
520
|
+
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93", subtitle: customUrl }),
|
|
521
|
+
addError && /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
522
|
+
"\u2716 ",
|
|
523
|
+
addError
|
|
292
524
|
] }),
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
/* @__PURE__ */
|
|
296
|
-
/* @__PURE__ */ jsx2(
|
|
525
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
526
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", children: "\u540D\u79F0 \u25B8 " }),
|
|
527
|
+
/* @__PURE__ */ jsx4(
|
|
297
528
|
TextInput,
|
|
298
529
|
{
|
|
299
|
-
placeholder: "upstream",
|
|
530
|
+
placeholder: defaultName || "upstream",
|
|
531
|
+
defaultValue: defaultName,
|
|
300
532
|
onSubmit: async (name) => {
|
|
301
533
|
const remoteName = name.trim();
|
|
302
534
|
if (!remoteName) {
|
|
@@ -333,9 +565,9 @@ function RemoteSelect({ onSelect }) {
|
|
|
333
565
|
value: "__add_custom__"
|
|
334
566
|
}
|
|
335
567
|
];
|
|
336
|
-
return /* @__PURE__ */
|
|
337
|
-
/* @__PURE__ */
|
|
338
|
-
/* @__PURE__ */
|
|
568
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", gap: 1, children: [
|
|
569
|
+
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u9009\u62E9\u8FDC\u7A0B\u4ED3\u5E93" }),
|
|
570
|
+
/* @__PURE__ */ jsx4(
|
|
339
571
|
Select,
|
|
340
572
|
{
|
|
341
573
|
options,
|
|
@@ -352,73 +584,70 @@ function RemoteSelect({ onSelect }) {
|
|
|
352
584
|
}
|
|
353
585
|
|
|
354
586
|
// src/components/branch-select.tsx
|
|
355
|
-
import { useState as
|
|
356
|
-
import { Box as
|
|
357
|
-
import { Select as Select2, Spinner as
|
|
358
|
-
import { jsx as
|
|
359
|
-
function BranchSelect({ remote, onSelect }) {
|
|
360
|
-
const { data: branches, loading, error } = useBranches(
|
|
361
|
-
const [filter, setFilter] =
|
|
587
|
+
import { useState as useState4, useMemo } from "react";
|
|
588
|
+
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
589
|
+
import { Select as Select2, Spinner as Spinner3, TextInput as TextInput2 } from "@inkjs/ui";
|
|
590
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
591
|
+
function BranchSelect({ remote: remote2, onSelect, onBack }) {
|
|
592
|
+
const { data: branches, loading, error: error2 } = useBranches(remote2);
|
|
593
|
+
const [filter, setFilter] = useState4("");
|
|
594
|
+
useInput4((_input, key) => {
|
|
595
|
+
if (key.escape) onBack?.();
|
|
596
|
+
});
|
|
362
597
|
const filteredOptions = useMemo(() => {
|
|
363
598
|
if (!branches) return [];
|
|
364
599
|
const filtered = filter ? branches.filter((b) => b.toLowerCase().includes(filter.toLowerCase())) : branches;
|
|
365
600
|
return filtered.map((b) => ({ label: b, value: b }));
|
|
366
601
|
}, [branches, filter]);
|
|
367
602
|
if (loading) {
|
|
368
|
-
return /* @__PURE__ */
|
|
603
|
+
return /* @__PURE__ */ jsx5(Spinner3, { label: `\u83B7\u53D6 ${remote2} \u7684\u5206\u652F\u5217\u8868...` });
|
|
369
604
|
}
|
|
370
|
-
if (
|
|
371
|
-
return /* @__PURE__ */
|
|
372
|
-
"\u83B7\u53D6\u5206\u652F\u5217\u8868\u5931\u8D25: ",
|
|
373
|
-
|
|
605
|
+
if (error2) {
|
|
606
|
+
return /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
607
|
+
"\u2716 \u83B7\u53D6\u5206\u652F\u5217\u8868\u5931\u8D25: ",
|
|
608
|
+
error2
|
|
374
609
|
] });
|
|
375
610
|
}
|
|
376
611
|
if (!branches || branches.length === 0) {
|
|
377
|
-
return /* @__PURE__ */
|
|
612
|
+
return /* @__PURE__ */ jsx5(Text5, { color: "red", children: "\u2716 \u672A\u627E\u5230\u8FDC\u7A0B\u5206\u652F" });
|
|
378
613
|
}
|
|
379
|
-
return /* @__PURE__ */
|
|
380
|
-
/* @__PURE__ */
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
] }),
|
|
385
|
-
/* @__PURE__ */ jsxs3(Box3, { children: [
|
|
386
|
-
/* @__PURE__ */ jsx3(Text3, { color: "gray", children: "\u641C\u7D22: " }),
|
|
387
|
-
/* @__PURE__ */ jsx3(
|
|
614
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", gap: 1, children: [
|
|
615
|
+
/* @__PURE__ */ jsx5(SectionHeader, { title: `\u9009\u62E9\u5206\u652F`, subtitle: `${remote2} \xB7 ${branches.length} \u4E2A\u5206\u652F` }),
|
|
616
|
+
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
617
|
+
/* @__PURE__ */ jsx5(Text5, { color: "gray", children: "/ " }),
|
|
618
|
+
/* @__PURE__ */ jsx5(
|
|
388
619
|
TextInput2,
|
|
389
620
|
{
|
|
390
|
-
placeholder: "\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4
|
|
621
|
+
placeholder: "\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4...",
|
|
391
622
|
onChange: setFilter
|
|
392
623
|
}
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
" \u4E2A\u5206\u652F",
|
|
399
|
-
filter ? `\uFF0C\u5339\u914D ${filteredOptions.length} \u4E2A` : ""
|
|
624
|
+
),
|
|
625
|
+
filter && /* @__PURE__ */ jsxs5(Text5, { color: "gray", dimColor: true, children: [
|
|
626
|
+
" \xB7 \u5339\u914D ",
|
|
627
|
+
filteredOptions.length
|
|
628
|
+
] })
|
|
400
629
|
] }),
|
|
401
|
-
filteredOptions.length > 0 ? /* @__PURE__ */
|
|
630
|
+
filteredOptions.length > 0 ? /* @__PURE__ */ jsx5(Select2, { options: filteredOptions, onChange: onSelect }) : /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B2 \u65E0\u5339\u914D\u5206\u652F" })
|
|
402
631
|
] });
|
|
403
632
|
}
|
|
404
633
|
|
|
405
634
|
// src/components/commit-list.tsx
|
|
406
|
-
import { useState as
|
|
407
|
-
import { Box as
|
|
408
|
-
import { Spinner as
|
|
409
|
-
import { jsx as
|
|
410
|
-
function CommitList({ remote, branch, onSelect }) {
|
|
411
|
-
const { data:
|
|
412
|
-
const [selectedIndex, setSelectedIndex] =
|
|
413
|
-
const [selectedHashes, setSelectedHashes] =
|
|
414
|
-
const [shiftMode, setShiftMode] =
|
|
635
|
+
import { useState as useState5, useMemo as useMemo2, useRef } from "react";
|
|
636
|
+
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
637
|
+
import { Spinner as Spinner4 } from "@inkjs/ui";
|
|
638
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
639
|
+
function CommitList({ remote: remote2, branch: branch2, onSelect, onBack }) {
|
|
640
|
+
const { data: commits2, loading, error: error2 } = useCommits(remote2, branch2, 30);
|
|
641
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
642
|
+
const [selectedHashes, setSelectedHashes] = useState5(/* @__PURE__ */ new Set());
|
|
643
|
+
const [shiftMode, setShiftMode] = useState5(false);
|
|
415
644
|
const anchorIndexRef = useRef(null);
|
|
416
645
|
const selectedKey = useMemo2(() => Array.from(selectedHashes).sort().join(","), [selectedHashes]);
|
|
417
646
|
const selectedArray = useMemo2(() => Array.from(selectedHashes), [selectedKey]);
|
|
418
647
|
const { stat, loading: statLoading } = useCommitStat(selectedArray);
|
|
419
648
|
const toggleCurrent = () => {
|
|
420
|
-
if (!
|
|
421
|
-
const hash =
|
|
649
|
+
if (!commits2 || commits2.length === 0) return;
|
|
650
|
+
const hash = commits2[selectedIndex].hash;
|
|
422
651
|
setSelectedHashes((prev) => {
|
|
423
652
|
const next = new Set(prev);
|
|
424
653
|
if (next.has(hash)) {
|
|
@@ -432,51 +661,49 @@ function CommitList({ remote, branch, onSelect }) {
|
|
|
432
661
|
});
|
|
433
662
|
};
|
|
434
663
|
const selectRange = (anchor, current) => {
|
|
435
|
-
if (!
|
|
664
|
+
if (!commits2) return;
|
|
436
665
|
const start = Math.min(anchor, current);
|
|
437
666
|
const end = Math.max(anchor, current);
|
|
438
667
|
setSelectedHashes((prev) => {
|
|
439
668
|
const next = new Set(prev);
|
|
440
669
|
for (let i = start; i <= end; i++) {
|
|
441
|
-
next.add(
|
|
670
|
+
next.add(commits2[i].hash);
|
|
442
671
|
}
|
|
443
672
|
return next;
|
|
444
673
|
});
|
|
445
674
|
};
|
|
446
675
|
const toggleAll = () => {
|
|
447
|
-
if (!
|
|
676
|
+
if (!commits2 || commits2.length === 0) return;
|
|
448
677
|
setSelectedHashes((prev) => {
|
|
449
|
-
if (prev.size ===
|
|
678
|
+
if (prev.size === commits2.length) {
|
|
450
679
|
anchorIndexRef.current = null;
|
|
451
680
|
return /* @__PURE__ */ new Set();
|
|
452
681
|
}
|
|
453
|
-
return new Set(
|
|
682
|
+
return new Set(commits2.map((c) => c.hash));
|
|
454
683
|
});
|
|
455
684
|
};
|
|
456
685
|
const invertSelection = () => {
|
|
457
|
-
if (!
|
|
686
|
+
if (!commits2 || commits2.length === 0) return;
|
|
458
687
|
setSelectedHashes((prev) => {
|
|
459
688
|
const next = /* @__PURE__ */ new Set();
|
|
460
|
-
for (const c of
|
|
461
|
-
if (!prev.has(c.hash))
|
|
462
|
-
next.add(c.hash);
|
|
463
|
-
}
|
|
689
|
+
for (const c of commits2) {
|
|
690
|
+
if (!prev.has(c.hash)) next.add(c.hash);
|
|
464
691
|
}
|
|
465
692
|
return next;
|
|
466
693
|
});
|
|
467
694
|
};
|
|
468
695
|
const selectToCurrent = () => {
|
|
469
|
-
if (!
|
|
696
|
+
if (!commits2 || commits2.length === 0) return;
|
|
470
697
|
setSelectedHashes((prev) => {
|
|
471
698
|
const next = new Set(prev);
|
|
472
699
|
for (let i = 0; i <= selectedIndex; i++) {
|
|
473
|
-
next.add(
|
|
700
|
+
next.add(commits2[i].hash);
|
|
474
701
|
}
|
|
475
702
|
return next;
|
|
476
703
|
});
|
|
477
704
|
};
|
|
478
|
-
|
|
479
|
-
if (!
|
|
705
|
+
useInput5((input, key) => {
|
|
706
|
+
if (!commits2 || commits2.length === 0) return;
|
|
480
707
|
if (key.shift) {
|
|
481
708
|
if (!shiftMode) {
|
|
482
709
|
setShiftMode(true);
|
|
@@ -489,7 +716,7 @@ function CommitList({ remote, branch, onSelect }) {
|
|
|
489
716
|
setSelectedIndex(newIndex);
|
|
490
717
|
selectRange(anchorIndexRef.current, newIndex);
|
|
491
718
|
} else if (key.downArrow) {
|
|
492
|
-
const newIndex = Math.min(
|
|
719
|
+
const newIndex = Math.min(commits2.length - 1, selectedIndex + 1);
|
|
493
720
|
setSelectedIndex(newIndex);
|
|
494
721
|
selectRange(anchorIndexRef.current, newIndex);
|
|
495
722
|
} else if (input === " ") {
|
|
@@ -501,13 +728,11 @@ function CommitList({ remote, branch, onSelect }) {
|
|
|
501
728
|
}
|
|
502
729
|
return;
|
|
503
730
|
}
|
|
504
|
-
if (shiftMode)
|
|
505
|
-
setShiftMode(false);
|
|
506
|
-
}
|
|
731
|
+
if (shiftMode) setShiftMode(false);
|
|
507
732
|
if (key.upArrow) {
|
|
508
733
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
509
734
|
} else if (key.downArrow) {
|
|
510
|
-
setSelectedIndex((prev) => Math.min(
|
|
735
|
+
setSelectedIndex((prev) => Math.min(commits2.length - 1, prev + 1));
|
|
511
736
|
} else if (input === " ") {
|
|
512
737
|
toggleCurrent();
|
|
513
738
|
} else if (input === "a" || input === "A") {
|
|
@@ -516,127 +741,105 @@ function CommitList({ remote, branch, onSelect }) {
|
|
|
516
741
|
invertSelection();
|
|
517
742
|
} else if (input === "r" || input === "R") {
|
|
518
743
|
selectToCurrent();
|
|
744
|
+
} else if (key.escape) {
|
|
745
|
+
onBack?.();
|
|
519
746
|
} else if (key.return) {
|
|
520
747
|
if (selectedHashes.size > 0) {
|
|
521
|
-
onSelect(Array.from(selectedHashes),
|
|
748
|
+
onSelect(Array.from(selectedHashes), commits2);
|
|
522
749
|
}
|
|
523
750
|
}
|
|
524
751
|
});
|
|
525
752
|
if (loading) {
|
|
526
|
-
return /* @__PURE__ */
|
|
753
|
+
return /* @__PURE__ */ jsx6(Spinner4, { label: `\u83B7\u53D6 ${remote2}/${branch2} \u7684 commit \u5217\u8868...` });
|
|
527
754
|
}
|
|
528
|
-
if (
|
|
529
|
-
return /* @__PURE__ */
|
|
530
|
-
"\u83B7\u53D6 commit \u5217\u8868\u5931\u8D25: ",
|
|
531
|
-
|
|
755
|
+
if (error2) {
|
|
756
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: "red", children: [
|
|
757
|
+
"\u2716 \u83B7\u53D6 commit \u5217\u8868\u5931\u8D25: ",
|
|
758
|
+
error2
|
|
532
759
|
] });
|
|
533
760
|
}
|
|
534
|
-
if (!
|
|
535
|
-
return /* @__PURE__ */
|
|
761
|
+
if (!commits2 || commits2.length === 0) {
|
|
762
|
+
return /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "\u25B2 \u8BE5\u5206\u652F\u6CA1\u6709 commit" });
|
|
536
763
|
}
|
|
537
764
|
const visibleCount = 10;
|
|
538
|
-
const startIdx = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2),
|
|
539
|
-
const visibleCommits =
|
|
540
|
-
return /* @__PURE__ */
|
|
541
|
-
/* @__PURE__ */
|
|
542
|
-
/* @__PURE__ */
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
765
|
+
const startIdx = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), commits2.length - visibleCount));
|
|
766
|
+
const visibleCommits = commits2.slice(startIdx, startIdx + visibleCount);
|
|
767
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", gap: 1, children: [
|
|
768
|
+
/* @__PURE__ */ jsx6(SectionHeader, { title: "\u9009\u62E9\u8981\u540C\u6B65\u7684 commit" }),
|
|
769
|
+
/* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
|
|
770
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
771
|
+
remote2,
|
|
772
|
+
"/",
|
|
773
|
+
branch2
|
|
774
|
+
] }),
|
|
775
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
776
|
+
commits2.length,
|
|
777
|
+
" commits"
|
|
778
|
+
] }),
|
|
779
|
+
/* @__PURE__ */ jsxs6(Text6, { color: selectedHashes.size > 0 ? "cyan" : "gray", bold: selectedHashes.size > 0, children: [
|
|
780
|
+
"\u5DF2\u9009 ",
|
|
781
|
+
selectedHashes.size
|
|
782
|
+
] }),
|
|
783
|
+
shiftMode && /* @__PURE__ */ jsx6(Text6, { color: "yellow", bold: true, children: "SHIFT" })
|
|
552
784
|
] }),
|
|
553
|
-
/* @__PURE__ */
|
|
554
|
-
startIdx > 0 && /* @__PURE__ */
|
|
785
|
+
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
786
|
+
startIdx > 0 && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
555
787
|
" \u2191 ",
|
|
556
788
|
startIdx,
|
|
557
|
-
" more
|
|
789
|
+
" more"
|
|
558
790
|
] }),
|
|
559
791
|
visibleCommits.map((c, i) => {
|
|
560
792
|
const actualIdx = startIdx + i;
|
|
561
793
|
const isSelected = selectedHashes.has(c.hash);
|
|
562
794
|
const isCursor = actualIdx === selectedIndex;
|
|
563
795
|
const isAnchor = actualIdx === anchorIndexRef.current;
|
|
564
|
-
return /* @__PURE__ */
|
|
565
|
-
/* @__PURE__ */
|
|
566
|
-
isCursor ? "\
|
|
567
|
-
isAnchor ? "\u2693
|
|
568
|
-
|
|
796
|
+
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
797
|
+
/* @__PURE__ */ jsxs6(Text6, { backgroundColor: isCursor ? "blue" : void 0, color: isSelected ? "green" : "white", children: [
|
|
798
|
+
isCursor ? "\u25B8 " : " ",
|
|
799
|
+
isAnchor ? "\u2693" : isSelected ? "\u25CF" : "\u25CB",
|
|
800
|
+
" "
|
|
801
|
+
] }),
|
|
802
|
+
/* @__PURE__ */ jsx6(Text6, { backgroundColor: isCursor ? "blue" : void 0, color: "yellow", children: c.shortHash }),
|
|
803
|
+
/* @__PURE__ */ jsxs6(Text6, { backgroundColor: isCursor ? "blue" : void 0, color: isSelected ? "green" : "white", children: [
|
|
569
804
|
" ",
|
|
570
805
|
c.message
|
|
571
806
|
] }),
|
|
572
|
-
/* @__PURE__ */
|
|
573
|
-
"
|
|
807
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
808
|
+
" ",
|
|
574
809
|
c.author,
|
|
575
|
-
"
|
|
810
|
+
" \xB7 ",
|
|
811
|
+
c.date
|
|
576
812
|
] })
|
|
577
813
|
] }, c.hash);
|
|
578
814
|
}),
|
|
579
|
-
startIdx + visibleCount <
|
|
815
|
+
startIdx + visibleCount < commits2.length && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
580
816
|
" \u2193 ",
|
|
581
|
-
|
|
582
|
-
" more
|
|
817
|
+
commits2.length - startIdx - visibleCount,
|
|
818
|
+
" more"
|
|
583
819
|
] })
|
|
584
820
|
] }),
|
|
585
|
-
/* @__PURE__ */
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
] }),
|
|
595
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
596
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "a" }),
|
|
597
|
-
" \u5168\u9009"
|
|
598
|
-
] }),
|
|
599
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
600
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "i" }),
|
|
601
|
-
" \u53CD\u9009"
|
|
602
|
-
] }),
|
|
603
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
604
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "Enter" }),
|
|
605
|
-
" \u786E\u8BA4"
|
|
606
|
-
] })
|
|
607
|
-
] }),
|
|
608
|
-
/* @__PURE__ */ jsxs4(Box4, { gap: 2, children: [
|
|
609
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
610
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "Shift+\u2191/\u2193" }),
|
|
611
|
-
" \u8FDE\u7EED\u9009\u62E9"
|
|
612
|
-
] }),
|
|
613
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
614
|
-
/* @__PURE__ */ jsx4(Text4, { color: "yellow", children: "Shift+Space" }),
|
|
615
|
-
" \u8303\u56F4\u9009\u62E9"
|
|
616
|
-
] }),
|
|
617
|
-
/* @__PURE__ */ jsxs4(Text4, { children: [
|
|
618
|
-
/* @__PURE__ */ jsx4(Text4, { color: "cyan", children: "r" }),
|
|
619
|
-
" \u9009\u81F3\u5F00\u5934"
|
|
620
|
-
] })
|
|
621
|
-
] })
|
|
821
|
+
/* @__PURE__ */ jsx6(KeyHints, { hints: [
|
|
822
|
+
{ key: "\u2191\u2193", label: "\u5BFC\u822A" },
|
|
823
|
+
{ key: "Space", label: "\u9009\u62E9" },
|
|
824
|
+
{ key: "a", label: "\u5168\u9009" },
|
|
825
|
+
{ key: "i", label: "\u53CD\u9009" },
|
|
826
|
+
{ key: "r", label: "\u9009\u81F3\u5F00\u5934" },
|
|
827
|
+
{ key: "Shift+\u2191\u2193", label: "\u8FDE\u9009" },
|
|
828
|
+
{ key: "Enter", label: "\u786E\u8BA4" },
|
|
829
|
+
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
622
830
|
] }),
|
|
623
|
-
selectedHashes.size > 0 && /* @__PURE__ */
|
|
624
|
-
/* @__PURE__ */ jsxs4(Text4, { bold: true, color: "yellow", children: [
|
|
625
|
-
"\u5DF2\u9009 ",
|
|
626
|
-
selectedHashes.size,
|
|
627
|
-
" \u4E2A commit \u2014 diff --stat \u9884\u89C8:"
|
|
628
|
-
] }),
|
|
629
|
-
statLoading ? /* @__PURE__ */ jsx4(Spinner3, { label: "\u52A0\u8F7D\u4E2D..." }) : /* @__PURE__ */ jsx4(Text4, { color: "gray", children: stat || "(\u65E0\u53D8\u66F4)" })
|
|
630
|
-
] })
|
|
831
|
+
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)" }) })
|
|
631
832
|
] });
|
|
632
833
|
}
|
|
633
834
|
|
|
634
835
|
// src/components/confirm-panel.tsx
|
|
635
|
-
import { Box as
|
|
636
|
-
import { jsx as
|
|
637
|
-
function ConfirmPanel({ commits, selectedHashes, hasMerge, useMainline, onToggleMainline, onConfirm, onCancel }) {
|
|
638
|
-
|
|
639
|
-
if (
|
|
836
|
+
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
837
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
838
|
+
function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline, onToggleMainline, onConfirm, onCancel }) {
|
|
839
|
+
useInput6((input, key) => {
|
|
840
|
+
if (key.escape) {
|
|
841
|
+
onCancel();
|
|
842
|
+
} else if (input === "y" || input === "Y") {
|
|
640
843
|
onConfirm();
|
|
641
844
|
} else if (input === "n" || input === "N" || input === "q") {
|
|
642
845
|
onCancel();
|
|
@@ -644,69 +847,58 @@ function ConfirmPanel({ commits, selectedHashes, hasMerge, useMainline, onToggle
|
|
|
644
847
|
onToggleMainline();
|
|
645
848
|
}
|
|
646
849
|
});
|
|
647
|
-
const selectedCommits = selectedHashes.map((hash) =>
|
|
648
|
-
return /* @__PURE__ */
|
|
649
|
-
/* @__PURE__ */
|
|
650
|
-
/* @__PURE__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
"
|
|
653
|
-
|
|
654
|
-
" \u4E2A commit:"
|
|
850
|
+
const selectedCommits = selectedHashes.map((hash) => commits2.find((c) => c.hash === hash)).filter(Boolean);
|
|
851
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
|
|
852
|
+
/* @__PURE__ */ jsx7(SectionHeader, { title: "\u786E\u8BA4\u6267\u884C" }),
|
|
853
|
+
/* @__PURE__ */ jsx7(StatusPanel, { type: "info", title: `cherry-pick --no-commit \xB7 ${selectedCommits.length} \u4E2A commit`, children: selectedCommits.map((c) => /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
854
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
|
|
855
|
+
" ",
|
|
856
|
+
c.shortHash
|
|
655
857
|
] }),
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
] })
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
hasMerge && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", borderStyle: "single", borderColor: "red", paddingX: 1, children: [
|
|
673
|
-
/* @__PURE__ */ jsx5(Text5, { bold: true, color: "red", children: "\u68C0\u6D4B\u5230 Merge Commit" }),
|
|
674
|
-
/* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "Cherry-pick \u5408\u5E76\u63D0\u4EA4\u9700\u8981\u6307\u5B9A\u7236\u8282\u70B9 (-m 1)" }),
|
|
675
|
-
/* @__PURE__ */ jsxs5(Text5, { children: [
|
|
676
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "[m]" }),
|
|
677
|
-
/* @__PURE__ */ jsx5(Text5, { children: " \u5207\u6362 -m 1: " }),
|
|
678
|
-
useMainline ? /* @__PURE__ */ jsx5(Text5, { color: "green", children: "\u5DF2\u542F\u7528" }) : /* @__PURE__ */ jsx5(Text5, { color: "gray", children: "\u672A\u542F\u7528" })
|
|
858
|
+
/* @__PURE__ */ jsxs7(Text7, { children: [
|
|
859
|
+
" ",
|
|
860
|
+
c.message
|
|
861
|
+
] }),
|
|
862
|
+
/* @__PURE__ */ jsxs7(Text7, { color: "gray", dimColor: true, children: [
|
|
863
|
+
" ",
|
|
864
|
+
c.author
|
|
865
|
+
] })
|
|
866
|
+
] }, c.hash)) }),
|
|
867
|
+
hasMerge && /* @__PURE__ */ jsxs7(StatusPanel, { type: "warn", title: "\u68C0\u6D4B\u5230 Merge Commit", children: [
|
|
868
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: " Cherry-pick \u5408\u5E76\u63D0\u4EA4\u9700\u8981\u6307\u5B9A\u7236\u8282\u70B9 (-m 1)" }),
|
|
869
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
870
|
+
/* @__PURE__ */ jsx7(Text7, { children: " " }),
|
|
871
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "[m]" }),
|
|
872
|
+
/* @__PURE__ */ jsx7(Text7, { children: " \u5207\u6362 -m 1: " }),
|
|
873
|
+
useMainline ? /* @__PURE__ */ jsx7(Text7, { color: "green", bold: true, children: "\u5DF2\u542F\u7528" }) : /* @__PURE__ */ jsx7(Text7, { color: "gray", children: "\u672A\u542F\u7528" })
|
|
679
874
|
] })
|
|
680
875
|
] }),
|
|
681
|
-
/* @__PURE__ */
|
|
682
|
-
/* @__PURE__ */
|
|
683
|
-
/* @__PURE__ */
|
|
876
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
877
|
+
/* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "\u25B2 " }),
|
|
878
|
+
/* @__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" })
|
|
684
879
|
] }),
|
|
685
|
-
/* @__PURE__ */
|
|
686
|
-
/* @__PURE__ */
|
|
687
|
-
/* @__PURE__ */
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
" / ",
|
|
693
|
-
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "[m]" }),
|
|
694
|
-
" \u5207\u6362 -m 1"
|
|
880
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
881
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: "\u786E\u8BA4\u6267\u884C? " }),
|
|
882
|
+
/* @__PURE__ */ jsx7(InlineKeys, { hints: [
|
|
883
|
+
{ key: "y", label: "\u786E\u8BA4" },
|
|
884
|
+
{ key: "n", label: "\u53D6\u6D88" },
|
|
885
|
+
...hasMerge ? [{ key: "m", label: "\u5207\u6362 -m 1" }] : [],
|
|
886
|
+
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
695
887
|
] })
|
|
696
888
|
] })
|
|
697
889
|
] });
|
|
698
890
|
}
|
|
699
891
|
|
|
700
892
|
// src/components/result-panel.tsx
|
|
701
|
-
import { useState as
|
|
702
|
-
import { Box as
|
|
703
|
-
import { Spinner as
|
|
704
|
-
import { jsx as
|
|
893
|
+
import { useState as useState6, useEffect as useEffect3 } from "react";
|
|
894
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
895
|
+
import { Spinner as Spinner5 } from "@inkjs/ui";
|
|
896
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
705
897
|
function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, onDone }) {
|
|
706
|
-
const [phase, setPhase] =
|
|
707
|
-
const [result, setResult] =
|
|
708
|
-
const [stagedStat, setStagedStat] =
|
|
709
|
-
const [stashRestored, setStashRestored] =
|
|
898
|
+
const [phase, setPhase] = useState6("executing");
|
|
899
|
+
const [result, setResult] = useState6(null);
|
|
900
|
+
const [stagedStat, setStagedStat] = useState6("");
|
|
901
|
+
const [stashRestored, setStashRestored] = useState6(null);
|
|
710
902
|
const tryRestoreStash = async () => {
|
|
711
903
|
if (!stashed) return true;
|
|
712
904
|
setPhase("restoring");
|
|
@@ -715,7 +907,7 @@ function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, on
|
|
|
715
907
|
if (ok) onStashRestored();
|
|
716
908
|
return ok;
|
|
717
909
|
};
|
|
718
|
-
|
|
910
|
+
useEffect3(() => {
|
|
719
911
|
async function run() {
|
|
720
912
|
const res = await cherryPick(selectedHashes, useMainline);
|
|
721
913
|
setResult(res);
|
|
@@ -732,62 +924,94 @@ function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, on
|
|
|
732
924
|
run();
|
|
733
925
|
}, []);
|
|
734
926
|
if (phase === "executing") {
|
|
735
|
-
return /* @__PURE__ */
|
|
927
|
+
return /* @__PURE__ */ jsx8(Spinner5, { label: `cherry-pick --no-commit (${selectedHashes.length} \u4E2A commit)...` });
|
|
736
928
|
}
|
|
737
929
|
if (phase === "restoring") {
|
|
738
|
-
return /* @__PURE__ */
|
|
930
|
+
return /* @__PURE__ */ jsx8(Spinner5, { label: "\u6062\u590D\u5DE5\u4F5C\u533A (git stash pop)..." });
|
|
739
931
|
}
|
|
740
932
|
if (phase === "error" && result) {
|
|
741
|
-
return /* @__PURE__ */
|
|
742
|
-
/* @__PURE__ */
|
|
743
|
-
result.conflictFiles && result.conflictFiles.length > 0 && /* @__PURE__ */
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
933
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
|
|
934
|
+
/* @__PURE__ */ jsx8(SectionHeader, { title: "Cherry-pick \u9047\u5230\u51B2\u7A81" }),
|
|
935
|
+
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: [
|
|
936
|
+
" ",
|
|
937
|
+
f
|
|
938
|
+
] }, f)) }),
|
|
939
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
940
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 \u624B\u52A8\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add \u548C git commit" }),
|
|
941
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "\u25B8 \u6216\u6267\u884C git cherry-pick --abort \u653E\u5F03\u64CD\u4F5C" })
|
|
749
942
|
] }),
|
|
750
|
-
/* @__PURE__ */
|
|
751
|
-
/* @__PURE__ */
|
|
752
|
-
stashed && stashRestored === false && /* @__PURE__ */ jsx6(Text6, { color: "yellow", children: "\u6CE8\u610F: stash \u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" }),
|
|
753
|
-
stashed && stashRestored === true && /* @__PURE__ */ jsx6(Text6, { color: "green", children: "\u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)" })
|
|
943
|
+
stashed && stashRestored === false && /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 stash \u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" }),
|
|
944
|
+
stashed && stashRestored === true && /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)" })
|
|
754
945
|
] });
|
|
755
946
|
}
|
|
756
|
-
return /* @__PURE__ */
|
|
757
|
-
/* @__PURE__ */
|
|
758
|
-
/* @__PURE__ */
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
947
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
|
|
948
|
+
/* @__PURE__ */ jsx8(SectionHeader, { title: "\u540C\u6B65\u5B8C\u6210" }),
|
|
949
|
+
/* @__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)" }) }),
|
|
950
|
+
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" })),
|
|
951
|
+
/* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
|
|
952
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)" }),
|
|
953
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: " \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:" }),
|
|
954
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
955
|
+
" git diff --cached ",
|
|
956
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u67E5\u770B\u8BE6\u7EC6 diff" })
|
|
957
|
+
] }),
|
|
958
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
959
|
+
' git commit -m "sync: ..." ',
|
|
960
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u63D0\u4EA4" })
|
|
961
|
+
] }),
|
|
962
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
|
|
963
|
+
" git reset HEAD ",
|
|
964
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u6216\u653E\u5F03" })
|
|
965
|
+
] })
|
|
966
|
+
] })
|
|
768
967
|
] });
|
|
769
968
|
}
|
|
770
969
|
|
|
771
970
|
// src/app.tsx
|
|
772
971
|
import { execSync } from "child_process";
|
|
773
|
-
import { jsx as
|
|
774
|
-
|
|
972
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
973
|
+
var STEP_NUMBER = {
|
|
974
|
+
checking: 0,
|
|
975
|
+
"stash-recovery": 0,
|
|
976
|
+
"stash-prompt": 0,
|
|
977
|
+
remote: 1,
|
|
978
|
+
branch: 2,
|
|
979
|
+
commits: 3,
|
|
980
|
+
confirm: 4,
|
|
981
|
+
result: 5
|
|
982
|
+
};
|
|
983
|
+
var STEP_DEBOUNCE = 100;
|
|
984
|
+
function App({ initialRemote, initialBranch }) {
|
|
775
985
|
const { exit } = useApp();
|
|
776
|
-
const
|
|
777
|
-
const [
|
|
778
|
-
const [
|
|
779
|
-
const [
|
|
780
|
-
const [
|
|
781
|
-
const [
|
|
782
|
-
const [
|
|
783
|
-
const [
|
|
986
|
+
const entryStep = initialRemote && initialBranch ? "commits" : initialRemote ? "branch" : "remote";
|
|
987
|
+
const [step, setStepRaw] = useState7("checking");
|
|
988
|
+
const [inputReady, setInputReady] = useState7(true);
|
|
989
|
+
const [remote2, setRemote] = useState7(initialRemote || "");
|
|
990
|
+
const [branch2, setBranch] = useState7(initialBranch || "");
|
|
991
|
+
const [selectedHashes, setSelectedHashes] = useState7([]);
|
|
992
|
+
const [commits2, setCommits] = useState7([]);
|
|
993
|
+
const [hasMerge, setHasMerge] = useState7(false);
|
|
994
|
+
const [useMainline, setUseMainline] = useState7(false);
|
|
995
|
+
const [stashed, setStashed] = useState7(false);
|
|
996
|
+
const [guardTimestamp, setGuardTimestamp] = useState7();
|
|
784
997
|
const stashedRef = useRef2(false);
|
|
785
998
|
const stashRestoredRef = useRef2(false);
|
|
999
|
+
const mountedRef = useRef2(true);
|
|
1000
|
+
const debounceTimer = useRef2(null);
|
|
1001
|
+
const setStep = useCallback2((newStep) => {
|
|
1002
|
+
setInputReady(false);
|
|
1003
|
+
setStepRaw(newStep);
|
|
1004
|
+
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
1005
|
+
debounceTimer.current = setTimeout(() => {
|
|
1006
|
+
if (mountedRef.current) setInputReady(true);
|
|
1007
|
+
}, STEP_DEBOUNCE);
|
|
1008
|
+
}, []);
|
|
786
1009
|
const restoreStashSync = useCallback2(() => {
|
|
787
1010
|
if (stashedRef.current && !stashRestoredRef.current) {
|
|
788
1011
|
try {
|
|
789
1012
|
execSync("git stash pop", { stdio: "ignore" });
|
|
790
1013
|
stashRestoredRef.current = true;
|
|
1014
|
+
removeStashGuardSync();
|
|
791
1015
|
} catch {
|
|
792
1016
|
try {
|
|
793
1017
|
process.stderr.write("\n\u26A0 stash \u81EA\u52A8\u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: git stash pop\n");
|
|
@@ -798,97 +1022,144 @@ function App() {
|
|
|
798
1022
|
}, []);
|
|
799
1023
|
const markStashRestored = useCallback2(() => {
|
|
800
1024
|
stashRestoredRef.current = true;
|
|
1025
|
+
removeStashGuard();
|
|
801
1026
|
}, []);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1027
|
+
useEffect4(() => {
|
|
1028
|
+
mountedRef.current = true;
|
|
1029
|
+
async function check() {
|
|
1030
|
+
const guard = await checkStashGuard();
|
|
1031
|
+
if (!mountedRef.current) return;
|
|
1032
|
+
if (guard.exists) {
|
|
1033
|
+
setGuardTimestamp(guard.timestamp);
|
|
1034
|
+
setStep("stash-recovery");
|
|
1035
|
+
return;
|
|
808
1036
|
}
|
|
809
|
-
|
|
1037
|
+
const clean = await isWorkingDirClean();
|
|
1038
|
+
if (!mountedRef.current) return;
|
|
1039
|
+
setStep(clean ? entryStep : "stash-prompt");
|
|
1040
|
+
}
|
|
1041
|
+
check();
|
|
810
1042
|
const onSignal = () => {
|
|
811
1043
|
restoreStashSync();
|
|
812
1044
|
process.exit(0);
|
|
813
1045
|
};
|
|
814
1046
|
process.on("SIGINT", onSignal);
|
|
815
1047
|
process.on("SIGTERM", onSignal);
|
|
816
|
-
process.on("
|
|
1048
|
+
process.on("SIGHUP", onSignal);
|
|
817
1049
|
return () => {
|
|
1050
|
+
mountedRef.current = false;
|
|
1051
|
+
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
818
1052
|
process.off("SIGINT", onSignal);
|
|
819
1053
|
process.off("SIGTERM", onSignal);
|
|
820
|
-
process.off("
|
|
1054
|
+
process.off("SIGHUP", onSignal);
|
|
821
1055
|
};
|
|
822
|
-
}, [restoreStashSync]);
|
|
1056
|
+
}, [restoreStashSync, setStep]);
|
|
823
1057
|
const doStash = async () => {
|
|
824
1058
|
const ok = await stash();
|
|
825
1059
|
if (ok) {
|
|
826
1060
|
setStashed(true);
|
|
827
1061
|
stashedRef.current = true;
|
|
1062
|
+
await writeStashGuard();
|
|
828
1063
|
}
|
|
829
|
-
setStep(
|
|
1064
|
+
if (mountedRef.current) setStep(entryStep);
|
|
830
1065
|
};
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
1066
|
+
const doStashRecover = async () => {
|
|
1067
|
+
const entry = await findStashEntry();
|
|
1068
|
+
if (entry) {
|
|
1069
|
+
await stashPop();
|
|
1070
|
+
}
|
|
1071
|
+
await removeStashGuard();
|
|
1072
|
+
if (!mountedRef.current) return;
|
|
1073
|
+
const clean = await isWorkingDirClean();
|
|
1074
|
+
if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
|
|
1075
|
+
};
|
|
1076
|
+
const skipStashRecover = async () => {
|
|
1077
|
+
await removeStashGuard();
|
|
1078
|
+
if (!mountedRef.current) return;
|
|
1079
|
+
const clean = await isWorkingDirClean();
|
|
1080
|
+
if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
|
|
1081
|
+
};
|
|
1082
|
+
const goBack = useCallback2((fromStep) => {
|
|
1083
|
+
const backMap = {
|
|
1084
|
+
branch: "remote",
|
|
1085
|
+
commits: "branch",
|
|
1086
|
+
confirm: "commits"
|
|
1087
|
+
};
|
|
1088
|
+
const prev = backMap[fromStep];
|
|
1089
|
+
if (prev) {
|
|
1090
|
+
setStep(prev);
|
|
1091
|
+
} else {
|
|
1092
|
+
restoreStashSync();
|
|
1093
|
+
exit();
|
|
1094
|
+
}
|
|
1095
|
+
}, [setStep, restoreStashSync, exit]);
|
|
1096
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1097
|
+
/* @__PURE__ */ jsx9(AppHeader, { step: STEP_NUMBER[step], stashed }),
|
|
1098
|
+
step === "checking" && /* @__PURE__ */ jsx9(Spinner6, { label: "\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001..." }),
|
|
1099
|
+
step === "stash-recovery" && inputReady && /* @__PURE__ */ jsx9(
|
|
1100
|
+
StashRecovery,
|
|
1101
|
+
{
|
|
1102
|
+
timestamp: guardTimestamp,
|
|
1103
|
+
onRecover: doStashRecover,
|
|
1104
|
+
onSkip: skipStashRecover
|
|
1105
|
+
}
|
|
1106
|
+
),
|
|
1107
|
+
step === "stash-prompt" && inputReady && /* @__PURE__ */ jsx9(
|
|
840
1108
|
StashPrompt,
|
|
841
1109
|
{
|
|
842
1110
|
onConfirm: doStash,
|
|
843
|
-
onSkip: () => setStep(
|
|
1111
|
+
onSkip: () => setStep(entryStep)
|
|
844
1112
|
}
|
|
845
1113
|
),
|
|
846
|
-
step === "remote" && /* @__PURE__ */
|
|
1114
|
+
step === "remote" && inputReady && /* @__PURE__ */ jsx9(
|
|
847
1115
|
RemoteSelect,
|
|
848
1116
|
{
|
|
849
1117
|
onSelect: (r) => {
|
|
850
1118
|
setRemote(r);
|
|
851
1119
|
setStep("branch");
|
|
852
|
-
}
|
|
1120
|
+
},
|
|
1121
|
+
onBack: () => goBack("remote")
|
|
853
1122
|
}
|
|
854
1123
|
),
|
|
855
|
-
step === "branch" && /* @__PURE__ */
|
|
1124
|
+
step === "branch" && inputReady && /* @__PURE__ */ jsx9(
|
|
856
1125
|
BranchSelect,
|
|
857
1126
|
{
|
|
858
|
-
remote,
|
|
1127
|
+
remote: remote2,
|
|
859
1128
|
onSelect: (b) => {
|
|
860
1129
|
setBranch(b);
|
|
861
1130
|
setStep("commits");
|
|
862
|
-
}
|
|
1131
|
+
},
|
|
1132
|
+
onBack: () => goBack("branch")
|
|
863
1133
|
}
|
|
864
1134
|
),
|
|
865
|
-
step === "commits" && /* @__PURE__ */
|
|
1135
|
+
step === "commits" && inputReady && /* @__PURE__ */ jsx9(
|
|
866
1136
|
CommitList,
|
|
867
1137
|
{
|
|
868
|
-
remote,
|
|
869
|
-
branch,
|
|
1138
|
+
remote: remote2,
|
|
1139
|
+
branch: branch2,
|
|
870
1140
|
onSelect: async (hashes, loadedCommits) => {
|
|
871
1141
|
setSelectedHashes(hashes);
|
|
872
1142
|
setCommits(loadedCommits);
|
|
873
1143
|
const merge = await hasMergeCommits(hashes);
|
|
874
1144
|
setHasMerge(merge);
|
|
875
1145
|
setStep("confirm");
|
|
876
|
-
}
|
|
1146
|
+
},
|
|
1147
|
+
onBack: () => goBack("commits")
|
|
877
1148
|
}
|
|
878
1149
|
),
|
|
879
|
-
step === "confirm" && /* @__PURE__ */
|
|
1150
|
+
step === "confirm" && inputReady && /* @__PURE__ */ jsx9(
|
|
880
1151
|
ConfirmPanel,
|
|
881
1152
|
{
|
|
882
|
-
commits,
|
|
1153
|
+
commits: commits2,
|
|
883
1154
|
selectedHashes,
|
|
884
1155
|
hasMerge,
|
|
885
1156
|
useMainline,
|
|
886
1157
|
onToggleMainline: () => setUseMainline((v) => !v),
|
|
887
1158
|
onConfirm: () => setStep("result"),
|
|
888
|
-
onCancel: () =>
|
|
1159
|
+
onCancel: () => goBack("confirm")
|
|
889
1160
|
}
|
|
890
1161
|
),
|
|
891
|
-
step === "result" && /* @__PURE__ */
|
|
1162
|
+
step === "result" && /* @__PURE__ */ jsx9(
|
|
892
1163
|
ResultPanel,
|
|
893
1164
|
{
|
|
894
1165
|
selectedHashes,
|
|
@@ -904,32 +1175,244 @@ function App() {
|
|
|
904
1175
|
] });
|
|
905
1176
|
}
|
|
906
1177
|
|
|
1178
|
+
// src/cli-runner.ts
|
|
1179
|
+
import { createInterface } from "readline";
|
|
1180
|
+
function log(msg) {
|
|
1181
|
+
process.stdout.write(msg + "\n");
|
|
1182
|
+
}
|
|
1183
|
+
function error(msg) {
|
|
1184
|
+
process.stderr.write(msg + "\n");
|
|
1185
|
+
}
|
|
1186
|
+
function padEnd(str, len) {
|
|
1187
|
+
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
1188
|
+
}
|
|
1189
|
+
async function confirm(message) {
|
|
1190
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1191
|
+
return new Promise((resolve) => {
|
|
1192
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
1193
|
+
rl.close();
|
|
1194
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
1195
|
+
});
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
1198
|
+
async function handleStash(noStash2) {
|
|
1199
|
+
const clean = await isWorkingDirClean();
|
|
1200
|
+
if (clean) {
|
|
1201
|
+
log("\u2714 \u5DE5\u4F5C\u533A\u5E72\u51C0");
|
|
1202
|
+
return false;
|
|
1203
|
+
}
|
|
1204
|
+
if (noStash2) {
|
|
1205
|
+
log("\u25B2 \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF08--no-stash \u8DF3\u8FC7 stash\uFF09");
|
|
1206
|
+
return false;
|
|
1207
|
+
}
|
|
1208
|
+
log("\u25B2 \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u81EA\u52A8 stash...");
|
|
1209
|
+
const ok = await stash();
|
|
1210
|
+
if (ok) {
|
|
1211
|
+
await writeStashGuard();
|
|
1212
|
+
log("\u2714 \u5DF2 stash \u5DE5\u4F5C\u533A\u53D8\u66F4");
|
|
1213
|
+
return true;
|
|
1214
|
+
}
|
|
1215
|
+
error("\u2716 stash \u5931\u8D25");
|
|
1216
|
+
process.exit(1);
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
async function restoreStash(stashed) {
|
|
1220
|
+
if (!stashed) return;
|
|
1221
|
+
const ok = await stashPop();
|
|
1222
|
+
await removeStashGuard();
|
|
1223
|
+
if (ok) {
|
|
1224
|
+
log("\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)");
|
|
1225
|
+
} else {
|
|
1226
|
+
error("\u25B2 stash pop \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: git stash pop");
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
async function validateRemote(name) {
|
|
1230
|
+
const remotes = await getRemotes();
|
|
1231
|
+
if (!remotes.some((r) => r.name === name)) {
|
|
1232
|
+
const available = remotes.map((r) => r.name).join(", ");
|
|
1233
|
+
error(`\u2716 \u8FDC\u7A0B\u4ED3\u5E93 '${name}' \u4E0D\u5B58\u5728`);
|
|
1234
|
+
if (available) error(` \u53EF\u7528: ${available}`);
|
|
1235
|
+
process.exit(1);
|
|
1236
|
+
}
|
|
1237
|
+
log(`\u2714 \u8FDC\u7A0B\u4ED3\u5E93 '${name}'`);
|
|
1238
|
+
}
|
|
1239
|
+
async function validateBranch(remote2, branch2) {
|
|
1240
|
+
const branches = await getRemoteBranches(remote2);
|
|
1241
|
+
if (!branches.includes(branch2)) {
|
|
1242
|
+
error(`\u2716 \u5206\u652F '${branch2}' \u4E0D\u5B58\u5728\u4E8E ${remote2}`);
|
|
1243
|
+
const similar = branches.filter((b) => b.toLowerCase().includes(branch2.toLowerCase())).slice(0, 5);
|
|
1244
|
+
if (similar.length > 0) error(` \u7C7B\u4F3C: ${similar.join(", ")}`);
|
|
1245
|
+
process.exit(1);
|
|
1246
|
+
}
|
|
1247
|
+
log(`\u2714 \u5206\u652F '${remote2}/${branch2}'`);
|
|
1248
|
+
}
|
|
1249
|
+
function formatCommitLine(c) {
|
|
1250
|
+
return ` ${c.shortHash} ${padEnd(c.message.slice(0, 60), 62)} ${padEnd(c.author, 16)} ${c.date}`;
|
|
1251
|
+
}
|
|
1252
|
+
async function runList(opts) {
|
|
1253
|
+
await validateRemote(opts.remote);
|
|
1254
|
+
log(`\u83B7\u53D6 ${opts.remote}/${opts.branch} \u7684 commit \u5217\u8868...`);
|
|
1255
|
+
await validateBranch(opts.remote, opts.branch);
|
|
1256
|
+
const commits2 = await getCommits(opts.remote, opts.branch, opts.count);
|
|
1257
|
+
if (commits2.length === 0) {
|
|
1258
|
+
log("(\u65E0 commit)");
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
log(`
|
|
1262
|
+
Commits on ${opts.remote}/${opts.branch} (${commits2.length}):`);
|
|
1263
|
+
for (const c of commits2) {
|
|
1264
|
+
log(formatCommitLine(c));
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
async function runExec(opts) {
|
|
1268
|
+
const stashed = await handleStash(opts.noStash);
|
|
1269
|
+
await validateRemote(opts.remote);
|
|
1270
|
+
await validateBranch(opts.remote, opts.branch);
|
|
1271
|
+
const allCommits = await getCommits(opts.remote, opts.branch, opts.count);
|
|
1272
|
+
const hashes = opts.commits;
|
|
1273
|
+
const resolved = [];
|
|
1274
|
+
for (const input of hashes) {
|
|
1275
|
+
const match = allCommits.find(
|
|
1276
|
+
(c) => c.hash === input || c.shortHash === input || c.hash.startsWith(input)
|
|
1277
|
+
);
|
|
1278
|
+
if (!match) {
|
|
1279
|
+
error(`\u2716 commit '${input}' \u672A\u627E\u5230\u5728 ${opts.remote}/${opts.branch}`);
|
|
1280
|
+
await restoreStash(stashed);
|
|
1281
|
+
process.exit(1);
|
|
1282
|
+
}
|
|
1283
|
+
resolved.push(match);
|
|
1284
|
+
}
|
|
1285
|
+
const resolvedHashes = resolved.map((c) => c.hash);
|
|
1286
|
+
const hasMerge = await hasMergeCommits(resolvedHashes);
|
|
1287
|
+
if (hasMerge && !opts.mainline) {
|
|
1288
|
+
log("\u25B2 \u68C0\u6D4B\u5230 merge commit\uFF0C\u5EFA\u8BAE\u6DFB\u52A0 --mainline (-m) \u53C2\u6570");
|
|
1289
|
+
}
|
|
1290
|
+
log(`
|
|
1291
|
+
Cherry-pick ${resolved.length} \u4E2A commit (--no-commit${opts.mainline ? " -m 1" : ""}):`);
|
|
1292
|
+
for (const c of resolved) {
|
|
1293
|
+
log(formatCommitLine(c));
|
|
1294
|
+
}
|
|
1295
|
+
log("");
|
|
1296
|
+
if (!opts.yes) {
|
|
1297
|
+
const ok = await confirm("\u786E\u8BA4\u6267\u884C?");
|
|
1298
|
+
if (!ok) {
|
|
1299
|
+
log("\u5DF2\u53D6\u6D88");
|
|
1300
|
+
await restoreStash(stashed);
|
|
1301
|
+
process.exit(0);
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
const result = await cherryPick(resolvedHashes, opts.mainline);
|
|
1305
|
+
if (result.success) {
|
|
1306
|
+
log("\u2714 Cherry-pick \u5B8C\u6210");
|
|
1307
|
+
const stat = await getStagedStat();
|
|
1308
|
+
if (stat) {
|
|
1309
|
+
log(`
|
|
1310
|
+
\u6682\u5B58\u533A\u53D8\u66F4 (git diff --cached --stat):
|
|
1311
|
+
${stat}`);
|
|
1312
|
+
}
|
|
1313
|
+
log("\n\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)");
|
|
1314
|
+
log(" \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:");
|
|
1315
|
+
log(" git diff --cached # \u67E5\u770B\u8BE6\u7EC6 diff");
|
|
1316
|
+
log(' git commit -m "sync: ..." # \u63D0\u4EA4');
|
|
1317
|
+
log(" git reset HEAD # \u6216\u653E\u5F03");
|
|
1318
|
+
} else {
|
|
1319
|
+
error("\u2716 Cherry-pick \u9047\u5230\u51B2\u7A81");
|
|
1320
|
+
if (result.conflictFiles && result.conflictFiles.length > 0) {
|
|
1321
|
+
error("\u51B2\u7A81\u6587\u4EF6:");
|
|
1322
|
+
for (const f of result.conflictFiles) {
|
|
1323
|
+
error(` ${f}`);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
error("\n\u25B8 \u624B\u52A8\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add \u548C git commit");
|
|
1327
|
+
error("\u25B8 \u6216\u6267\u884C git cherry-pick --abort \u653E\u5F03\u64CD\u4F5C");
|
|
1328
|
+
}
|
|
1329
|
+
await restoreStash(stashed);
|
|
1330
|
+
if (!result.success) {
|
|
1331
|
+
process.exit(1);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
async function runCli(opts) {
|
|
1335
|
+
if (opts.list) {
|
|
1336
|
+
await runList(opts);
|
|
1337
|
+
} else {
|
|
1338
|
+
await runExec(opts);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
|
|
907
1342
|
// src/cli.tsx
|
|
908
|
-
import { jsx as
|
|
1343
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
909
1344
|
var cli = meow(
|
|
910
1345
|
`
|
|
911
1346
|
\u7528\u6CD5
|
|
912
|
-
$ git-sync-tui
|
|
1347
|
+
$ git-sync-tui [options]
|
|
913
1348
|
|
|
914
1349
|
\u9009\u9879
|
|
915
|
-
--
|
|
916
|
-
--
|
|
1350
|
+
-r, --remote <name> \u6307\u5B9A\u8FDC\u7A0B\u4ED3\u5E93\u540D\u79F0
|
|
1351
|
+
-b, --branch <name> \u6307\u5B9A\u8FDC\u7A0B\u5206\u652F\u540D\u79F0
|
|
1352
|
+
-c, --commits <hashes> \u6307\u5B9A commit hash\uFF08\u9017\u53F7\u5206\u9694\uFF09
|
|
1353
|
+
-n, --count <number> \u663E\u793A commit \u6570\u91CF\uFF08\u9ED8\u8BA4 30\uFF09
|
|
1354
|
+
-m, --mainline \u5BF9 merge commit \u4F7F\u7528 -m 1
|
|
1355
|
+
-y, --yes \u8DF3\u8FC7\u786E\u8BA4\u76F4\u63A5\u6267\u884C
|
|
1356
|
+
--no-stash \u8DF3\u8FC7 stash \u63D0\u793A
|
|
1357
|
+
--list \u5217\u51FA\u8FDC\u7A0B\u5206\u652F\u7684 commit \u540E\u9000\u51FA
|
|
917
1358
|
|
|
918
|
-
\
|
|
919
|
-
\u4EA4\u4E92\u5F0F TUI \
|
|
920
|
-
|
|
1359
|
+
\u6A21\u5F0F
|
|
1360
|
+
\u65E0\u53C2\u6570 \u4EA4\u4E92\u5F0F TUI \u6A21\u5F0F
|
|
1361
|
+
-r -b --list \u5217\u51FA commit\uFF08\u7EAF\u6587\u672C\uFF09
|
|
1362
|
+
-r -b -c CLI \u6A21\u5F0F\uFF0C\u786E\u8BA4\u540E\u6267\u884C
|
|
1363
|
+
-r -b -c --yes CLI \u6A21\u5F0F\uFF0C\u76F4\u63A5\u6267\u884C
|
|
1364
|
+
\u4EC5 -r \u6216 -r -b TUI \u6A21\u5F0F\uFF0C\u8DF3\u8FC7\u5DF2\u6307\u5B9A\u6B65\u9AA4
|
|
921
1365
|
|
|
922
|
-
\u5FEB\u6377\u952E
|
|
1366
|
+
TUI \u5FEB\u6377\u952E
|
|
923
1367
|
Space \u9009\u62E9/\u53D6\u6D88 commit
|
|
924
1368
|
Shift+\u2191/\u2193 \u8FDE\u7EED\u9009\u62E9
|
|
925
1369
|
a \u5168\u9009/\u53D6\u6D88\u5168\u9009
|
|
926
1370
|
i \u53CD\u9009
|
|
927
1371
|
r \u9009\u81F3\u5F00\u5934
|
|
928
1372
|
Enter \u786E\u8BA4\u9009\u62E9
|
|
1373
|
+
Esc \u8FD4\u56DE\u4E0A\u4E00\u6B65
|
|
929
1374
|
y/n \u786E\u8BA4/\u53D6\u6D88\u6267\u884C
|
|
1375
|
+
|
|
1376
|
+
\u793A\u4F8B
|
|
1377
|
+
$ git-sync-tui # TUI \u6A21\u5F0F
|
|
1378
|
+
$ git-sync-tui -r upstream -b main --list # \u5217\u51FA commits
|
|
1379
|
+
$ git-sync-tui -r upstream -b main -c abc1234 --yes # \u76F4\u63A5\u6267\u884C
|
|
1380
|
+
$ git-sync-tui -r upstream -b main -c abc1234,def5678 # \u786E\u8BA4\u540E\u6267\u884C
|
|
1381
|
+
$ git-sync-tui -r upstream # TUI \u6A21\u5F0F\uFF0C\u8DF3\u8FC7\u9009\u62E9\u4ED3\u5E93
|
|
930
1382
|
`,
|
|
931
1383
|
{
|
|
932
|
-
importMeta: import.meta
|
|
1384
|
+
importMeta: import.meta,
|
|
1385
|
+
flags: {
|
|
1386
|
+
remote: { type: "string", shortFlag: "r" },
|
|
1387
|
+
branch: { type: "string", shortFlag: "b" },
|
|
1388
|
+
commits: { type: "string", shortFlag: "c" },
|
|
1389
|
+
count: { type: "number", shortFlag: "n", default: 30 },
|
|
1390
|
+
mainline: { type: "boolean", shortFlag: "m", default: false },
|
|
1391
|
+
yes: { type: "boolean", shortFlag: "y", default: false },
|
|
1392
|
+
noStash: { type: "boolean", default: false },
|
|
1393
|
+
list: { type: "boolean", default: false }
|
|
1394
|
+
}
|
|
933
1395
|
}
|
|
934
1396
|
);
|
|
935
|
-
|
|
1397
|
+
var { remote, branch, commits, count, mainline, yes, noStash, list } = cli.flags;
|
|
1398
|
+
var commitList = commits ? commits.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
1399
|
+
var hasAllParams = !!(remote && branch && commitList && commitList.length > 0);
|
|
1400
|
+
var isListMode = !!(list && remote && branch);
|
|
1401
|
+
var isCliMode = hasAllParams || isListMode;
|
|
1402
|
+
if (isCliMode) {
|
|
1403
|
+
runCli({
|
|
1404
|
+
remote,
|
|
1405
|
+
branch,
|
|
1406
|
+
commits: commitList,
|
|
1407
|
+
count,
|
|
1408
|
+
mainline,
|
|
1409
|
+
yes,
|
|
1410
|
+
noStash,
|
|
1411
|
+
list
|
|
1412
|
+
}).catch((err) => {
|
|
1413
|
+
console.error("\u2716 " + (err.message || err));
|
|
1414
|
+
process.exit(1);
|
|
1415
|
+
});
|
|
1416
|
+
} else {
|
|
1417
|
+
render(/* @__PURE__ */ jsx10(App, { initialRemote: remote, initialBranch: branch }));
|
|
1418
|
+
}
|