git-sync-tui 0.1.3 → 0.1.5
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 +623 -184
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ import { render } from "ink";
|
|
|
11
11
|
import meow from "meow";
|
|
12
12
|
|
|
13
13
|
// src/app.tsx
|
|
14
|
-
import { useState as useState7, useEffect as
|
|
14
|
+
import { useState as useState7, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback2 } from "react";
|
|
15
15
|
import { Box as Box9, useApp } from "ink";
|
|
16
16
|
import { Spinner as Spinner6 } from "@inkjs/ui";
|
|
17
17
|
|
|
@@ -32,14 +32,18 @@ function StepProgress({ current }) {
|
|
|
32
32
|
" ",
|
|
33
33
|
isActive ? /* @__PURE__ */ jsx(Text, { bold: true, children: label }) : label
|
|
34
34
|
] }),
|
|
35
|
-
!isLast && /* @__PURE__ */
|
|
35
|
+
!isLast && /* @__PURE__ */ jsxs(Text, { color: isDone ? "green" : "gray", dimColor: !isDone, children: [
|
|
36
|
+
" ",
|
|
37
|
+
"\u2500\u2500\u2500",
|
|
38
|
+
" "
|
|
39
|
+
] })
|
|
36
40
|
] }, label);
|
|
37
41
|
}) });
|
|
38
42
|
}
|
|
39
43
|
function SectionHeader({ title, subtitle }) {
|
|
40
44
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
41
45
|
/* @__PURE__ */ jsxs(Text, { bold: true, color: "cyan", children: [
|
|
42
|
-
"\
|
|
46
|
+
"\u25BE ",
|
|
43
47
|
title
|
|
44
48
|
] }),
|
|
45
49
|
subtitle && /* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: true, children: [
|
|
@@ -50,10 +54,8 @@ function SectionHeader({ title, subtitle }) {
|
|
|
50
54
|
}
|
|
51
55
|
function KeyHints({ hints }) {
|
|
52
56
|
return /* @__PURE__ */ jsx(Box, { gap: 1, flexWrap: "wrap", children: hints.map(({ key, label }) => /* @__PURE__ */ jsxs(Box, { children: [
|
|
53
|
-
/* @__PURE__ */ jsx(Text, {
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
/* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: "]" }),
|
|
56
|
-
/* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: true, children: [
|
|
57
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: "gray", color: "white", bold: true, children: ` ${key} ` }),
|
|
58
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
57
59
|
" ",
|
|
58
60
|
label
|
|
59
61
|
] })
|
|
@@ -61,7 +63,7 @@ function KeyHints({ hints }) {
|
|
|
61
63
|
}
|
|
62
64
|
function InlineKeys({ hints }) {
|
|
63
65
|
return /* @__PURE__ */ jsx(Box, { gap: 1, children: hints.map(({ key, label }, i) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
64
|
-
/* @__PURE__ */ jsxs(Text, { color: "green", children: [
|
|
66
|
+
/* @__PURE__ */ jsxs(Text, { color: "green", bold: true, children: [
|
|
65
67
|
"[",
|
|
66
68
|
key,
|
|
67
69
|
"]"
|
|
@@ -92,13 +94,12 @@ function StatusPanel({ type, title, children }) {
|
|
|
92
94
|
}
|
|
93
95
|
function AppHeader({ step, stashed }) {
|
|
94
96
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
95
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
96
|
-
/* @__PURE__ */ jsx(Text, {
|
|
97
|
-
/* @__PURE__ */ jsx(Text, {
|
|
98
|
-
/* @__PURE__ */ jsx(Text, { color: "
|
|
99
|
-
stashed && /* @__PURE__ */ jsx(Text, { color: "yellow", children: " \u25AA stashed" })
|
|
97
|
+
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
98
|
+
/* @__PURE__ */ jsx(Text, { backgroundColor: "cyan", color: "white", bold: true, children: " git-sync-tui " }),
|
|
99
|
+
/* @__PURE__ */ jsx(Text, { color: "gray", children: "cherry-pick --no-commit" }),
|
|
100
|
+
stashed && /* @__PURE__ */ jsx(Text, { backgroundColor: "yellow", color: "white", bold: true, children: " STASHED " })
|
|
100
101
|
] }),
|
|
101
|
-
/* @__PURE__ */ jsxs(Box, { children: [
|
|
102
|
+
/* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
|
|
102
103
|
/* @__PURE__ */ jsx(Text, { color: "gray", dimColor: true, children: " " }),
|
|
103
104
|
/* @__PURE__ */ jsx(StepProgress, { current: step })
|
|
104
105
|
] })
|
|
@@ -156,38 +157,66 @@ async function addRemote(name, url) {
|
|
|
156
157
|
const git = getGit();
|
|
157
158
|
await git.addRemote(name, url);
|
|
158
159
|
}
|
|
159
|
-
async function getRemoteBranches(
|
|
160
|
+
async function getRemoteBranches(remote2) {
|
|
160
161
|
const git = getGit();
|
|
161
162
|
try {
|
|
162
|
-
await git.
|
|
163
|
+
const lsResult = await git.raw(["ls-remote", "--heads", remote2]);
|
|
164
|
+
if (lsResult.trim()) {
|
|
165
|
+
return lsResult.trim().split("\n").map((line) => line.replace(/^.*refs\/heads\//, "")).filter(Boolean).sort();
|
|
166
|
+
}
|
|
163
167
|
} catch {
|
|
164
168
|
}
|
|
165
169
|
const result = await git.branch(["-r"]);
|
|
166
|
-
const prefix = `${
|
|
167
|
-
|
|
170
|
+
const prefix = `${remote2}/`;
|
|
171
|
+
const branches = result.all.filter((b) => b.startsWith(prefix) && !b.includes("HEAD")).map((b) => b.replace(prefix, "")).sort();
|
|
172
|
+
if (branches.length > 0) return branches;
|
|
173
|
+
throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93 '${remote2}' \u7684\u5206\u652F\u5217\u8868\uFF0C\u8BF7\u68C0\u67E5\u7F51\u7EDC\u6216\u4ED3\u5E93\u5730\u5740`);
|
|
168
174
|
}
|
|
169
|
-
async function getCommits(
|
|
175
|
+
async function getCommits(remote2, branch2, count2 = 100) {
|
|
170
176
|
const git = getGit();
|
|
171
|
-
const ref = `${
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
message: "%s",
|
|
180
|
-
author: "%an",
|
|
181
|
-
date: "%ar"
|
|
177
|
+
const ref = `${remote2}/${branch2}`;
|
|
178
|
+
try {
|
|
179
|
+
await git.raw(["rev-parse", "--verify", ref]);
|
|
180
|
+
} catch {
|
|
181
|
+
try {
|
|
182
|
+
await git.fetch(remote2, branch2);
|
|
183
|
+
} catch {
|
|
184
|
+
throw new Error(`\u65E0\u6CD5\u83B7\u53D6 ${ref}\uFF0C\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u4ED3\u5E93\u8FDE\u63A5`);
|
|
182
185
|
}
|
|
186
|
+
}
|
|
187
|
+
const result = await git.raw([
|
|
188
|
+
"log",
|
|
189
|
+
ref,
|
|
190
|
+
`--max-count=${count2}`,
|
|
191
|
+
"--format=%H%n%h%n%s%n%an%n%ar%n---"
|
|
192
|
+
]);
|
|
193
|
+
if (!result.trim()) return [];
|
|
194
|
+
const entries = result.trim().split("\n---\n").filter(Boolean);
|
|
195
|
+
return entries.map((block) => {
|
|
196
|
+
const [hash, shortHash, message, author, date] = block.split("\n");
|
|
197
|
+
return { hash, shortHash, message: message || "", author: author || "", date: date || "" };
|
|
183
198
|
});
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
199
|
+
}
|
|
200
|
+
async function getUnsyncedCommits(remote2, branch2, count2 = 100) {
|
|
201
|
+
const git = getGit();
|
|
202
|
+
const ref = `${remote2}/${branch2}`;
|
|
203
|
+
const allCommits = await getCommits(remote2, branch2, count2);
|
|
204
|
+
try {
|
|
205
|
+
const result = await git.raw([
|
|
206
|
+
"cherry",
|
|
207
|
+
"HEAD",
|
|
208
|
+
ref
|
|
209
|
+
]);
|
|
210
|
+
const unsyncedHashes = new Set(
|
|
211
|
+
result.trim().split("\n").filter((line) => line.startsWith("+ ")).map((line) => line.substring(2).trim())
|
|
212
|
+
);
|
|
213
|
+
return allCommits.map((c) => ({
|
|
214
|
+
...c,
|
|
215
|
+
synced: !unsyncedHashes.has(c.hash)
|
|
216
|
+
}));
|
|
217
|
+
} catch {
|
|
218
|
+
return allCommits;
|
|
219
|
+
}
|
|
191
220
|
}
|
|
192
221
|
async function getMultiCommitStat(hashes) {
|
|
193
222
|
if (hashes.length === 0) return "";
|
|
@@ -378,10 +407,10 @@ function StashRecovery({ timestamp, onRecover, onSkip }) {
|
|
|
378
407
|
// src/components/remote-select.tsx
|
|
379
408
|
import { useState as useState3 } from "react";
|
|
380
409
|
import { Box as Box4, Text as Text4, useInput as useInput3 } from "ink";
|
|
381
|
-
import {
|
|
410
|
+
import { Spinner as Spinner2, TextInput } from "@inkjs/ui";
|
|
382
411
|
|
|
383
412
|
// src/hooks/use-git.ts
|
|
384
|
-
import { useState as useState2, useEffect as useEffect2, useCallback } from "react";
|
|
413
|
+
import { useState as useState2, useEffect as useEffect2, useCallback, useRef } from "react";
|
|
385
414
|
function useAsync(fn, deps = []) {
|
|
386
415
|
const [state, setState] = useState2({
|
|
387
416
|
data: null,
|
|
@@ -405,17 +434,55 @@ function useAsync(fn, deps = []) {
|
|
|
405
434
|
function useRemotes() {
|
|
406
435
|
return useAsync(() => getRemotes(), []);
|
|
407
436
|
}
|
|
408
|
-
function useBranches(
|
|
437
|
+
function useBranches(remote2) {
|
|
409
438
|
return useAsync(
|
|
410
|
-
() =>
|
|
411
|
-
[
|
|
439
|
+
() => remote2 ? getRemoteBranches(remote2) : Promise.resolve([]),
|
|
440
|
+
[remote2]
|
|
412
441
|
);
|
|
413
442
|
}
|
|
414
|
-
function useCommits(
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
);
|
|
443
|
+
function useCommits(remote2, branch2, pageSize = 100) {
|
|
444
|
+
const [data, setData] = useState2(null);
|
|
445
|
+
const [loading, setLoading] = useState2(true);
|
|
446
|
+
const [loadingMore, setLoadingMore] = useState2(false);
|
|
447
|
+
const [error2, setError] = useState2(null);
|
|
448
|
+
const [hasMore, setHasMore] = useState2(true);
|
|
449
|
+
const loadedRef = useRef(0);
|
|
450
|
+
useEffect2(() => {
|
|
451
|
+
if (!remote2 || !branch2) {
|
|
452
|
+
setData([]);
|
|
453
|
+
setLoading(false);
|
|
454
|
+
setHasMore(false);
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
setData(null);
|
|
458
|
+
setLoading(true);
|
|
459
|
+
setError(null);
|
|
460
|
+
setHasMore(true);
|
|
461
|
+
loadedRef.current = 0;
|
|
462
|
+
getUnsyncedCommits(remote2, branch2, pageSize).then((commits2) => {
|
|
463
|
+
setData(commits2);
|
|
464
|
+
setLoading(false);
|
|
465
|
+
loadedRef.current = commits2.length;
|
|
466
|
+
setHasMore(commits2.length >= pageSize);
|
|
467
|
+
}).catch((err) => {
|
|
468
|
+
setError(err.message);
|
|
469
|
+
setLoading(false);
|
|
470
|
+
});
|
|
471
|
+
}, [remote2, branch2, pageSize]);
|
|
472
|
+
const loadMore = useCallback(async () => {
|
|
473
|
+
if (!remote2 || !branch2 || loadingMore || !hasMore) return;
|
|
474
|
+
setLoadingMore(true);
|
|
475
|
+
try {
|
|
476
|
+
const nextCount = loadedRef.current + pageSize;
|
|
477
|
+
const allCommits = await getUnsyncedCommits(remote2, branch2, nextCount);
|
|
478
|
+
setData(allCommits);
|
|
479
|
+
setHasMore(allCommits.length >= nextCount);
|
|
480
|
+
loadedRef.current = allCommits.length;
|
|
481
|
+
} catch {
|
|
482
|
+
}
|
|
483
|
+
setLoadingMore(false);
|
|
484
|
+
}, [remote2, branch2, pageSize, loadingMore, hasMore]);
|
|
485
|
+
return { data, loading, loadingMore, error: error2, hasMore, loadMore };
|
|
419
486
|
}
|
|
420
487
|
function useCommitStat(hashes) {
|
|
421
488
|
const [stat, setStat] = useState2("");
|
|
@@ -445,28 +512,45 @@ function extractRemoteName(url) {
|
|
|
445
512
|
return lastSegment;
|
|
446
513
|
}
|
|
447
514
|
function RemoteSelect({ onSelect, onBack }) {
|
|
448
|
-
const { data: remotes, loading, error, reload } = useRemotes();
|
|
515
|
+
const { data: remotes, loading, error: error2, reload } = useRemotes();
|
|
449
516
|
const [phase, setPhase] = useState3("list");
|
|
517
|
+
const [cursorIndex, setCursorIndex] = useState3(0);
|
|
450
518
|
const [customUrl, setCustomUrl] = useState3("");
|
|
451
519
|
const [addError, setAddError] = useState3(null);
|
|
452
|
-
|
|
520
|
+
const totalItems = (remotes?.length || 0) + 1;
|
|
521
|
+
useInput3((input, key) => {
|
|
522
|
+
if (phase !== "list") {
|
|
523
|
+
if (key.escape) {
|
|
524
|
+
if (phase === "input-name") {
|
|
525
|
+
setPhase("input-url");
|
|
526
|
+
} else if (phase === "input-url") {
|
|
527
|
+
setPhase("list");
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
453
532
|
if (key.escape) {
|
|
454
|
-
|
|
533
|
+
onBack?.();
|
|
534
|
+
} else if (key.upArrow) {
|
|
535
|
+
setCursorIndex((prev) => Math.max(0, prev - 1));
|
|
536
|
+
} else if (key.downArrow) {
|
|
537
|
+
setCursorIndex((prev) => Math.min(totalItems - 1, prev + 1));
|
|
538
|
+
} else if (key.return) {
|
|
539
|
+
if (remotes && cursorIndex < remotes.length) {
|
|
540
|
+
onSelect(remotes[cursorIndex].name);
|
|
541
|
+
} else {
|
|
455
542
|
setPhase("input-url");
|
|
456
|
-
} else if (phase === "input-url") {
|
|
457
|
-
setPhase("list");
|
|
458
|
-
} else if (phase === "list") {
|
|
459
|
-
onBack?.();
|
|
460
543
|
}
|
|
461
544
|
}
|
|
462
545
|
});
|
|
463
546
|
if (loading) {
|
|
464
547
|
return /* @__PURE__ */ jsx4(Spinner2, { label: "\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93..." });
|
|
465
548
|
}
|
|
466
|
-
if (
|
|
549
|
+
if (error2) {
|
|
467
550
|
return /* @__PURE__ */ jsxs4(Text4, { color: "red", children: [
|
|
468
|
-
"\u2716
|
|
469
|
-
|
|
551
|
+
"\u2716 ",
|
|
552
|
+
"\u83B7\u53D6\u8FDC\u7A0B\u4ED3\u5E93\u5931\u8D25: ",
|
|
553
|
+
error2
|
|
470
554
|
] });
|
|
471
555
|
}
|
|
472
556
|
if (phase === "adding") {
|
|
@@ -496,7 +580,10 @@ function RemoteSelect({ onSelect, onBack }) {
|
|
|
496
580
|
}
|
|
497
581
|
)
|
|
498
582
|
] }),
|
|
499
|
-
/* @__PURE__ */
|
|
583
|
+
/* @__PURE__ */ jsxs4(Text4, { color: "gray", dimColor: true, children: [
|
|
584
|
+
" ",
|
|
585
|
+
"\u652F\u6301 HTTPS / SSH \u5730\u5740"
|
|
586
|
+
] })
|
|
500
587
|
] });
|
|
501
588
|
}
|
|
502
589
|
if (phase === "input-name") {
|
|
@@ -540,99 +627,187 @@ function RemoteSelect({ onSelect, onBack }) {
|
|
|
540
627
|
] })
|
|
541
628
|
] });
|
|
542
629
|
}
|
|
543
|
-
const
|
|
544
|
-
|
|
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: [
|
|
630
|
+
const maxNameLen = Math.max(...(remotes || []).map((r) => r.name.length), 0);
|
|
631
|
+
return /* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", children: [
|
|
554
632
|
/* @__PURE__ */ jsx4(SectionHeader, { title: "\u9009\u62E9\u8FDC\u7A0B\u4ED3\u5E93" }),
|
|
555
|
-
/* @__PURE__ */
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
633
|
+
/* @__PURE__ */ jsxs4(Box4, { flexDirection: "column", marginTop: 1, children: [
|
|
634
|
+
(remotes || []).map((r, i) => {
|
|
635
|
+
const isCursor = i === cursorIndex;
|
|
636
|
+
return /* @__PURE__ */ jsxs4(Box4, { children: [
|
|
637
|
+
/* @__PURE__ */ jsx4(Text4, { color: isCursor ? "cyan" : "gray", children: isCursor ? "\u203A" : " " }),
|
|
638
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
639
|
+
/* @__PURE__ */ jsx4(Text4, { color: isCursor ? "cyan" : "white", bold: isCursor, children: r.name.padEnd(maxNameLen + 2) }),
|
|
640
|
+
/* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: r.fetchUrl })
|
|
641
|
+
] }, r.name);
|
|
642
|
+
}),
|
|
643
|
+
/* @__PURE__ */ jsxs4(Box4, { children: [
|
|
644
|
+
/* @__PURE__ */ jsx4(Text4, { color: cursorIndex === (remotes?.length || 0) ? "cyan" : "gray", children: cursorIndex === (remotes?.length || 0) ? "\u203A" : " " }),
|
|
645
|
+
/* @__PURE__ */ jsx4(Text4, { children: " " }),
|
|
646
|
+
/* @__PURE__ */ jsx4(Text4, { color: "green", dimColor: cursorIndex !== (remotes?.length || 0), children: "+ \u6DFB\u52A0\u8FDC\u7A0B\u4ED3\u5E93..." })
|
|
647
|
+
] })
|
|
648
|
+
] })
|
|
568
649
|
] });
|
|
569
650
|
}
|
|
570
651
|
|
|
571
652
|
// src/components/branch-select.tsx
|
|
572
653
|
import { useState as useState4, useMemo } from "react";
|
|
573
654
|
import { Box as Box5, Text as Text5, useInput as useInput4 } from "ink";
|
|
574
|
-
import {
|
|
655
|
+
import { Spinner as Spinner3, TextInput as TextInput2 } from "@inkjs/ui";
|
|
575
656
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
576
|
-
function BranchSelect({ remote, onSelect, onBack }) {
|
|
577
|
-
const { data: branches, loading, error } = useBranches(
|
|
657
|
+
function BranchSelect({ remote: remote2, onSelect, onBack }) {
|
|
658
|
+
const { data: branches, loading, error: error2 } = useBranches(remote2);
|
|
578
659
|
const [filter, setFilter] = useState4("");
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
});
|
|
582
|
-
const filteredOptions = useMemo(() => {
|
|
660
|
+
const [cursorIndex, setCursorIndex] = useState4(0);
|
|
661
|
+
const filteredBranches = useMemo(() => {
|
|
583
662
|
if (!branches) return [];
|
|
584
|
-
|
|
585
|
-
return filtered.map((b) => ({ label: b, value: b }));
|
|
663
|
+
return filter ? branches.filter((b) => b.toLowerCase().includes(filter.toLowerCase())) : branches;
|
|
586
664
|
}, [branches, filter]);
|
|
665
|
+
const visibleCount = 10;
|
|
666
|
+
const startIdx = Math.max(0, Math.min(cursorIndex - Math.floor(visibleCount / 2), filteredBranches.length - visibleCount));
|
|
667
|
+
const visibleBranches = filteredBranches.slice(startIdx, startIdx + visibleCount);
|
|
668
|
+
useInput4((input, key) => {
|
|
669
|
+
if (key.escape) {
|
|
670
|
+
onBack?.();
|
|
671
|
+
} else if (key.upArrow) {
|
|
672
|
+
setCursorIndex((prev) => Math.max(0, prev - 1));
|
|
673
|
+
} else if (key.downArrow) {
|
|
674
|
+
setCursorIndex((prev) => Math.min(filteredBranches.length - 1, prev + 1));
|
|
675
|
+
} else if (key.return) {
|
|
676
|
+
if (filteredBranches.length > 0) {
|
|
677
|
+
onSelect(filteredBranches[cursorIndex]);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
});
|
|
587
681
|
if (loading) {
|
|
588
|
-
return /* @__PURE__ */ jsx5(Spinner3, { label: `\u83B7\u53D6 ${
|
|
682
|
+
return /* @__PURE__ */ jsx5(Spinner3, { label: `\u83B7\u53D6 ${remote2} \u7684\u5206\u652F\u5217\u8868...` });
|
|
589
683
|
}
|
|
590
|
-
if (
|
|
684
|
+
if (error2) {
|
|
591
685
|
return /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
592
|
-
"\u2716
|
|
593
|
-
|
|
686
|
+
"\u2716 ",
|
|
687
|
+
"\u83B7\u53D6\u5206\u652F\u5217\u8868\u5931\u8D25: ",
|
|
688
|
+
error2
|
|
594
689
|
] });
|
|
595
690
|
}
|
|
596
691
|
if (!branches || branches.length === 0) {
|
|
597
|
-
return /* @__PURE__ */
|
|
692
|
+
return /* @__PURE__ */ jsxs5(Text5, { color: "red", children: [
|
|
693
|
+
"\u2716 ",
|
|
694
|
+
"\u672A\u627E\u5230\u8FDC\u7A0B\u5206\u652F"
|
|
695
|
+
] });
|
|
598
696
|
}
|
|
599
|
-
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column",
|
|
600
|
-
/* @__PURE__ */ jsx5(SectionHeader, { title:
|
|
601
|
-
/* @__PURE__ */ jsxs5(Box5, { children: [
|
|
602
|
-
/* @__PURE__ */ jsx5(Text5, { color: "
|
|
697
|
+
return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
|
|
698
|
+
/* @__PURE__ */ jsx5(SectionHeader, { title: "\u9009\u62E9\u5206\u652F", subtitle: `${remote2} \xB7 ${branches.length} \u4E2A\u5206\u652F` }),
|
|
699
|
+
/* @__PURE__ */ jsxs5(Box5, { marginTop: 1, children: [
|
|
700
|
+
/* @__PURE__ */ jsx5(Text5, { color: "cyan", children: "/ " }),
|
|
603
701
|
/* @__PURE__ */ jsx5(
|
|
604
702
|
TextInput2,
|
|
605
703
|
{
|
|
606
704
|
placeholder: "\u8F93\u5165\u5173\u952E\u5B57\u8FC7\u6EE4...",
|
|
607
|
-
onChange:
|
|
705
|
+
onChange: (val) => {
|
|
706
|
+
setFilter(val);
|
|
707
|
+
setCursorIndex(0);
|
|
708
|
+
}
|
|
608
709
|
}
|
|
609
710
|
),
|
|
610
711
|
filter && /* @__PURE__ */ jsxs5(Text5, { color: "gray", dimColor: true, children: [
|
|
611
712
|
" \xB7 \u5339\u914D ",
|
|
612
|
-
|
|
713
|
+
filteredBranches.length
|
|
613
714
|
] })
|
|
614
715
|
] }),
|
|
615
|
-
|
|
716
|
+
filteredBranches.length > 0 ? /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
|
|
717
|
+
startIdx > 0 && /* @__PURE__ */ jsxs5(Text5, { color: "gray", dimColor: true, children: [
|
|
718
|
+
" ",
|
|
719
|
+
"\u2191 ",
|
|
720
|
+
startIdx,
|
|
721
|
+
" more"
|
|
722
|
+
] }),
|
|
723
|
+
visibleBranches.map((b, i) => {
|
|
724
|
+
const actualIdx = startIdx + i;
|
|
725
|
+
const isCursor = actualIdx === cursorIndex;
|
|
726
|
+
return /* @__PURE__ */ jsxs5(Box5, { children: [
|
|
727
|
+
/* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : "gray", children: isCursor ? "\u203A" : " " }),
|
|
728
|
+
/* @__PURE__ */ jsx5(Text5, { children: " " }),
|
|
729
|
+
/* @__PURE__ */ jsx5(Text5, { color: isCursor ? "cyan" : "white", bold: isCursor, children: b })
|
|
730
|
+
] }, b);
|
|
731
|
+
}),
|
|
732
|
+
startIdx + visibleCount < filteredBranches.length && /* @__PURE__ */ jsxs5(Text5, { color: "gray", dimColor: true, children: [
|
|
733
|
+
" ",
|
|
734
|
+
"\u2193 ",
|
|
735
|
+
filteredBranches.length - startIdx - visibleCount,
|
|
736
|
+
" more"
|
|
737
|
+
] })
|
|
738
|
+
] }) : /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
|
|
739
|
+
"\u25B2 ",
|
|
740
|
+
"\u65E0\u5339\u914D\u5206\u652F"
|
|
741
|
+
] })
|
|
616
742
|
] });
|
|
617
743
|
}
|
|
618
744
|
|
|
619
745
|
// src/components/commit-list.tsx
|
|
620
|
-
import { useState as useState5, useMemo as useMemo2, useRef } from "react";
|
|
746
|
+
import { useState as useState5, useMemo as useMemo2, useRef as useRef2, useEffect as useEffect3 } from "react";
|
|
621
747
|
import { Box as Box6, Text as Text6, useInput as useInput5 } from "ink";
|
|
622
748
|
import { Spinner as Spinner4 } from "@inkjs/ui";
|
|
623
749
|
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
624
|
-
function
|
|
625
|
-
const
|
|
750
|
+
function StatPanel({ stat, loading, count: count2 }) {
|
|
751
|
+
const STAT_HEIGHT = 8;
|
|
752
|
+
const [scrollOffset, setScrollOffset] = useState5(0);
|
|
753
|
+
const lines = useMemo2(() => stat ? stat.split("\n") : [], [stat]);
|
|
754
|
+
const canScroll = lines.length > STAT_HEIGHT;
|
|
755
|
+
useEffect3(() => {
|
|
756
|
+
setScrollOffset(0);
|
|
757
|
+
}, [stat]);
|
|
758
|
+
useInput5((input) => {
|
|
759
|
+
if (!canScroll) return;
|
|
760
|
+
if (input === "j") {
|
|
761
|
+
setScrollOffset((prev) => Math.min(prev + 1, lines.length - STAT_HEIGHT));
|
|
762
|
+
} else if (input === "k") {
|
|
763
|
+
setScrollOffset((prev) => Math.max(0, prev - 1));
|
|
764
|
+
}
|
|
765
|
+
});
|
|
766
|
+
const visibleLines = canScroll ? lines.slice(scrollOffset, scrollOffset + STAT_HEIGHT) : lines;
|
|
767
|
+
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, children: [
|
|
768
|
+
/* @__PURE__ */ jsxs6(Box6, { justifyContent: "space-between", children: [
|
|
769
|
+
/* @__PURE__ */ jsxs6(Text6, { bold: true, color: "cyan", children: [
|
|
770
|
+
"\u25C6 ",
|
|
771
|
+
"\u5DF2\u9009 ",
|
|
772
|
+
count2,
|
|
773
|
+
" \u4E2A commit \xB7 diff --stat"
|
|
774
|
+
] }),
|
|
775
|
+
canScroll && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
776
|
+
scrollOffset + 1,
|
|
777
|
+
"-",
|
|
778
|
+
Math.min(scrollOffset + STAT_HEIGHT, lines.length),
|
|
779
|
+
"/",
|
|
780
|
+
lines.length,
|
|
781
|
+
" [j/k]"
|
|
782
|
+
] })
|
|
783
|
+
] }),
|
|
784
|
+
/* @__PURE__ */ jsx6(Box6, { flexDirection: "column", height: STAT_HEIGHT, children: loading ? /* @__PURE__ */ jsx6(Spinner4, { label: "\u52A0\u8F7D\u4E2D..." }) : visibleLines.length > 0 ? visibleLines.map((line, i) => /* @__PURE__ */ jsx6(Text6, { color: "gray", children: line }, scrollOffset + i)) : /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "(\u65E0\u53D8\u66F4)" }) })
|
|
785
|
+
] });
|
|
786
|
+
}
|
|
787
|
+
function CommitList({ remote: remote2, branch: branch2, onSelect, onBack }) {
|
|
788
|
+
const { data: commits2, loading, loadingMore, error: error2, hasMore, loadMore } = useCommits(remote2, branch2, 100);
|
|
626
789
|
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
627
790
|
const [selectedHashes, setSelectedHashes] = useState5(/* @__PURE__ */ new Set());
|
|
628
791
|
const [shiftMode, setShiftMode] = useState5(false);
|
|
629
|
-
const anchorIndexRef =
|
|
792
|
+
const anchorIndexRef = useRef2(null);
|
|
630
793
|
const selectedKey = useMemo2(() => Array.from(selectedHashes).sort().join(","), [selectedHashes]);
|
|
631
794
|
const selectedArray = useMemo2(() => Array.from(selectedHashes), [selectedKey]);
|
|
632
795
|
const { stat, loading: statLoading } = useCommitStat(selectedArray);
|
|
796
|
+
const syncedCount = useMemo2(() => {
|
|
797
|
+
if (!commits2) return 0;
|
|
798
|
+
return commits2.filter((c) => c.synced).length;
|
|
799
|
+
}, [commits2]);
|
|
800
|
+
useEffect3(() => {
|
|
801
|
+
if (!commits2 || !hasMore || loadingMore) return;
|
|
802
|
+
if (selectedIndex >= commits2.length - 5) {
|
|
803
|
+
loadMore();
|
|
804
|
+
}
|
|
805
|
+
}, [selectedIndex, commits2?.length, hasMore, loadingMore, loadMore]);
|
|
633
806
|
const toggleCurrent = () => {
|
|
634
|
-
if (!
|
|
635
|
-
const
|
|
807
|
+
if (!commits2 || commits2.length === 0) return;
|
|
808
|
+
const commit = commits2[selectedIndex];
|
|
809
|
+
if (commit.synced) return;
|
|
810
|
+
const hash = commit.hash;
|
|
636
811
|
setSelectedHashes((prev) => {
|
|
637
812
|
const next = new Set(prev);
|
|
638
813
|
if (next.has(hash)) {
|
|
@@ -646,49 +821,54 @@ function CommitList({ remote, branch, onSelect, onBack }) {
|
|
|
646
821
|
});
|
|
647
822
|
};
|
|
648
823
|
const selectRange = (anchor, current) => {
|
|
649
|
-
if (!
|
|
824
|
+
if (!commits2) return;
|
|
650
825
|
const start = Math.min(anchor, current);
|
|
651
826
|
const end = Math.max(anchor, current);
|
|
652
827
|
setSelectedHashes((prev) => {
|
|
653
828
|
const next = new Set(prev);
|
|
654
829
|
for (let i = start; i <= end; i++) {
|
|
655
|
-
|
|
830
|
+
if (!commits2[i].synced) {
|
|
831
|
+
next.add(commits2[i].hash);
|
|
832
|
+
}
|
|
656
833
|
}
|
|
657
834
|
return next;
|
|
658
835
|
});
|
|
659
836
|
};
|
|
660
837
|
const toggleAll = () => {
|
|
661
|
-
if (!
|
|
838
|
+
if (!commits2 || commits2.length === 0) return;
|
|
839
|
+
const unsyncedCommits = commits2.filter((c) => !c.synced);
|
|
662
840
|
setSelectedHashes((prev) => {
|
|
663
|
-
if (prev.size ===
|
|
841
|
+
if (prev.size === unsyncedCommits.length) {
|
|
664
842
|
anchorIndexRef.current = null;
|
|
665
843
|
return /* @__PURE__ */ new Set();
|
|
666
844
|
}
|
|
667
|
-
return new Set(
|
|
845
|
+
return new Set(unsyncedCommits.map((c) => c.hash));
|
|
668
846
|
});
|
|
669
847
|
};
|
|
670
848
|
const invertSelection = () => {
|
|
671
|
-
if (!
|
|
849
|
+
if (!commits2 || commits2.length === 0) return;
|
|
672
850
|
setSelectedHashes((prev) => {
|
|
673
851
|
const next = /* @__PURE__ */ new Set();
|
|
674
|
-
for (const c of
|
|
675
|
-
if (!prev.has(c.hash)) next.add(c.hash);
|
|
852
|
+
for (const c of commits2) {
|
|
853
|
+
if (!c.synced && !prev.has(c.hash)) next.add(c.hash);
|
|
676
854
|
}
|
|
677
855
|
return next;
|
|
678
856
|
});
|
|
679
857
|
};
|
|
680
858
|
const selectToCurrent = () => {
|
|
681
|
-
if (!
|
|
859
|
+
if (!commits2 || commits2.length === 0) return;
|
|
682
860
|
setSelectedHashes((prev) => {
|
|
683
861
|
const next = new Set(prev);
|
|
684
862
|
for (let i = 0; i <= selectedIndex; i++) {
|
|
685
|
-
|
|
863
|
+
if (!commits2[i].synced) {
|
|
864
|
+
next.add(commits2[i].hash);
|
|
865
|
+
}
|
|
686
866
|
}
|
|
687
867
|
return next;
|
|
688
868
|
});
|
|
689
869
|
};
|
|
690
870
|
useInput5((input, key) => {
|
|
691
|
-
if (!
|
|
871
|
+
if (!commits2 || commits2.length === 0) return;
|
|
692
872
|
if (key.shift) {
|
|
693
873
|
if (!shiftMode) {
|
|
694
874
|
setShiftMode(true);
|
|
@@ -701,7 +881,7 @@ function CommitList({ remote, branch, onSelect, onBack }) {
|
|
|
701
881
|
setSelectedIndex(newIndex);
|
|
702
882
|
selectRange(anchorIndexRef.current, newIndex);
|
|
703
883
|
} else if (key.downArrow) {
|
|
704
|
-
const newIndex = Math.min(
|
|
884
|
+
const newIndex = Math.min(commits2.length - 1, selectedIndex + 1);
|
|
705
885
|
setSelectedIndex(newIndex);
|
|
706
886
|
selectRange(anchorIndexRef.current, newIndex);
|
|
707
887
|
} else if (input === " ") {
|
|
@@ -717,7 +897,7 @@ function CommitList({ remote, branch, onSelect, onBack }) {
|
|
|
717
897
|
if (key.upArrow) {
|
|
718
898
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
719
899
|
} else if (key.downArrow) {
|
|
720
|
-
setSelectedIndex((prev) => Math.min(
|
|
900
|
+
setSelectedIndex((prev) => Math.min(commits2.length - 1, prev + 1));
|
|
721
901
|
} else if (input === " ") {
|
|
722
902
|
toggleCurrent();
|
|
723
903
|
} else if (input === "a" || input === "A") {
|
|
@@ -730,46 +910,62 @@ function CommitList({ remote, branch, onSelect, onBack }) {
|
|
|
730
910
|
onBack?.();
|
|
731
911
|
} else if (key.return) {
|
|
732
912
|
if (selectedHashes.size > 0) {
|
|
733
|
-
onSelect(Array.from(selectedHashes),
|
|
913
|
+
onSelect(Array.from(selectedHashes), commits2);
|
|
734
914
|
}
|
|
735
915
|
}
|
|
736
916
|
});
|
|
737
917
|
if (loading) {
|
|
738
|
-
return /* @__PURE__ */ jsx6(Spinner4, { label: `\u83B7\u53D6 ${
|
|
918
|
+
return /* @__PURE__ */ jsx6(Spinner4, { label: `\u83B7\u53D6 ${remote2}/${branch2} \u7684 commit \u5217\u8868...` });
|
|
739
919
|
}
|
|
740
|
-
if (
|
|
920
|
+
if (error2) {
|
|
741
921
|
return /* @__PURE__ */ jsxs6(Text6, { color: "red", children: [
|
|
742
|
-
"\u2716
|
|
743
|
-
|
|
922
|
+
"\u2716 ",
|
|
923
|
+
"\u83B7\u53D6 commit \u5217\u8868\u5931\u8D25: ",
|
|
924
|
+
error2
|
|
744
925
|
] });
|
|
745
926
|
}
|
|
746
|
-
if (!
|
|
747
|
-
return /* @__PURE__ */
|
|
927
|
+
if (!commits2 || commits2.length === 0) {
|
|
928
|
+
return /* @__PURE__ */ jsxs6(Text6, { color: "yellow", children: [
|
|
929
|
+
"\u25B2 ",
|
|
930
|
+
"\u8BE5\u5206\u652F\u6CA1\u6709 commit"
|
|
931
|
+
] });
|
|
748
932
|
}
|
|
749
933
|
const visibleCount = 10;
|
|
750
|
-
const startIdx = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2),
|
|
751
|
-
const visibleCommits =
|
|
934
|
+
const startIdx = Math.max(0, Math.min(selectedIndex - Math.floor(visibleCount / 2), commits2.length - visibleCount));
|
|
935
|
+
const visibleCommits = commits2.slice(startIdx, startIdx + visibleCount);
|
|
936
|
+
const unsyncedTotal = commits2.length - syncedCount;
|
|
752
937
|
return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", gap: 1, children: [
|
|
753
938
|
/* @__PURE__ */ jsx6(SectionHeader, { title: "\u9009\u62E9\u8981\u540C\u6B65\u7684 commit" }),
|
|
754
939
|
/* @__PURE__ */ jsxs6(Box6, { gap: 2, children: [
|
|
755
940
|
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
756
|
-
|
|
941
|
+
remote2,
|
|
757
942
|
"/",
|
|
758
|
-
|
|
943
|
+
branch2
|
|
759
944
|
] }),
|
|
760
945
|
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
761
|
-
|
|
762
|
-
" commits"
|
|
946
|
+
commits2.length,
|
|
947
|
+
" commits",
|
|
948
|
+
hasMore ? "+" : ""
|
|
949
|
+
] }),
|
|
950
|
+
syncedCount > 0 && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
951
|
+
syncedCount,
|
|
952
|
+
" \u5DF2\u540C\u6B65"
|
|
953
|
+
] }),
|
|
954
|
+
/* @__PURE__ */ jsxs6(Text6, { color: "green", children: [
|
|
955
|
+
unsyncedTotal,
|
|
956
|
+
" \u5F85\u540C\u6B65"
|
|
763
957
|
] }),
|
|
764
958
|
/* @__PURE__ */ jsxs6(Text6, { color: selectedHashes.size > 0 ? "cyan" : "gray", bold: selectedHashes.size > 0, children: [
|
|
765
959
|
"\u5DF2\u9009 ",
|
|
766
960
|
selectedHashes.size
|
|
767
961
|
] }),
|
|
768
|
-
shiftMode && /* @__PURE__ */ jsx6(Text6, { color: "yellow", bold: true, children: "SHIFT" })
|
|
962
|
+
shiftMode && /* @__PURE__ */ jsx6(Text6, { color: "yellow", bold: true, children: "SHIFT" }),
|
|
963
|
+
loadingMore && /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: "\u52A0\u8F7D\u4E2D..." })
|
|
769
964
|
] }),
|
|
770
965
|
/* @__PURE__ */ jsxs6(Box6, { flexDirection: "column", children: [
|
|
771
966
|
startIdx > 0 && /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
772
|
-
"
|
|
967
|
+
" ",
|
|
968
|
+
"\u2191 ",
|
|
773
969
|
startIdx,
|
|
774
970
|
" more"
|
|
775
971
|
] }),
|
|
@@ -778,30 +974,60 @@ function CommitList({ remote, branch, onSelect, onBack }) {
|
|
|
778
974
|
const isSelected = selectedHashes.has(c.hash);
|
|
779
975
|
const isCursor = actualIdx === selectedIndex;
|
|
780
976
|
const isAnchor = actualIdx === anchorIndexRef.current;
|
|
977
|
+
const isSynced = !!c.synced;
|
|
781
978
|
return /* @__PURE__ */ jsxs6(Box6, { children: [
|
|
782
|
-
/* @__PURE__ */ jsxs6(
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
979
|
+
/* @__PURE__ */ jsxs6(
|
|
980
|
+
Text6,
|
|
981
|
+
{
|
|
982
|
+
backgroundColor: isCursor ? isSynced ? "gray" : "blue" : void 0,
|
|
983
|
+
color: isSynced ? "gray" : isSelected ? "green" : "white",
|
|
984
|
+
dimColor: isSynced,
|
|
985
|
+
children: [
|
|
986
|
+
isCursor ? "\u25B8 " : " ",
|
|
987
|
+
isSynced ? "\u2713" : isAnchor ? "\u2693" : isSelected ? "\u25CF" : "\u25CB",
|
|
988
|
+
" "
|
|
989
|
+
]
|
|
990
|
+
}
|
|
991
|
+
),
|
|
992
|
+
/* @__PURE__ */ jsx6(
|
|
993
|
+
Text6,
|
|
994
|
+
{
|
|
995
|
+
backgroundColor: isCursor ? isSynced ? "gray" : "blue" : void 0,
|
|
996
|
+
color: isSynced ? "gray" : "yellow",
|
|
997
|
+
dimColor: isSynced,
|
|
998
|
+
children: c.shortHash
|
|
999
|
+
}
|
|
1000
|
+
),
|
|
1001
|
+
/* @__PURE__ */ jsxs6(
|
|
1002
|
+
Text6,
|
|
1003
|
+
{
|
|
1004
|
+
backgroundColor: isCursor ? isSynced ? "gray" : "blue" : void 0,
|
|
1005
|
+
color: isSynced ? "gray" : isSelected ? "green" : "white",
|
|
1006
|
+
dimColor: isSynced,
|
|
1007
|
+
children: [
|
|
1008
|
+
" ",
|
|
1009
|
+
c.message
|
|
1010
|
+
]
|
|
1011
|
+
}
|
|
1012
|
+
),
|
|
792
1013
|
/* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
793
1014
|
" ",
|
|
794
1015
|
c.author,
|
|
795
1016
|
" \xB7 ",
|
|
796
1017
|
c.date
|
|
797
|
-
] })
|
|
1018
|
+
] }),
|
|
1019
|
+
isSynced && /* @__PURE__ */ jsx6(Text6, { color: "gray", dimColor: true, children: " [\u5DF2\u540C\u6B65]" })
|
|
798
1020
|
] }, c.hash);
|
|
799
1021
|
}),
|
|
800
|
-
startIdx + visibleCount <
|
|
801
|
-
"
|
|
802
|
-
|
|
1022
|
+
startIdx + visibleCount < commits2.length ? /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
1023
|
+
" ",
|
|
1024
|
+
"\u2193 ",
|
|
1025
|
+
commits2.length - startIdx - visibleCount,
|
|
803
1026
|
" more"
|
|
804
|
-
] })
|
|
1027
|
+
] }) : hasMore ? /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
|
|
1028
|
+
" ",
|
|
1029
|
+
"\u2193 \u6EDA\u52A8\u52A0\u8F7D\u66F4\u591A..."
|
|
1030
|
+
] }) : null
|
|
805
1031
|
] }),
|
|
806
1032
|
/* @__PURE__ */ jsx6(KeyHints, { hints: [
|
|
807
1033
|
{ key: "\u2191\u2193", label: "\u5BFC\u822A" },
|
|
@@ -813,14 +1039,14 @@ function CommitList({ remote, branch, onSelect, onBack }) {
|
|
|
813
1039
|
{ key: "Enter", label: "\u786E\u8BA4" },
|
|
814
1040
|
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
815
1041
|
] }),
|
|
816
|
-
selectedHashes.size > 0 && /* @__PURE__ */ jsx6(
|
|
1042
|
+
selectedHashes.size > 0 && /* @__PURE__ */ jsx6(StatPanel, { stat, loading: statLoading, count: selectedHashes.size })
|
|
817
1043
|
] });
|
|
818
1044
|
}
|
|
819
1045
|
|
|
820
1046
|
// src/components/confirm-panel.tsx
|
|
821
1047
|
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
822
1048
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
823
|
-
function ConfirmPanel({ commits, selectedHashes, hasMerge, useMainline, onToggleMainline, onConfirm, onCancel }) {
|
|
1049
|
+
function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline, onToggleMainline, onConfirm, onCancel }) {
|
|
824
1050
|
useInput6((input, key) => {
|
|
825
1051
|
if (key.escape) {
|
|
826
1052
|
onCancel();
|
|
@@ -832,7 +1058,7 @@ function ConfirmPanel({ commits, selectedHashes, hasMerge, useMainline, onToggle
|
|
|
832
1058
|
onToggleMainline();
|
|
833
1059
|
}
|
|
834
1060
|
});
|
|
835
|
-
const selectedCommits = selectedHashes.map((hash) =>
|
|
1061
|
+
const selectedCommits = selectedHashes.map((hash) => commits2.find((c) => c.hash === hash)).filter(Boolean);
|
|
836
1062
|
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
|
|
837
1063
|
/* @__PURE__ */ jsx7(SectionHeader, { title: "\u786E\u8BA4\u6267\u884C" }),
|
|
838
1064
|
/* @__PURE__ */ jsx7(StatusPanel, { type: "info", title: `cherry-pick --no-commit \xB7 ${selectedCommits.length} \u4E2A commit`, children: selectedCommits.map((c) => /* @__PURE__ */ jsxs7(Box7, { children: [
|
|
@@ -875,7 +1101,7 @@ function ConfirmPanel({ commits, selectedHashes, hasMerge, useMainline, onToggle
|
|
|
875
1101
|
}
|
|
876
1102
|
|
|
877
1103
|
// src/components/result-panel.tsx
|
|
878
|
-
import { useState as useState6, useEffect as
|
|
1104
|
+
import { useState as useState6, useEffect as useEffect4 } from "react";
|
|
879
1105
|
import { Box as Box8, Text as Text8 } from "ink";
|
|
880
1106
|
import { Spinner as Spinner5 } from "@inkjs/ui";
|
|
881
1107
|
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -892,7 +1118,7 @@ function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, on
|
|
|
892
1118
|
if (ok) onStashRestored();
|
|
893
1119
|
return ok;
|
|
894
1120
|
};
|
|
895
|
-
|
|
1121
|
+
useEffect4(() => {
|
|
896
1122
|
async function run() {
|
|
897
1123
|
const res = await cherryPick(selectedHashes, useMainline);
|
|
898
1124
|
setResult(res);
|
|
@@ -966,22 +1192,23 @@ var STEP_NUMBER = {
|
|
|
966
1192
|
result: 5
|
|
967
1193
|
};
|
|
968
1194
|
var STEP_DEBOUNCE = 100;
|
|
969
|
-
function App() {
|
|
1195
|
+
function App({ initialRemote, initialBranch }) {
|
|
970
1196
|
const { exit } = useApp();
|
|
1197
|
+
const entryStep = initialRemote && initialBranch ? "commits" : initialRemote ? "branch" : "remote";
|
|
971
1198
|
const [step, setStepRaw] = useState7("checking");
|
|
972
1199
|
const [inputReady, setInputReady] = useState7(true);
|
|
973
|
-
const [
|
|
974
|
-
const [
|
|
1200
|
+
const [remote2, setRemote] = useState7(initialRemote || "");
|
|
1201
|
+
const [branch2, setBranch] = useState7(initialBranch || "");
|
|
975
1202
|
const [selectedHashes, setSelectedHashes] = useState7([]);
|
|
976
|
-
const [
|
|
1203
|
+
const [commits2, setCommits] = useState7([]);
|
|
977
1204
|
const [hasMerge, setHasMerge] = useState7(false);
|
|
978
1205
|
const [useMainline, setUseMainline] = useState7(false);
|
|
979
1206
|
const [stashed, setStashed] = useState7(false);
|
|
980
1207
|
const [guardTimestamp, setGuardTimestamp] = useState7();
|
|
981
|
-
const stashedRef =
|
|
982
|
-
const stashRestoredRef =
|
|
983
|
-
const mountedRef =
|
|
984
|
-
const debounceTimer =
|
|
1208
|
+
const stashedRef = useRef3(false);
|
|
1209
|
+
const stashRestoredRef = useRef3(false);
|
|
1210
|
+
const mountedRef = useRef3(true);
|
|
1211
|
+
const debounceTimer = useRef3(null);
|
|
985
1212
|
const setStep = useCallback2((newStep) => {
|
|
986
1213
|
setInputReady(false);
|
|
987
1214
|
setStepRaw(newStep);
|
|
@@ -1008,7 +1235,7 @@ function App() {
|
|
|
1008
1235
|
stashRestoredRef.current = true;
|
|
1009
1236
|
removeStashGuard();
|
|
1010
1237
|
}, []);
|
|
1011
|
-
|
|
1238
|
+
useEffect5(() => {
|
|
1012
1239
|
mountedRef.current = true;
|
|
1013
1240
|
async function check() {
|
|
1014
1241
|
const guard = await checkStashGuard();
|
|
@@ -1020,7 +1247,7 @@ function App() {
|
|
|
1020
1247
|
}
|
|
1021
1248
|
const clean = await isWorkingDirClean();
|
|
1022
1249
|
if (!mountedRef.current) return;
|
|
1023
|
-
setStep(clean ?
|
|
1250
|
+
setStep(clean ? entryStep : "stash-prompt");
|
|
1024
1251
|
}
|
|
1025
1252
|
check();
|
|
1026
1253
|
const onSignal = () => {
|
|
@@ -1045,7 +1272,7 @@ function App() {
|
|
|
1045
1272
|
stashedRef.current = true;
|
|
1046
1273
|
await writeStashGuard();
|
|
1047
1274
|
}
|
|
1048
|
-
if (mountedRef.current) setStep(
|
|
1275
|
+
if (mountedRef.current) setStep(entryStep);
|
|
1049
1276
|
};
|
|
1050
1277
|
const doStashRecover = async () => {
|
|
1051
1278
|
const entry = await findStashEntry();
|
|
@@ -1055,13 +1282,13 @@ function App() {
|
|
|
1055
1282
|
await removeStashGuard();
|
|
1056
1283
|
if (!mountedRef.current) return;
|
|
1057
1284
|
const clean = await isWorkingDirClean();
|
|
1058
|
-
if (mountedRef.current) setStep(clean ?
|
|
1285
|
+
if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
|
|
1059
1286
|
};
|
|
1060
1287
|
const skipStashRecover = async () => {
|
|
1061
1288
|
await removeStashGuard();
|
|
1062
1289
|
if (!mountedRef.current) return;
|
|
1063
1290
|
const clean = await isWorkingDirClean();
|
|
1064
|
-
if (mountedRef.current) setStep(clean ?
|
|
1291
|
+
if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
|
|
1065
1292
|
};
|
|
1066
1293
|
const goBack = useCallback2((fromStep) => {
|
|
1067
1294
|
const backMap = {
|
|
@@ -1092,7 +1319,7 @@ function App() {
|
|
|
1092
1319
|
StashPrompt,
|
|
1093
1320
|
{
|
|
1094
1321
|
onConfirm: doStash,
|
|
1095
|
-
onSkip: () => setStep(
|
|
1322
|
+
onSkip: () => setStep(entryStep)
|
|
1096
1323
|
}
|
|
1097
1324
|
),
|
|
1098
1325
|
step === "remote" && inputReady && /* @__PURE__ */ jsx9(
|
|
@@ -1108,7 +1335,7 @@ function App() {
|
|
|
1108
1335
|
step === "branch" && inputReady && /* @__PURE__ */ jsx9(
|
|
1109
1336
|
BranchSelect,
|
|
1110
1337
|
{
|
|
1111
|
-
remote,
|
|
1338
|
+
remote: remote2,
|
|
1112
1339
|
onSelect: (b) => {
|
|
1113
1340
|
setBranch(b);
|
|
1114
1341
|
setStep("commits");
|
|
@@ -1119,8 +1346,8 @@ function App() {
|
|
|
1119
1346
|
step === "commits" && inputReady && /* @__PURE__ */ jsx9(
|
|
1120
1347
|
CommitList,
|
|
1121
1348
|
{
|
|
1122
|
-
remote,
|
|
1123
|
-
branch,
|
|
1349
|
+
remote: remote2,
|
|
1350
|
+
branch: branch2,
|
|
1124
1351
|
onSelect: async (hashes, loadedCommits) => {
|
|
1125
1352
|
setSelectedHashes(hashes);
|
|
1126
1353
|
setCommits(loadedCommits);
|
|
@@ -1134,7 +1361,7 @@ function App() {
|
|
|
1134
1361
|
step === "confirm" && inputReady && /* @__PURE__ */ jsx9(
|
|
1135
1362
|
ConfirmPanel,
|
|
1136
1363
|
{
|
|
1137
|
-
commits,
|
|
1364
|
+
commits: commits2,
|
|
1138
1365
|
selectedHashes,
|
|
1139
1366
|
hasMerge,
|
|
1140
1367
|
useMainline,
|
|
@@ -1159,32 +1386,244 @@ function App() {
|
|
|
1159
1386
|
] });
|
|
1160
1387
|
}
|
|
1161
1388
|
|
|
1389
|
+
// src/cli-runner.ts
|
|
1390
|
+
import { createInterface } from "readline";
|
|
1391
|
+
function log(msg) {
|
|
1392
|
+
process.stdout.write(msg + "\n");
|
|
1393
|
+
}
|
|
1394
|
+
function error(msg) {
|
|
1395
|
+
process.stderr.write(msg + "\n");
|
|
1396
|
+
}
|
|
1397
|
+
function padEnd(str, len) {
|
|
1398
|
+
return str.length >= len ? str : str + " ".repeat(len - str.length);
|
|
1399
|
+
}
|
|
1400
|
+
async function confirm(message) {
|
|
1401
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1402
|
+
return new Promise((resolve) => {
|
|
1403
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
1404
|
+
rl.close();
|
|
1405
|
+
resolve(answer.trim().toLowerCase() === "y");
|
|
1406
|
+
});
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
async function handleStash(noStash2) {
|
|
1410
|
+
const clean = await isWorkingDirClean();
|
|
1411
|
+
if (clean) {
|
|
1412
|
+
log("\u2714 \u5DE5\u4F5C\u533A\u5E72\u51C0");
|
|
1413
|
+
return false;
|
|
1414
|
+
}
|
|
1415
|
+
if (noStash2) {
|
|
1416
|
+
log("\u25B2 \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF08--no-stash \u8DF3\u8FC7 stash\uFF09");
|
|
1417
|
+
return false;
|
|
1418
|
+
}
|
|
1419
|
+
log("\u25B2 \u5DE5\u4F5C\u533A\u6709\u672A\u63D0\u4EA4\u53D8\u66F4\uFF0C\u81EA\u52A8 stash...");
|
|
1420
|
+
const ok = await stash();
|
|
1421
|
+
if (ok) {
|
|
1422
|
+
await writeStashGuard();
|
|
1423
|
+
log("\u2714 \u5DF2 stash \u5DE5\u4F5C\u533A\u53D8\u66F4");
|
|
1424
|
+
return true;
|
|
1425
|
+
}
|
|
1426
|
+
error("\u2716 stash \u5931\u8D25");
|
|
1427
|
+
process.exit(1);
|
|
1428
|
+
return false;
|
|
1429
|
+
}
|
|
1430
|
+
async function restoreStash(stashed) {
|
|
1431
|
+
if (!stashed) return;
|
|
1432
|
+
const ok = await stashPop();
|
|
1433
|
+
await removeStashGuard();
|
|
1434
|
+
if (ok) {
|
|
1435
|
+
log("\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)");
|
|
1436
|
+
} else {
|
|
1437
|
+
error("\u25B2 stash pop \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: git stash pop");
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
async function validateRemote(name) {
|
|
1441
|
+
const remotes = await getRemotes();
|
|
1442
|
+
if (!remotes.some((r) => r.name === name)) {
|
|
1443
|
+
const available = remotes.map((r) => r.name).join(", ");
|
|
1444
|
+
error(`\u2716 \u8FDC\u7A0B\u4ED3\u5E93 '${name}' \u4E0D\u5B58\u5728`);
|
|
1445
|
+
if (available) error(` \u53EF\u7528: ${available}`);
|
|
1446
|
+
process.exit(1);
|
|
1447
|
+
}
|
|
1448
|
+
log(`\u2714 \u8FDC\u7A0B\u4ED3\u5E93 '${name}'`);
|
|
1449
|
+
}
|
|
1450
|
+
async function validateBranch(remote2, branch2) {
|
|
1451
|
+
const branches = await getRemoteBranches(remote2);
|
|
1452
|
+
if (!branches.includes(branch2)) {
|
|
1453
|
+
error(`\u2716 \u5206\u652F '${branch2}' \u4E0D\u5B58\u5728\u4E8E ${remote2}`);
|
|
1454
|
+
const similar = branches.filter((b) => b.toLowerCase().includes(branch2.toLowerCase())).slice(0, 5);
|
|
1455
|
+
if (similar.length > 0) error(` \u7C7B\u4F3C: ${similar.join(", ")}`);
|
|
1456
|
+
process.exit(1);
|
|
1457
|
+
}
|
|
1458
|
+
log(`\u2714 \u5206\u652F '${remote2}/${branch2}'`);
|
|
1459
|
+
}
|
|
1460
|
+
function formatCommitLine(c) {
|
|
1461
|
+
return ` ${c.shortHash} ${padEnd(c.message.slice(0, 60), 62)} ${padEnd(c.author, 16)} ${c.date}`;
|
|
1462
|
+
}
|
|
1463
|
+
async function runList(opts) {
|
|
1464
|
+
await validateRemote(opts.remote);
|
|
1465
|
+
log(`\u83B7\u53D6 ${opts.remote}/${opts.branch} \u7684 commit \u5217\u8868...`);
|
|
1466
|
+
await validateBranch(opts.remote, opts.branch);
|
|
1467
|
+
const commits2 = await getCommits(opts.remote, opts.branch, opts.count);
|
|
1468
|
+
if (commits2.length === 0) {
|
|
1469
|
+
log("(\u65E0 commit)");
|
|
1470
|
+
return;
|
|
1471
|
+
}
|
|
1472
|
+
log(`
|
|
1473
|
+
Commits on ${opts.remote}/${opts.branch} (${commits2.length}):`);
|
|
1474
|
+
for (const c of commits2) {
|
|
1475
|
+
log(formatCommitLine(c));
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
async function runExec(opts) {
|
|
1479
|
+
const stashed = await handleStash(opts.noStash);
|
|
1480
|
+
await validateRemote(opts.remote);
|
|
1481
|
+
await validateBranch(opts.remote, opts.branch);
|
|
1482
|
+
const allCommits = await getCommits(opts.remote, opts.branch, opts.count);
|
|
1483
|
+
const hashes = opts.commits;
|
|
1484
|
+
const resolved = [];
|
|
1485
|
+
for (const input of hashes) {
|
|
1486
|
+
const match = allCommits.find(
|
|
1487
|
+
(c) => c.hash === input || c.shortHash === input || c.hash.startsWith(input)
|
|
1488
|
+
);
|
|
1489
|
+
if (!match) {
|
|
1490
|
+
error(`\u2716 commit '${input}' \u672A\u627E\u5230\u5728 ${opts.remote}/${opts.branch}`);
|
|
1491
|
+
await restoreStash(stashed);
|
|
1492
|
+
process.exit(1);
|
|
1493
|
+
}
|
|
1494
|
+
resolved.push(match);
|
|
1495
|
+
}
|
|
1496
|
+
const resolvedHashes = resolved.map((c) => c.hash);
|
|
1497
|
+
const hasMerge = await hasMergeCommits(resolvedHashes);
|
|
1498
|
+
if (hasMerge && !opts.mainline) {
|
|
1499
|
+
log("\u25B2 \u68C0\u6D4B\u5230 merge commit\uFF0C\u5EFA\u8BAE\u6DFB\u52A0 --mainline (-m) \u53C2\u6570");
|
|
1500
|
+
}
|
|
1501
|
+
log(`
|
|
1502
|
+
Cherry-pick ${resolved.length} \u4E2A commit (--no-commit${opts.mainline ? " -m 1" : ""}):`);
|
|
1503
|
+
for (const c of resolved) {
|
|
1504
|
+
log(formatCommitLine(c));
|
|
1505
|
+
}
|
|
1506
|
+
log("");
|
|
1507
|
+
if (!opts.yes) {
|
|
1508
|
+
const ok = await confirm("\u786E\u8BA4\u6267\u884C?");
|
|
1509
|
+
if (!ok) {
|
|
1510
|
+
log("\u5DF2\u53D6\u6D88");
|
|
1511
|
+
await restoreStash(stashed);
|
|
1512
|
+
process.exit(0);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
const result = await cherryPick(resolvedHashes, opts.mainline);
|
|
1516
|
+
if (result.success) {
|
|
1517
|
+
log("\u2714 Cherry-pick \u5B8C\u6210");
|
|
1518
|
+
const stat = await getStagedStat();
|
|
1519
|
+
if (stat) {
|
|
1520
|
+
log(`
|
|
1521
|
+
\u6682\u5B58\u533A\u53D8\u66F4 (git diff --cached --stat):
|
|
1522
|
+
${stat}`);
|
|
1523
|
+
}
|
|
1524
|
+
log("\n\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)");
|
|
1525
|
+
log(" \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:");
|
|
1526
|
+
log(" git diff --cached # \u67E5\u770B\u8BE6\u7EC6 diff");
|
|
1527
|
+
log(' git commit -m "sync: ..." # \u63D0\u4EA4');
|
|
1528
|
+
log(" git reset HEAD # \u6216\u653E\u5F03");
|
|
1529
|
+
} else {
|
|
1530
|
+
error("\u2716 Cherry-pick \u9047\u5230\u51B2\u7A81");
|
|
1531
|
+
if (result.conflictFiles && result.conflictFiles.length > 0) {
|
|
1532
|
+
error("\u51B2\u7A81\u6587\u4EF6:");
|
|
1533
|
+
for (const f of result.conflictFiles) {
|
|
1534
|
+
error(` ${f}`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
error("\n\u25B8 \u624B\u52A8\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add \u548C git commit");
|
|
1538
|
+
error("\u25B8 \u6216\u6267\u884C git cherry-pick --abort \u653E\u5F03\u64CD\u4F5C");
|
|
1539
|
+
}
|
|
1540
|
+
await restoreStash(stashed);
|
|
1541
|
+
if (!result.success) {
|
|
1542
|
+
process.exit(1);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
async function runCli(opts) {
|
|
1546
|
+
if (opts.list) {
|
|
1547
|
+
await runList(opts);
|
|
1548
|
+
} else {
|
|
1549
|
+
await runExec(opts);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1162
1553
|
// src/cli.tsx
|
|
1163
1554
|
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
1164
1555
|
var cli = meow(
|
|
1165
1556
|
`
|
|
1166
1557
|
\u7528\u6CD5
|
|
1167
|
-
$ git-sync-tui
|
|
1558
|
+
$ git-sync-tui [options]
|
|
1168
1559
|
|
|
1169
1560
|
\u9009\u9879
|
|
1170
|
-
--
|
|
1171
|
-
--
|
|
1561
|
+
-r, --remote <name> \u6307\u5B9A\u8FDC\u7A0B\u4ED3\u5E93\u540D\u79F0
|
|
1562
|
+
-b, --branch <name> \u6307\u5B9A\u8FDC\u7A0B\u5206\u652F\u540D\u79F0
|
|
1563
|
+
-c, --commits <hashes> \u6307\u5B9A commit hash\uFF08\u9017\u53F7\u5206\u9694\uFF09
|
|
1564
|
+
-n, --count <number> \u663E\u793A commit \u6570\u91CF\uFF08\u9ED8\u8BA4 100\uFF09
|
|
1565
|
+
-m, --mainline \u5BF9 merge commit \u4F7F\u7528 -m 1
|
|
1566
|
+
-y, --yes \u8DF3\u8FC7\u786E\u8BA4\u76F4\u63A5\u6267\u884C
|
|
1567
|
+
--no-stash \u8DF3\u8FC7 stash \u63D0\u793A
|
|
1568
|
+
--list \u5217\u51FA\u8FDC\u7A0B\u5206\u652F\u7684 commit \u540E\u9000\u51FA
|
|
1172
1569
|
|
|
1173
|
-
\
|
|
1174
|
-
\u4EA4\u4E92\u5F0F TUI \
|
|
1175
|
-
|
|
1570
|
+
\u6A21\u5F0F
|
|
1571
|
+
\u65E0\u53C2\u6570 \u4EA4\u4E92\u5F0F TUI \u6A21\u5F0F
|
|
1572
|
+
-r -b --list \u5217\u51FA commit\uFF08\u7EAF\u6587\u672C\uFF09
|
|
1573
|
+
-r -b -c CLI \u6A21\u5F0F\uFF0C\u786E\u8BA4\u540E\u6267\u884C
|
|
1574
|
+
-r -b -c --yes CLI \u6A21\u5F0F\uFF0C\u76F4\u63A5\u6267\u884C
|
|
1575
|
+
\u4EC5 -r \u6216 -r -b TUI \u6A21\u5F0F\uFF0C\u8DF3\u8FC7\u5DF2\u6307\u5B9A\u6B65\u9AA4
|
|
1176
1576
|
|
|
1177
|
-
\u5FEB\u6377\u952E
|
|
1577
|
+
TUI \u5FEB\u6377\u952E
|
|
1178
1578
|
Space \u9009\u62E9/\u53D6\u6D88 commit
|
|
1179
1579
|
Shift+\u2191/\u2193 \u8FDE\u7EED\u9009\u62E9
|
|
1180
1580
|
a \u5168\u9009/\u53D6\u6D88\u5168\u9009
|
|
1181
1581
|
i \u53CD\u9009
|
|
1182
1582
|
r \u9009\u81F3\u5F00\u5934
|
|
1183
1583
|
Enter \u786E\u8BA4\u9009\u62E9
|
|
1584
|
+
Esc \u8FD4\u56DE\u4E0A\u4E00\u6B65
|
|
1184
1585
|
y/n \u786E\u8BA4/\u53D6\u6D88\u6267\u884C
|
|
1586
|
+
|
|
1587
|
+
\u793A\u4F8B
|
|
1588
|
+
$ git-sync-tui # TUI \u6A21\u5F0F
|
|
1589
|
+
$ git-sync-tui -r upstream -b main --list # \u5217\u51FA commits
|
|
1590
|
+
$ git-sync-tui -r upstream -b main -c abc1234 --yes # \u76F4\u63A5\u6267\u884C
|
|
1591
|
+
$ git-sync-tui -r upstream -b main -c abc1234,def5678 # \u786E\u8BA4\u540E\u6267\u884C
|
|
1592
|
+
$ git-sync-tui -r upstream # TUI \u6A21\u5F0F\uFF0C\u8DF3\u8FC7\u9009\u62E9\u4ED3\u5E93
|
|
1185
1593
|
`,
|
|
1186
1594
|
{
|
|
1187
|
-
importMeta: import.meta
|
|
1595
|
+
importMeta: import.meta,
|
|
1596
|
+
flags: {
|
|
1597
|
+
remote: { type: "string", shortFlag: "r" },
|
|
1598
|
+
branch: { type: "string", shortFlag: "b" },
|
|
1599
|
+
commits: { type: "string", shortFlag: "c" },
|
|
1600
|
+
count: { type: "number", shortFlag: "n", default: 100 },
|
|
1601
|
+
mainline: { type: "boolean", shortFlag: "m", default: false },
|
|
1602
|
+
yes: { type: "boolean", shortFlag: "y", default: false },
|
|
1603
|
+
noStash: { type: "boolean", default: false },
|
|
1604
|
+
list: { type: "boolean", default: false }
|
|
1605
|
+
}
|
|
1188
1606
|
}
|
|
1189
1607
|
);
|
|
1190
|
-
|
|
1608
|
+
var { remote, branch, commits, count, mainline, yes, noStash, list } = cli.flags;
|
|
1609
|
+
var commitList = commits ? commits.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
1610
|
+
var hasAllParams = !!(remote && branch && commitList && commitList.length > 0);
|
|
1611
|
+
var isListMode = !!(list && remote && branch);
|
|
1612
|
+
var isCliMode = hasAllParams || isListMode;
|
|
1613
|
+
if (isCliMode) {
|
|
1614
|
+
runCli({
|
|
1615
|
+
remote,
|
|
1616
|
+
branch,
|
|
1617
|
+
commits: commitList,
|
|
1618
|
+
count,
|
|
1619
|
+
mainline,
|
|
1620
|
+
yes,
|
|
1621
|
+
noStash,
|
|
1622
|
+
list
|
|
1623
|
+
}).catch((err) => {
|
|
1624
|
+
console.error("\u2716 " + (err.message || err));
|
|
1625
|
+
process.exit(1);
|
|
1626
|
+
});
|
|
1627
|
+
} else {
|
|
1628
|
+
render(/* @__PURE__ */ jsx10(App, { initialRemote: remote, initialBranch: branch }));
|
|
1629
|
+
}
|
package/package.json
CHANGED