git-sync-tui 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -23
- package/README.zh-CN.md +91 -22
- package/dist/cli.js +702 -113
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,9 +11,9 @@ import { render } from "ink";
|
|
|
11
11
|
import meow from "meow";
|
|
12
12
|
|
|
13
13
|
// src/app.tsx
|
|
14
|
-
import { useState as
|
|
15
|
-
import { Box as
|
|
16
|
-
import { Spinner as
|
|
14
|
+
import { useState as useState9, useEffect as useEffect7, useRef as useRef5, useCallback as useCallback3 } from "react";
|
|
15
|
+
import { Box as Box11, useApp } from "ink";
|
|
16
|
+
import { Spinner as Spinner7 } from "@inkjs/ui";
|
|
17
17
|
|
|
18
18
|
// src/components/ui.tsx
|
|
19
19
|
import React from "react";
|
|
@@ -92,11 +92,14 @@ function StatusPanel({ type, title, children }) {
|
|
|
92
92
|
children
|
|
93
93
|
] });
|
|
94
94
|
}
|
|
95
|
-
function AppHeader({ step, stashed }) {
|
|
95
|
+
function AppHeader({ step, stashed, noCommit }) {
|
|
96
96
|
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
97
97
|
/* @__PURE__ */ jsxs(Box, { gap: 1, children: [
|
|
98
98
|
/* @__PURE__ */ jsx(Text, { backgroundColor: "cyan", color: "white", bold: true, children: " git-sync-tui " }),
|
|
99
|
-
/* @__PURE__ */
|
|
99
|
+
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
100
|
+
"cherry-pick",
|
|
101
|
+
noCommit ? " --no-commit" : ""
|
|
102
|
+
] }),
|
|
100
103
|
stashed && /* @__PURE__ */ jsx(Text, { backgroundColor: "yellow", color: "white", bold: true, children: " STASHED " })
|
|
101
104
|
] }),
|
|
102
105
|
/* @__PURE__ */ jsxs(Box, { marginTop: 0, children: [
|
|
@@ -187,6 +190,8 @@ async function getCommits(remote2, branch2, count2 = 100) {
|
|
|
187
190
|
const result = await git.raw([
|
|
188
191
|
"log",
|
|
189
192
|
ref,
|
|
193
|
+
"--first-parent",
|
|
194
|
+
"--no-merges",
|
|
190
195
|
`--max-count=${count2}`,
|
|
191
196
|
"--format=%H%n%h%n%s%n%an%n%ar%n---"
|
|
192
197
|
]);
|
|
@@ -281,6 +286,45 @@ async function getStagedStat() {
|
|
|
281
286
|
return "";
|
|
282
287
|
}
|
|
283
288
|
}
|
|
289
|
+
var BACKUP_BRANCH_PREFIX = "git-sync-backup";
|
|
290
|
+
async function createBackupBranch() {
|
|
291
|
+
const git = getGit();
|
|
292
|
+
const timestamp = Date.now();
|
|
293
|
+
const branchName = `${BACKUP_BRANCH_PREFIX}-${timestamp}`;
|
|
294
|
+
await git.raw(["branch", branchName]);
|
|
295
|
+
return branchName;
|
|
296
|
+
}
|
|
297
|
+
async function restoreFromBackup(backupBranch) {
|
|
298
|
+
const git = getGit();
|
|
299
|
+
try {
|
|
300
|
+
await git.raw(["rev-parse", "--verify", backupBranch]);
|
|
301
|
+
await git.raw(["reset", "--hard", backupBranch]);
|
|
302
|
+
await git.raw(["branch", "-D", backupBranch]);
|
|
303
|
+
return { success: true };
|
|
304
|
+
} catch (err) {
|
|
305
|
+
return {
|
|
306
|
+
success: false,
|
|
307
|
+
error: `\u56DE\u9000\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8\u6267\u884C: git reset --hard ${backupBranch}
|
|
308
|
+
${err.message}`
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
async function deleteBackupBranch(backupBranch) {
|
|
313
|
+
const git = getGit();
|
|
314
|
+
try {
|
|
315
|
+
await git.raw(["branch", "-D", backupBranch]);
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async function getCurrentBranch() {
|
|
320
|
+
const git = getGit();
|
|
321
|
+
const result = await git.raw(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
322
|
+
return result.trim();
|
|
323
|
+
}
|
|
324
|
+
async function createBranchFrom(newBranch, baseBranch) {
|
|
325
|
+
const git = getGit();
|
|
326
|
+
await git.raw(["checkout", "-b", newBranch, baseBranch]);
|
|
327
|
+
}
|
|
284
328
|
async function isWorkingDirClean() {
|
|
285
329
|
const git = getGit();
|
|
286
330
|
const status = await git.status();
|
|
@@ -304,6 +348,87 @@ async function stashPop() {
|
|
|
304
348
|
return false;
|
|
305
349
|
}
|
|
306
350
|
}
|
|
351
|
+
async function abortCherryPick() {
|
|
352
|
+
const git = getGit();
|
|
353
|
+
try {
|
|
354
|
+
await git.raw(["cherry-pick", "--abort"]);
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
async function getConflictFiles() {
|
|
359
|
+
const git = getGit();
|
|
360
|
+
const status = await git.status();
|
|
361
|
+
return status.conflicted;
|
|
362
|
+
}
|
|
363
|
+
async function continueCherryPick() {
|
|
364
|
+
const git = getGit();
|
|
365
|
+
try {
|
|
366
|
+
await git.raw(["cherry-pick", "--continue", "--no-edit"]);
|
|
367
|
+
return { success: true };
|
|
368
|
+
} catch (err) {
|
|
369
|
+
const msg = err.message || "";
|
|
370
|
+
if (msg.includes("nothing to commit") || msg.includes("empty") || msg.includes("allow-empty")) {
|
|
371
|
+
return { success: false, error: msg, empty: true };
|
|
372
|
+
}
|
|
373
|
+
try {
|
|
374
|
+
const status = await git.status();
|
|
375
|
+
const conflictFiles = status.conflicted;
|
|
376
|
+
return {
|
|
377
|
+
success: false,
|
|
378
|
+
error: msg,
|
|
379
|
+
conflictFiles: conflictFiles.length > 0 ? conflictFiles : void 0
|
|
380
|
+
};
|
|
381
|
+
} catch {
|
|
382
|
+
return { success: false, error: msg };
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
async function clearCherryPickState() {
|
|
387
|
+
try {
|
|
388
|
+
const gitDir = await getGitDir();
|
|
389
|
+
const cpHead = join(gitDir, "CHERRY_PICK_HEAD");
|
|
390
|
+
if (existsSync(cpHead)) {
|
|
391
|
+
unlinkSync(cpHead);
|
|
392
|
+
}
|
|
393
|
+
} catch {
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
async function cherryPickOne(hash, useMainline = false, noCommit = true) {
|
|
397
|
+
const git = getGit();
|
|
398
|
+
try {
|
|
399
|
+
const args = ["cherry-pick"];
|
|
400
|
+
if (noCommit) args.push("--no-commit");
|
|
401
|
+
if (useMainline) args.push("-m", "1");
|
|
402
|
+
args.push(hash);
|
|
403
|
+
await git.raw(args);
|
|
404
|
+
return { success: true };
|
|
405
|
+
} catch (err) {
|
|
406
|
+
try {
|
|
407
|
+
const status = await git.status();
|
|
408
|
+
const conflictFiles = status.conflicted;
|
|
409
|
+
return {
|
|
410
|
+
success: false,
|
|
411
|
+
error: err.message,
|
|
412
|
+
conflictFiles: conflictFiles.length > 0 ? conflictFiles : void 0
|
|
413
|
+
};
|
|
414
|
+
} catch {
|
|
415
|
+
return { success: false, error: err.message };
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function skipCherryPick() {
|
|
420
|
+
const git = getGit();
|
|
421
|
+
await git.raw(["cherry-pick", "--skip"]);
|
|
422
|
+
}
|
|
423
|
+
async function isCherryPickInProgress() {
|
|
424
|
+
const git = getGit();
|
|
425
|
+
try {
|
|
426
|
+
const gitDir = await getGitDir();
|
|
427
|
+
return existsSync(join(gitDir, "CHERRY_PICK_HEAD"));
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
307
432
|
var STASH_GUARD_FILE = "git-sync-tui-stash-guard";
|
|
308
433
|
async function getGitDir() {
|
|
309
434
|
const git = getGit();
|
|
@@ -1043,11 +1168,107 @@ function CommitList({ remote: remote2, branch: branch2, onSelect, onBack }) {
|
|
|
1043
1168
|
] });
|
|
1044
1169
|
}
|
|
1045
1170
|
|
|
1046
|
-
// src/components/
|
|
1171
|
+
// src/components/branch-check.tsx
|
|
1172
|
+
import { useState as useState6, useEffect as useEffect4, useRef as useRef3 } from "react";
|
|
1047
1173
|
import { Box as Box7, Text as Text7, useInput as useInput6 } from "ink";
|
|
1174
|
+
import { Spinner as Spinner5 } from "@inkjs/ui";
|
|
1048
1175
|
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1049
|
-
|
|
1176
|
+
var BASE_BRANCHES = ["main", "master"];
|
|
1177
|
+
function BranchCheck({ targetBranch, onContinue, onBack }) {
|
|
1178
|
+
const [currentBranch, setCurrentBranch] = useState6(null);
|
|
1179
|
+
const [creating, setCreating] = useState6(false);
|
|
1180
|
+
const [error2, setError] = useState6(null);
|
|
1181
|
+
const [matched, setMatched] = useState6(false);
|
|
1182
|
+
const autoCreated = useRef3(false);
|
|
1183
|
+
useEffect4(() => {
|
|
1184
|
+
getCurrentBranch().then((branch2) => {
|
|
1185
|
+
setCurrentBranch(branch2);
|
|
1186
|
+
if (branch2 === targetBranch) {
|
|
1187
|
+
setMatched(true);
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
}, [targetBranch]);
|
|
1191
|
+
useEffect4(() => {
|
|
1192
|
+
if (matched) onContinue();
|
|
1193
|
+
}, [matched]);
|
|
1194
|
+
useEffect4(() => {
|
|
1195
|
+
if (currentBranch === null || matched || autoCreated.current) return;
|
|
1196
|
+
if (!BASE_BRANCHES.includes(currentBranch)) return;
|
|
1197
|
+
autoCreated.current = true;
|
|
1198
|
+
setCreating(true);
|
|
1199
|
+
createBranchFrom(targetBranch, currentBranch).then(() => {
|
|
1200
|
+
onContinue();
|
|
1201
|
+
}).catch((err) => {
|
|
1202
|
+
setCreating(false);
|
|
1203
|
+
setError(err.message);
|
|
1204
|
+
});
|
|
1205
|
+
}, [currentBranch, matched, targetBranch]);
|
|
1206
|
+
const doCreate = () => {
|
|
1207
|
+
if (!currentBranch) return;
|
|
1208
|
+
setCreating(true);
|
|
1209
|
+
setError(null);
|
|
1210
|
+
createBranchFrom(targetBranch, currentBranch).then(() => {
|
|
1211
|
+
onContinue();
|
|
1212
|
+
}).catch((err) => {
|
|
1213
|
+
setCreating(false);
|
|
1214
|
+
setError(err.message);
|
|
1215
|
+
});
|
|
1216
|
+
};
|
|
1050
1217
|
useInput6((input, key) => {
|
|
1218
|
+
if (creating || currentBranch === null || matched) return;
|
|
1219
|
+
if (BASE_BRANCHES.includes(currentBranch)) return;
|
|
1220
|
+
if (input === "y" || input === "Y") {
|
|
1221
|
+
doCreate();
|
|
1222
|
+
} else if (input === "n" || input === "N") {
|
|
1223
|
+
onContinue();
|
|
1224
|
+
} else if (key.escape) {
|
|
1225
|
+
onBack?.();
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
if (currentBranch === null || matched) {
|
|
1229
|
+
return /* @__PURE__ */ jsx7(Spinner5, { label: "\u68C0\u67E5\u5F53\u524D\u5206\u652F..." });
|
|
1230
|
+
}
|
|
1231
|
+
if (creating) {
|
|
1232
|
+
return /* @__PURE__ */ jsx7(Spinner5, { label: `\u6B63\u5728\u4ECE ${currentBranch} \u521B\u5EFA\u5206\u652F ${targetBranch}...` });
|
|
1233
|
+
}
|
|
1234
|
+
return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
|
|
1235
|
+
/* @__PURE__ */ jsx7(SectionHeader, { title: "\u5206\u652F\u68C0\u67E5" }),
|
|
1236
|
+
/* @__PURE__ */ jsxs7(StatusPanel, { type: "warn", title: "\u5F53\u524D\u5206\u652F\u4E0E\u76EE\u6807\u5206\u652F\u4E0D\u4E00\u81F4", children: [
|
|
1237
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1238
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: " \u5F53\u524D\u5206\u652F: " }),
|
|
1239
|
+
/* @__PURE__ */ jsx7(Text7, { color: "yellow", bold: true, children: currentBranch })
|
|
1240
|
+
] }),
|
|
1241
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1242
|
+
/* @__PURE__ */ jsx7(Text7, { color: "gray", children: " \u76EE\u6807\u5206\u652F: " }),
|
|
1243
|
+
/* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: true, children: targetBranch })
|
|
1244
|
+
] })
|
|
1245
|
+
] }),
|
|
1246
|
+
error2 && /* @__PURE__ */ jsxs7(Text7, { color: "red", children: [
|
|
1247
|
+
"\u2716 ",
|
|
1248
|
+
error2
|
|
1249
|
+
] }),
|
|
1250
|
+
/* @__PURE__ */ jsxs7(Box7, { children: [
|
|
1251
|
+
/* @__PURE__ */ jsxs7(Text7, { bold: true, children: [
|
|
1252
|
+
"\u4ECE ",
|
|
1253
|
+
currentBranch,
|
|
1254
|
+
" \u521B\u5EFA ",
|
|
1255
|
+
targetBranch,
|
|
1256
|
+
" \u5206\u652F? "
|
|
1257
|
+
] }),
|
|
1258
|
+
/* @__PURE__ */ jsx7(InlineKeys, { hints: [
|
|
1259
|
+
{ key: "y", label: "\u521B\u5EFA\u5E76\u5207\u6362" },
|
|
1260
|
+
{ key: "n", label: "\u8DF3\u8FC7" },
|
|
1261
|
+
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
1262
|
+
] })
|
|
1263
|
+
] })
|
|
1264
|
+
] });
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// src/components/confirm-panel.tsx
|
|
1268
|
+
import { Box as Box8, Text as Text8, useInput as useInput7 } from "ink";
|
|
1269
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1270
|
+
function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline, noCommit, onToggleMainline, onToggleNoCommit, onConfirm, onCancel }) {
|
|
1271
|
+
useInput7((input, key) => {
|
|
1051
1272
|
if (key.escape) {
|
|
1052
1273
|
onCancel();
|
|
1053
1274
|
} else if (input === "y" || input === "Y") {
|
|
@@ -1056,43 +1277,54 @@ function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline
|
|
|
1056
1277
|
onCancel();
|
|
1057
1278
|
} else if (hasMerge && (input === "m" || input === "M")) {
|
|
1058
1279
|
onToggleMainline();
|
|
1280
|
+
} else if (input === "c" || input === "C") {
|
|
1281
|
+
onToggleNoCommit();
|
|
1059
1282
|
}
|
|
1060
1283
|
});
|
|
1061
1284
|
const selectedCommits = selectedHashes.map((hash) => commits2.find((c) => c.hash === hash)).filter(Boolean);
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
/* @__PURE__ */
|
|
1065
|
-
|
|
1285
|
+
const modeLabel = noCommit ? "--no-commit" : "\u9010\u4E2A\u63D0\u4EA4";
|
|
1286
|
+
return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
|
|
1287
|
+
/* @__PURE__ */ jsx8(SectionHeader, { title: "\u786E\u8BA4\u6267\u884C" }),
|
|
1288
|
+
/* @__PURE__ */ jsx8(StatusPanel, { type: "info", title: `cherry-pick \xB7 ${modeLabel} \xB7 ${selectedCommits.length} \u4E2A commit`, children: selectedCommits.map((c) => /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1289
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
|
|
1066
1290
|
" ",
|
|
1067
1291
|
c.shortHash
|
|
1068
1292
|
] }),
|
|
1069
|
-
/* @__PURE__ */
|
|
1293
|
+
/* @__PURE__ */ jsxs8(Text8, { children: [
|
|
1070
1294
|
" ",
|
|
1071
1295
|
c.message
|
|
1072
1296
|
] }),
|
|
1073
|
-
/* @__PURE__ */
|
|
1297
|
+
/* @__PURE__ */ jsxs8(Text8, { color: "gray", dimColor: true, children: [
|
|
1074
1298
|
" ",
|
|
1075
1299
|
c.author
|
|
1076
1300
|
] })
|
|
1077
1301
|
] }, c.hash)) }),
|
|
1078
|
-
|
|
1079
|
-
/* @__PURE__ */
|
|
1080
|
-
/* @__PURE__ */
|
|
1081
|
-
|
|
1082
|
-
/* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1084
|
-
|
|
1302
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1303
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "[c]" }),
|
|
1304
|
+
/* @__PURE__ */ jsx8(Text8, { children: " \u63D0\u4EA4\u6A21\u5F0F: " }),
|
|
1305
|
+
noCommit ? /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1306
|
+
/* @__PURE__ */ jsx8(Text8, { color: "yellow", bold: true, children: "--no-commit" }),
|
|
1307
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: " (\u6539\u52A8\u6682\u5B58\u5230\u5DE5\u4F5C\u533A\uFF0C\u9700\u624B\u52A8 commit)" })
|
|
1308
|
+
] }) : /* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1309
|
+
/* @__PURE__ */ jsx8(Text8, { color: "green", bold: true, children: "\u9010\u4E2A\u63D0\u4EA4" }),
|
|
1310
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: " (\u4FDD\u7559\u539F\u59CB commit \u4FE1\u606F)" })
|
|
1085
1311
|
] })
|
|
1086
1312
|
] }),
|
|
1087
|
-
/* @__PURE__ */
|
|
1088
|
-
/* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1313
|
+
hasMerge && /* @__PURE__ */ jsxs8(StatusPanel, { type: "warn", title: "\u68C0\u6D4B\u5230 Merge Commit", children: [
|
|
1314
|
+
/* @__PURE__ */ jsx8(Text8, { color: "gray", children: " Cherry-pick \u5408\u5E76\u63D0\u4EA4\u9700\u8981\u6307\u5B9A\u7236\u8282\u70B9 (-m 1)" }),
|
|
1315
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1316
|
+
/* @__PURE__ */ jsx8(Text8, { children: " " }),
|
|
1317
|
+
/* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "[m]" }),
|
|
1318
|
+
/* @__PURE__ */ jsx8(Text8, { children: " \u5207\u6362 -m 1: " }),
|
|
1319
|
+
useMainline ? /* @__PURE__ */ jsx8(Text8, { color: "green", bold: true, children: "\u5DF2\u542F\u7528" }) : /* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u672A\u542F\u7528" })
|
|
1320
|
+
] })
|
|
1090
1321
|
] }),
|
|
1091
|
-
/* @__PURE__ */
|
|
1092
|
-
/* @__PURE__ */
|
|
1093
|
-
/* @__PURE__ */
|
|
1322
|
+
/* @__PURE__ */ jsxs8(Box8, { children: [
|
|
1323
|
+
/* @__PURE__ */ jsx8(Text8, { bold: true, children: "\u786E\u8BA4\u6267\u884C? " }),
|
|
1324
|
+
/* @__PURE__ */ jsx8(InlineKeys, { hints: [
|
|
1094
1325
|
{ key: "y", label: "\u786E\u8BA4" },
|
|
1095
1326
|
{ key: "n", label: "\u53D6\u6D88" },
|
|
1327
|
+
{ key: "c", label: "\u5207\u6362\u63D0\u4EA4\u6A21\u5F0F" },
|
|
1096
1328
|
...hasMerge ? [{ key: "m", label: "\u5207\u6362 -m 1" }] : [],
|
|
1097
1329
|
{ key: "Esc", label: "\u8FD4\u56DE" }
|
|
1098
1330
|
] })
|
|
@@ -1101,92 +1333,426 @@ function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline
|
|
|
1101
1333
|
}
|
|
1102
1334
|
|
|
1103
1335
|
// src/components/result-panel.tsx
|
|
1104
|
-
import { useState as
|
|
1105
|
-
import { Box as
|
|
1106
|
-
import { Spinner as
|
|
1107
|
-
import { jsx as
|
|
1108
|
-
function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, onDone }) {
|
|
1109
|
-
const [phase, setPhase] =
|
|
1110
|
-
const [
|
|
1111
|
-
const [
|
|
1112
|
-
const [
|
|
1113
|
-
const
|
|
1336
|
+
import { useState as useState7, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef4 } from "react";
|
|
1337
|
+
import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
|
|
1338
|
+
import { Spinner as Spinner6 } from "@inkjs/ui";
|
|
1339
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1340
|
+
function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRestored, onDone }) {
|
|
1341
|
+
const [phase, setPhase] = useState7("executing");
|
|
1342
|
+
const [conflictFiles, setConflictFiles] = useState7([]);
|
|
1343
|
+
const [currentIndex, setCurrentIndex] = useState7(0);
|
|
1344
|
+
const [stagedStat, setStagedStat] = useState7("");
|
|
1345
|
+
const [stashRestored, setStashRestored] = useState7(null);
|
|
1346
|
+
const [errorMsg, setErrorMsg] = useState7("");
|
|
1347
|
+
const [completedCount, setCompletedCount] = useState7(0);
|
|
1348
|
+
const [skippedCount, setSkippedCount] = useState7(0);
|
|
1349
|
+
const remainingRef = useRef4([]);
|
|
1350
|
+
const backupBranchRef = useRef4("");
|
|
1351
|
+
const orderedHashes = useRef4([...selectedHashes].reverse());
|
|
1352
|
+
const tryRestoreStash = useCallback2(async () => {
|
|
1114
1353
|
if (!stashed) return true;
|
|
1115
1354
|
setPhase("restoring");
|
|
1116
1355
|
const ok = await stashPop();
|
|
1117
1356
|
setStashRestored(ok);
|
|
1118
1357
|
if (ok) onStashRestored();
|
|
1119
1358
|
return ok;
|
|
1120
|
-
};
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1359
|
+
}, [stashed, onStashRestored]);
|
|
1360
|
+
const finishAll = useCallback2(async () => {
|
|
1361
|
+
if (noCommit) {
|
|
1362
|
+
const stat = await getStagedStat();
|
|
1363
|
+
setStagedStat(stat);
|
|
1364
|
+
}
|
|
1365
|
+
if (backupBranchRef.current) {
|
|
1366
|
+
await deleteBackupBranch(backupBranchRef.current);
|
|
1367
|
+
}
|
|
1368
|
+
await tryRestoreStash();
|
|
1369
|
+
setPhase("done");
|
|
1370
|
+
}, [noCommit, tryRestoreStash]);
|
|
1371
|
+
const executeFrom = useCallback2(async (startIndex) => {
|
|
1372
|
+
const hashes = orderedHashes.current;
|
|
1373
|
+
for (let i = startIndex; i < hashes.length; i++) {
|
|
1374
|
+
setCurrentIndex(i);
|
|
1375
|
+
const res = await cherryPickOne(hashes[i], useMainline, noCommit);
|
|
1376
|
+
if (!res.success) {
|
|
1377
|
+
setConflictFiles(res.conflictFiles || []);
|
|
1378
|
+
remainingRef.current = hashes.slice(i + 1);
|
|
1379
|
+
setPhase("conflict");
|
|
1380
|
+
return;
|
|
1133
1381
|
}
|
|
1382
|
+
setCompletedCount((c) => c + 1);
|
|
1134
1383
|
}
|
|
1135
|
-
|
|
1384
|
+
await finishAll();
|
|
1385
|
+
}, [useMainline, noCommit, finishAll]);
|
|
1386
|
+
useEffect5(() => {
|
|
1387
|
+
createBackupBranch().then((branch2) => {
|
|
1388
|
+
backupBranchRef.current = branch2;
|
|
1389
|
+
executeFrom(0);
|
|
1390
|
+
});
|
|
1136
1391
|
}, []);
|
|
1392
|
+
const continueRemaining = useCallback2(async () => {
|
|
1393
|
+
const remaining = remainingRef.current;
|
|
1394
|
+
if (remaining.length === 0) {
|
|
1395
|
+
await finishAll();
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
setPhase("executing");
|
|
1399
|
+
for (let i = 0; i < remaining.length; i++) {
|
|
1400
|
+
setCurrentIndex(orderedHashes.current.length - remaining.length + i);
|
|
1401
|
+
const res = await cherryPickOne(remaining[i], useMainline, noCommit);
|
|
1402
|
+
if (!res.success) {
|
|
1403
|
+
setConflictFiles(res.conflictFiles || []);
|
|
1404
|
+
remainingRef.current = remaining.slice(i + 1);
|
|
1405
|
+
setPhase("conflict");
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
setCompletedCount((c) => c + 1);
|
|
1409
|
+
}
|
|
1410
|
+
await finishAll();
|
|
1411
|
+
}, [useMainline, noCommit, finishAll]);
|
|
1412
|
+
const handleContinue = useCallback2(async () => {
|
|
1413
|
+
const conflicts = await getConflictFiles();
|
|
1414
|
+
if (conflicts.length > 0) {
|
|
1415
|
+
setConflictFiles(conflicts);
|
|
1416
|
+
setErrorMsg("\u4ECD\u6709\u672A\u89E3\u51B3\u7684\u51B2\u7A81\u6587\u4EF6\uFF0C\u8BF7\u5148\u89E3\u51B3\u5E76 git add");
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
setPhase("continuing");
|
|
1420
|
+
setErrorMsg("");
|
|
1421
|
+
const inProgress = await isCherryPickInProgress();
|
|
1422
|
+
if (noCommit) {
|
|
1423
|
+
if (inProgress) await clearCherryPickState();
|
|
1424
|
+
setCompletedCount((c) => c + 1);
|
|
1425
|
+
await continueRemaining();
|
|
1426
|
+
} else if (!inProgress) {
|
|
1427
|
+
setCompletedCount((c) => c + 1);
|
|
1428
|
+
await continueRemaining();
|
|
1429
|
+
} else {
|
|
1430
|
+
const contResult = await continueCherryPick();
|
|
1431
|
+
if (contResult.empty) {
|
|
1432
|
+
setPhase("empty");
|
|
1433
|
+
return;
|
|
1434
|
+
}
|
|
1435
|
+
if (!contResult.success) {
|
|
1436
|
+
setConflictFiles(contResult.conflictFiles || []);
|
|
1437
|
+
setErrorMsg("cherry-pick --continue \u5931\u8D25: " + (contResult.error || "").substring(0, 100));
|
|
1438
|
+
setPhase("conflict");
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
setCompletedCount((c) => c + 1);
|
|
1442
|
+
await continueRemaining();
|
|
1443
|
+
}
|
|
1444
|
+
}, [noCommit, continueRemaining]);
|
|
1445
|
+
const handleSkip = useCallback2(async () => {
|
|
1446
|
+
setPhase("continuing");
|
|
1447
|
+
setErrorMsg("");
|
|
1448
|
+
await skipCherryPick();
|
|
1449
|
+
setSkippedCount((c) => c + 1);
|
|
1450
|
+
await continueRemaining();
|
|
1451
|
+
}, [continueRemaining]);
|
|
1452
|
+
const handleAbort = useCallback2(async () => {
|
|
1453
|
+
setPhase("aborting");
|
|
1454
|
+
await abortCherryPick();
|
|
1455
|
+
if (backupBranchRef.current) {
|
|
1456
|
+
const result = await restoreFromBackup(backupBranchRef.current);
|
|
1457
|
+
if (!result.success) {
|
|
1458
|
+
setErrorMsg(result.error || "");
|
|
1459
|
+
}
|
|
1460
|
+
backupBranchRef.current = "";
|
|
1461
|
+
}
|
|
1462
|
+
await tryRestoreStash();
|
|
1463
|
+
setPhase("aborted");
|
|
1464
|
+
}, [tryRestoreStash]);
|
|
1465
|
+
useInput8((input, key) => {
|
|
1466
|
+
if (phase === "done" || phase === "aborted") {
|
|
1467
|
+
onDone();
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (phase === "conflict") {
|
|
1471
|
+
if (input === "c" || input === "C") {
|
|
1472
|
+
handleContinue();
|
|
1473
|
+
} else if (input === "a" || input === "A") {
|
|
1474
|
+
handleAbort();
|
|
1475
|
+
} else if (input === "q" || input === "Q") {
|
|
1476
|
+
onDone();
|
|
1477
|
+
}
|
|
1478
|
+
} else if (phase === "empty") {
|
|
1479
|
+
if (input === "s" || input === "S") {
|
|
1480
|
+
handleSkip();
|
|
1481
|
+
} else if (input === "a" || input === "A") {
|
|
1482
|
+
handleAbort();
|
|
1483
|
+
} else if (input === "q" || input === "Q") {
|
|
1484
|
+
onDone();
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
});
|
|
1137
1488
|
if (phase === "executing") {
|
|
1138
|
-
|
|
1489
|
+
const mode = noCommit ? "--no-commit" : "";
|
|
1490
|
+
return /* @__PURE__ */ jsx9(Spinner6, { label: `cherry-pick ${mode} (${currentIndex + 1}/${orderedHashes.current.length})...` });
|
|
1491
|
+
}
|
|
1492
|
+
if (phase === "continuing") {
|
|
1493
|
+
return /* @__PURE__ */ jsx9(Spinner6, { label: "\u7EE7\u7EED cherry-pick..." });
|
|
1494
|
+
}
|
|
1495
|
+
if (phase === "aborting") {
|
|
1496
|
+
return /* @__PURE__ */ jsx9(Spinner6, { label: "\u6B63\u5728\u653E\u5F03 cherry-pick..." });
|
|
1139
1497
|
}
|
|
1140
1498
|
if (phase === "restoring") {
|
|
1141
|
-
return /* @__PURE__ */
|
|
1499
|
+
return /* @__PURE__ */ jsx9(Spinner6, { label: "\u6062\u590D\u5DE5\u4F5C\u533A (git stash pop)..." });
|
|
1500
|
+
}
|
|
1501
|
+
if (phase === "empty") {
|
|
1502
|
+
const total2 = orderedHashes.current.length;
|
|
1503
|
+
const currentHash = orderedHashes.current[total2 - remainingRef.current.length - 1];
|
|
1504
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
|
|
1505
|
+
/* @__PURE__ */ jsx9(SectionHeader, { title: "\u7A7A\u63D0\u4EA4" }),
|
|
1506
|
+
/* @__PURE__ */ jsx9(StatusPanel, { type: "warn", title: `commit ${currentHash?.substring(0, 7)} \u89E3\u51B3\u51B2\u7A81\u540E\u65E0\u5B9E\u9645\u53D8\u66F4`, children: /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " \u8BE5 commit \u7684\u6240\u6709\u66F4\u6539\u5728\u51B2\u7A81\u89E3\u51B3\u8FC7\u7A0B\u4E2D\u5DF2\u88AB\u4E22\u5F03" }) }),
|
|
1507
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(InlineKeys, { hints: [
|
|
1508
|
+
{ key: "s", label: "\u8DF3\u8FC7\u6B64 commit (skip)" },
|
|
1509
|
+
{ key: "a", label: "\u653E\u5F03\u5168\u90E8 (abort)" },
|
|
1510
|
+
{ key: "q", label: "\u9000\u51FA (\u4FDD\u7559\u5F53\u524D\u72B6\u6001)" }
|
|
1511
|
+
] }) })
|
|
1512
|
+
] });
|
|
1142
1513
|
}
|
|
1143
|
-
if (phase === "
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1514
|
+
if (phase === "conflict") {
|
|
1515
|
+
const total2 = orderedHashes.current.length;
|
|
1516
|
+
const doneCount = total2 - remainingRef.current.length - 1;
|
|
1517
|
+
const currentHash = orderedHashes.current[total2 - remainingRef.current.length - 1];
|
|
1518
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
|
|
1519
|
+
/* @__PURE__ */ jsx9(SectionHeader, { title: "Cherry-pick \u9047\u5230\u51B2\u7A81" }),
|
|
1520
|
+
/* @__PURE__ */ jsxs9(Box9, { gap: 2, children: [
|
|
1521
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
|
|
1522
|
+
"\u8FDB\u5EA6: ",
|
|
1523
|
+
doneCount,
|
|
1524
|
+
"/",
|
|
1525
|
+
total2
|
|
1526
|
+
] }),
|
|
1527
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
1528
|
+
"\u51B2\u7A81 commit: ",
|
|
1529
|
+
currentHash?.substring(0, 7)
|
|
1530
|
+
] }),
|
|
1531
|
+
remainingRef.current.length > 0 && /* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
|
|
1532
|
+
"\u5269\u4F59: ",
|
|
1533
|
+
remainingRef.current.length
|
|
1534
|
+
] })
|
|
1535
|
+
] }),
|
|
1536
|
+
conflictFiles.length > 0 && /* @__PURE__ */ jsx9(StatusPanel, { type: "error", title: "\u51B2\u7A81\u6587\u4EF6", children: conflictFiles.map((f) => /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
|
|
1147
1537
|
" ",
|
|
1148
1538
|
f
|
|
1149
1539
|
] }, f)) }),
|
|
1150
|
-
/* @__PURE__ */
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
/* @__PURE__ */
|
|
1540
|
+
/* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1541
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B8 \u8BF7\u5728\u53E6\u4E00\u4E2A\u7EC8\u7AEF\u4E2D\u624B\u52A8\u89E3\u51B3\u51B2\u7A81" }),
|
|
1542
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
|
|
1543
|
+
" 1. \u7F16\u8F91\u51B2\u7A81\u6587\u4EF6\uFF0C\u89E3\u51B3\u51B2\u7A81\u6807\u8BB0 ",
|
|
1544
|
+
"<<<<<<< / ======= / >>>>>>>"
|
|
1545
|
+
] }),
|
|
1546
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
|
|
1547
|
+
" 2. \u6267\u884C git add ",
|
|
1548
|
+
"<file>",
|
|
1549
|
+
" \u6807\u8BB0\u5DF2\u89E3\u51B3"
|
|
1550
|
+
] }),
|
|
1551
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: " 3. \u56DE\u5230\u6B64\u5904\u6309 [c] \u7EE7\u7EED" })
|
|
1552
|
+
] }),
|
|
1553
|
+
errorMsg && /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
|
|
1554
|
+
"\u2716 ",
|
|
1555
|
+
errorMsg
|
|
1153
1556
|
] }),
|
|
1154
|
-
|
|
1155
|
-
|
|
1557
|
+
/* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(InlineKeys, { hints: [
|
|
1558
|
+
{ key: "c", label: "\u7EE7\u7EED (\u51B2\u7A81\u5DF2\u89E3\u51B3)" },
|
|
1559
|
+
{ key: "a", label: "\u653E\u5F03 (abort)" },
|
|
1560
|
+
{ key: "q", label: "\u9000\u51FA (\u4FDD\u7559\u5F53\u524D\u72B6\u6001)" }
|
|
1561
|
+
] }) })
|
|
1156
1562
|
] });
|
|
1157
1563
|
}
|
|
1158
|
-
|
|
1159
|
-
/* @__PURE__ */
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
/* @__PURE__ */
|
|
1165
|
-
|
|
1564
|
+
if (phase === "aborted") {
|
|
1565
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
|
|
1566
|
+
/* @__PURE__ */ jsx9(SectionHeader, { title: "\u5DF2\u653E\u5F03\u64CD\u4F5C" }),
|
|
1567
|
+
errorMsg ? /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
|
|
1568
|
+
"\u2716 ",
|
|
1569
|
+
errorMsg
|
|
1570
|
+
] }) }) : /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
|
|
1571
|
+
"\u2714 ",
|
|
1572
|
+
"\u5DF2\u56DE\u9000\u5230 cherry-pick \u524D\u7684\u72B6\u6001"
|
|
1573
|
+
] }),
|
|
1574
|
+
stashed && stashRestored === false && /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B2 stash \u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" }),
|
|
1575
|
+
stashed && stashRestored === true && /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
|
|
1576
|
+
"\u2714 ",
|
|
1577
|
+
"\u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)"
|
|
1578
|
+
] }),
|
|
1579
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "\u6309\u4EFB\u610F\u952E\u9000\u51FA" })
|
|
1580
|
+
] });
|
|
1581
|
+
}
|
|
1582
|
+
const total = orderedHashes.current.length;
|
|
1583
|
+
return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
|
|
1584
|
+
/* @__PURE__ */ jsx9(SectionHeader, { title: "\u540C\u6B65\u5B8C\u6210" }),
|
|
1585
|
+
/* @__PURE__ */ jsxs9(Box9, { gap: 2, children: [
|
|
1586
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "green", bold: true, children: [
|
|
1587
|
+
"\u2714 ",
|
|
1588
|
+
completedCount,
|
|
1589
|
+
" \u4E2A commit \u5DF2\u540C\u6B65"
|
|
1590
|
+
] }),
|
|
1591
|
+
skippedCount > 0 && /* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
|
|
1592
|
+
skippedCount,
|
|
1593
|
+
" \u4E2A\u5DF2\u8DF3\u8FC7"
|
|
1594
|
+
] })
|
|
1595
|
+
] }),
|
|
1596
|
+
noCommit && /* @__PURE__ */ jsx9(StatusPanel, { type: "success", title: "\u6682\u5B58\u533A\u53D8\u66F4 (git diff --cached --stat)", children: /* @__PURE__ */ jsx9(Text9, { color: "gray", children: stagedStat || "(\u65E0\u53D8\u66F4)" }) }),
|
|
1597
|
+
stashed && (stashRestored ? /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
|
|
1598
|
+
"\u2714 ",
|
|
1599
|
+
"\u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)"
|
|
1600
|
+
] }) : /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B2 stash pop \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" })),
|
|
1601
|
+
noCommit ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
|
|
1602
|
+
/* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)" }),
|
|
1603
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: " \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:" }),
|
|
1604
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
|
|
1166
1605
|
" git diff --cached ",
|
|
1167
|
-
/* @__PURE__ */
|
|
1606
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "# \u67E5\u770B\u8BE6\u7EC6 diff" })
|
|
1168
1607
|
] }),
|
|
1169
|
-
/* @__PURE__ */
|
|
1608
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
|
|
1170
1609
|
' git commit -m "sync: ..." ',
|
|
1171
|
-
/* @__PURE__ */
|
|
1610
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "# \u63D0\u4EA4" })
|
|
1172
1611
|
] }),
|
|
1173
|
-
/* @__PURE__ */
|
|
1612
|
+
/* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
|
|
1174
1613
|
" git reset HEAD ",
|
|
1175
|
-
/* @__PURE__ */
|
|
1614
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "# \u6216\u653E\u5F03" })
|
|
1176
1615
|
] })
|
|
1177
|
-
] })
|
|
1616
|
+
] }) : /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: " \u5DF2\u4FDD\u7559\u539F\u59CB commit \u4FE1\u606F\uFF0C\u53EF\u901A\u8FC7 git log \u67E5\u770B" }) }),
|
|
1617
|
+
/* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "\u6309\u4EFB\u610F\u952E\u9000\u51FA" })
|
|
1178
1618
|
] });
|
|
1179
1619
|
}
|
|
1180
1620
|
|
|
1621
|
+
// src/components/update-banner.tsx
|
|
1622
|
+
import { useState as useState8, useEffect as useEffect6 } from "react";
|
|
1623
|
+
import { Box as Box10, Text as Text10 } from "ink";
|
|
1624
|
+
|
|
1625
|
+
// src/utils/update-check.ts
|
|
1626
|
+
import https from "https";
|
|
1627
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync } from "fs";
|
|
1628
|
+
import { join as join2 } from "path";
|
|
1629
|
+
import { homedir } from "os";
|
|
1630
|
+
var PKG_NAME = "git-sync-tui";
|
|
1631
|
+
var CHECK_INTERVAL = 24 * 60 * 60 * 1e3;
|
|
1632
|
+
var REQUEST_TIMEOUT = 3e3;
|
|
1633
|
+
function getCachePath() {
|
|
1634
|
+
const dir = join2(homedir(), ".config", "git-sync-tui");
|
|
1635
|
+
return join2(dir, "update-check.json");
|
|
1636
|
+
}
|
|
1637
|
+
function readCache() {
|
|
1638
|
+
try {
|
|
1639
|
+
const raw = readFileSync2(getCachePath(), "utf-8");
|
|
1640
|
+
return JSON.parse(raw);
|
|
1641
|
+
} catch {
|
|
1642
|
+
return null;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
function writeCache(data) {
|
|
1646
|
+
try {
|
|
1647
|
+
const dir = join2(homedir(), ".config", "git-sync-tui");
|
|
1648
|
+
mkdirSync(dir, { recursive: true });
|
|
1649
|
+
writeFileSync2(getCachePath(), JSON.stringify(data));
|
|
1650
|
+
} catch {
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
function fetchLatestVersion() {
|
|
1654
|
+
return new Promise((resolve) => {
|
|
1655
|
+
const req = https.get(
|
|
1656
|
+
`https://registry.npmjs.org/${PKG_NAME}/latest`,
|
|
1657
|
+
{ timeout: REQUEST_TIMEOUT, headers: { Accept: "application/json" } },
|
|
1658
|
+
(res) => {
|
|
1659
|
+
if (res.statusCode !== 200) {
|
|
1660
|
+
resolve(null);
|
|
1661
|
+
return;
|
|
1662
|
+
}
|
|
1663
|
+
let data = "";
|
|
1664
|
+
res.on("data", (chunk) => {
|
|
1665
|
+
data += chunk.toString();
|
|
1666
|
+
});
|
|
1667
|
+
res.on("end", () => {
|
|
1668
|
+
try {
|
|
1669
|
+
const json = JSON.parse(data);
|
|
1670
|
+
resolve(json.version || null);
|
|
1671
|
+
} catch {
|
|
1672
|
+
resolve(null);
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
);
|
|
1677
|
+
req.on("error", () => resolve(null));
|
|
1678
|
+
req.on("timeout", () => {
|
|
1679
|
+
req.destroy();
|
|
1680
|
+
resolve(null);
|
|
1681
|
+
});
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
function compareVersions(current, latest) {
|
|
1685
|
+
const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
|
|
1686
|
+
const c = parse(current);
|
|
1687
|
+
const l = parse(latest);
|
|
1688
|
+
for (let i = 0; i < 3; i++) {
|
|
1689
|
+
if ((l[i] || 0) > (c[i] || 0)) return true;
|
|
1690
|
+
if ((l[i] || 0) < (c[i] || 0)) return false;
|
|
1691
|
+
}
|
|
1692
|
+
return false;
|
|
1693
|
+
}
|
|
1694
|
+
async function checkForUpdate(currentVersion) {
|
|
1695
|
+
const noUpdate = { hasUpdate: false, current: currentVersion, latest: currentVersion };
|
|
1696
|
+
const cache = readCache();
|
|
1697
|
+
if (cache && Date.now() - cache.checkedAt < CHECK_INTERVAL) {
|
|
1698
|
+
return {
|
|
1699
|
+
hasUpdate: compareVersions(currentVersion, cache.latest),
|
|
1700
|
+
current: currentVersion,
|
|
1701
|
+
latest: cache.latest
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
const latest = await fetchLatestVersion();
|
|
1705
|
+
if (!latest) return noUpdate;
|
|
1706
|
+
writeCache({ latest, checkedAt: Date.now() });
|
|
1707
|
+
return {
|
|
1708
|
+
hasUpdate: compareVersions(currentVersion, latest),
|
|
1709
|
+
current: currentVersion,
|
|
1710
|
+
latest
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
// src/components/update-banner.tsx
|
|
1715
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1716
|
+
function UpdateBanner({ currentVersion }) {
|
|
1717
|
+
const [info, setInfo] = useState8(null);
|
|
1718
|
+
useEffect6(() => {
|
|
1719
|
+
let cancelled = false;
|
|
1720
|
+
checkForUpdate(currentVersion).then((result) => {
|
|
1721
|
+
if (!cancelled && result.hasUpdate) {
|
|
1722
|
+
setInfo(result);
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1725
|
+
return () => {
|
|
1726
|
+
cancelled = true;
|
|
1727
|
+
};
|
|
1728
|
+
}, [currentVersion]);
|
|
1729
|
+
if (!info) return null;
|
|
1730
|
+
return /* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text10, { color: "yellow", children: [
|
|
1731
|
+
"\u{1F4A1} ",
|
|
1732
|
+
"\u65B0\u7248\u672C\u53EF\u7528 ",
|
|
1733
|
+
/* @__PURE__ */ jsx10(Text10, { bold: true, color: "green", children: info.latest }),
|
|
1734
|
+
/* @__PURE__ */ jsxs10(Text10, { color: "gray", children: [
|
|
1735
|
+
" (\u5F53\u524D ",
|
|
1736
|
+
info.current,
|
|
1737
|
+
")"
|
|
1738
|
+
] }),
|
|
1739
|
+
/* @__PURE__ */ jsx10(Text10, { color: "cyan", children: " \u2192 npm i -g git-sync-tui" })
|
|
1740
|
+
] }) });
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1181
1743
|
// src/app.tsx
|
|
1182
1744
|
import { execSync } from "child_process";
|
|
1183
|
-
import {
|
|
1745
|
+
import { createRequire } from "module";
|
|
1746
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1747
|
+
var require2 = createRequire(import.meta.url);
|
|
1748
|
+
var { version: APP_VERSION } = require2("../package.json");
|
|
1184
1749
|
var STEP_NUMBER = {
|
|
1185
1750
|
checking: 0,
|
|
1186
1751
|
"stash-recovery": 0,
|
|
1187
1752
|
"stash-prompt": 0,
|
|
1188
1753
|
remote: 1,
|
|
1189
1754
|
branch: 2,
|
|
1755
|
+
"branch-check": 2,
|
|
1190
1756
|
commits: 3,
|
|
1191
1757
|
confirm: 4,
|
|
1192
1758
|
result: 5
|
|
@@ -1194,22 +1760,23 @@ var STEP_NUMBER = {
|
|
|
1194
1760
|
var STEP_DEBOUNCE = 100;
|
|
1195
1761
|
function App({ initialRemote, initialBranch }) {
|
|
1196
1762
|
const { exit } = useApp();
|
|
1197
|
-
const entryStep = initialRemote && initialBranch ? "
|
|
1198
|
-
const [step, setStepRaw] =
|
|
1199
|
-
const [inputReady, setInputReady] =
|
|
1200
|
-
const [remote2, setRemote] =
|
|
1201
|
-
const [branch2, setBranch] =
|
|
1202
|
-
const [selectedHashes, setSelectedHashes] =
|
|
1203
|
-
const [commits2, setCommits] =
|
|
1204
|
-
const [hasMerge, setHasMerge] =
|
|
1205
|
-
const [useMainline, setUseMainline] =
|
|
1206
|
-
const [
|
|
1207
|
-
const [
|
|
1208
|
-
const
|
|
1209
|
-
const
|
|
1210
|
-
const
|
|
1211
|
-
const
|
|
1212
|
-
const
|
|
1763
|
+
const entryStep = initialRemote && initialBranch ? "branch-check" : initialRemote ? "branch" : "remote";
|
|
1764
|
+
const [step, setStepRaw] = useState9("checking");
|
|
1765
|
+
const [inputReady, setInputReady] = useState9(true);
|
|
1766
|
+
const [remote2, setRemote] = useState9(initialRemote || "");
|
|
1767
|
+
const [branch2, setBranch] = useState9(initialBranch || "");
|
|
1768
|
+
const [selectedHashes, setSelectedHashes] = useState9([]);
|
|
1769
|
+
const [commits2, setCommits] = useState9([]);
|
|
1770
|
+
const [hasMerge, setHasMerge] = useState9(false);
|
|
1771
|
+
const [useMainline, setUseMainline] = useState9(false);
|
|
1772
|
+
const [noCommit, setNoCommit] = useState9(false);
|
|
1773
|
+
const [stashed, setStashed] = useState9(false);
|
|
1774
|
+
const [guardTimestamp, setGuardTimestamp] = useState9();
|
|
1775
|
+
const stashedRef = useRef5(false);
|
|
1776
|
+
const stashRestoredRef = useRef5(false);
|
|
1777
|
+
const mountedRef = useRef5(true);
|
|
1778
|
+
const debounceTimer = useRef5(null);
|
|
1779
|
+
const setStep = useCallback3((newStep) => {
|
|
1213
1780
|
setInputReady(false);
|
|
1214
1781
|
setStepRaw(newStep);
|
|
1215
1782
|
if (debounceTimer.current) clearTimeout(debounceTimer.current);
|
|
@@ -1217,7 +1784,7 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1217
1784
|
if (mountedRef.current) setInputReady(true);
|
|
1218
1785
|
}, STEP_DEBOUNCE);
|
|
1219
1786
|
}, []);
|
|
1220
|
-
const restoreStashSync =
|
|
1787
|
+
const restoreStashSync = useCallback3(() => {
|
|
1221
1788
|
if (stashedRef.current && !stashRestoredRef.current) {
|
|
1222
1789
|
try {
|
|
1223
1790
|
execSync("git stash pop", { stdio: "ignore" });
|
|
@@ -1231,11 +1798,11 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1231
1798
|
}
|
|
1232
1799
|
}
|
|
1233
1800
|
}, []);
|
|
1234
|
-
const markStashRestored =
|
|
1801
|
+
const markStashRestored = useCallback3(() => {
|
|
1235
1802
|
stashRestoredRef.current = true;
|
|
1236
1803
|
removeStashGuard();
|
|
1237
1804
|
}, []);
|
|
1238
|
-
|
|
1805
|
+
useEffect7(() => {
|
|
1239
1806
|
mountedRef.current = true;
|
|
1240
1807
|
async function check() {
|
|
1241
1808
|
const guard = await checkStashGuard();
|
|
@@ -1290,9 +1857,10 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1290
1857
|
const clean = await isWorkingDirClean();
|
|
1291
1858
|
if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
|
|
1292
1859
|
};
|
|
1293
|
-
const goBack =
|
|
1860
|
+
const goBack = useCallback3((fromStep) => {
|
|
1294
1861
|
const backMap = {
|
|
1295
1862
|
branch: "remote",
|
|
1863
|
+
"branch-check": "branch",
|
|
1296
1864
|
commits: "branch",
|
|
1297
1865
|
confirm: "commits"
|
|
1298
1866
|
};
|
|
@@ -1304,10 +1872,10 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1304
1872
|
exit();
|
|
1305
1873
|
}
|
|
1306
1874
|
}, [setStep, restoreStashSync, exit]);
|
|
1307
|
-
return /* @__PURE__ */
|
|
1308
|
-
/* @__PURE__ */
|
|
1309
|
-
step === "checking" && /* @__PURE__ */
|
|
1310
|
-
step === "stash-recovery" && inputReady && /* @__PURE__ */
|
|
1875
|
+
return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
|
|
1876
|
+
/* @__PURE__ */ jsx11(AppHeader, { step: STEP_NUMBER[step], stashed, noCommit }),
|
|
1877
|
+
step === "checking" && /* @__PURE__ */ jsx11(Spinner7, { label: "\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001..." }),
|
|
1878
|
+
step === "stash-recovery" && inputReady && /* @__PURE__ */ jsx11(
|
|
1311
1879
|
StashRecovery,
|
|
1312
1880
|
{
|
|
1313
1881
|
timestamp: guardTimestamp,
|
|
@@ -1315,14 +1883,14 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1315
1883
|
onSkip: skipStashRecover
|
|
1316
1884
|
}
|
|
1317
1885
|
),
|
|
1318
|
-
step === "stash-prompt" && inputReady && /* @__PURE__ */
|
|
1886
|
+
step === "stash-prompt" && inputReady && /* @__PURE__ */ jsx11(
|
|
1319
1887
|
StashPrompt,
|
|
1320
1888
|
{
|
|
1321
1889
|
onConfirm: doStash,
|
|
1322
1890
|
onSkip: () => setStep(entryStep)
|
|
1323
1891
|
}
|
|
1324
1892
|
),
|
|
1325
|
-
step === "remote" && inputReady && /* @__PURE__ */
|
|
1893
|
+
step === "remote" && inputReady && /* @__PURE__ */ jsx11(
|
|
1326
1894
|
RemoteSelect,
|
|
1327
1895
|
{
|
|
1328
1896
|
onSelect: (r) => {
|
|
@@ -1332,49 +1900,58 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1332
1900
|
onBack: () => goBack("remote")
|
|
1333
1901
|
}
|
|
1334
1902
|
),
|
|
1335
|
-
step === "branch" && inputReady && /* @__PURE__ */
|
|
1903
|
+
step === "branch" && inputReady && /* @__PURE__ */ jsx11(
|
|
1336
1904
|
BranchSelect,
|
|
1337
1905
|
{
|
|
1338
1906
|
remote: remote2,
|
|
1339
1907
|
onSelect: (b) => {
|
|
1340
1908
|
setBranch(b);
|
|
1341
|
-
setStep("
|
|
1909
|
+
setStep("branch-check");
|
|
1342
1910
|
},
|
|
1343
1911
|
onBack: () => goBack("branch")
|
|
1344
1912
|
}
|
|
1345
1913
|
),
|
|
1346
|
-
step === "
|
|
1914
|
+
step === "branch-check" && inputReady && /* @__PURE__ */ jsx11(
|
|
1915
|
+
BranchCheck,
|
|
1916
|
+
{
|
|
1917
|
+
targetBranch: branch2,
|
|
1918
|
+
onContinue: () => setStep("commits"),
|
|
1919
|
+
onBack: () => goBack("branch-check")
|
|
1920
|
+
}
|
|
1921
|
+
),
|
|
1922
|
+
step === "commits" && inputReady && /* @__PURE__ */ jsx11(
|
|
1347
1923
|
CommitList,
|
|
1348
1924
|
{
|
|
1349
1925
|
remote: remote2,
|
|
1350
1926
|
branch: branch2,
|
|
1351
|
-
onSelect:
|
|
1927
|
+
onSelect: (hashes, loadedCommits) => {
|
|
1352
1928
|
setSelectedHashes(hashes);
|
|
1353
1929
|
setCommits(loadedCommits);
|
|
1354
|
-
const merge = await hasMergeCommits(hashes);
|
|
1355
|
-
setHasMerge(merge);
|
|
1356
1930
|
setStep("confirm");
|
|
1357
1931
|
},
|
|
1358
1932
|
onBack: () => goBack("commits")
|
|
1359
1933
|
}
|
|
1360
1934
|
),
|
|
1361
|
-
step === "confirm" && inputReady && /* @__PURE__ */
|
|
1935
|
+
step === "confirm" && inputReady && /* @__PURE__ */ jsx11(
|
|
1362
1936
|
ConfirmPanel,
|
|
1363
1937
|
{
|
|
1364
1938
|
commits: commits2,
|
|
1365
1939
|
selectedHashes,
|
|
1366
1940
|
hasMerge,
|
|
1367
1941
|
useMainline,
|
|
1942
|
+
noCommit,
|
|
1368
1943
|
onToggleMainline: () => setUseMainline((v) => !v),
|
|
1944
|
+
onToggleNoCommit: () => setNoCommit((v) => !v),
|
|
1369
1945
|
onConfirm: () => setStep("result"),
|
|
1370
1946
|
onCancel: () => goBack("confirm")
|
|
1371
1947
|
}
|
|
1372
1948
|
),
|
|
1373
|
-
step === "result" && /* @__PURE__ */
|
|
1949
|
+
step === "result" && /* @__PURE__ */ jsx11(
|
|
1374
1950
|
ResultPanel,
|
|
1375
1951
|
{
|
|
1376
1952
|
selectedHashes,
|
|
1377
1953
|
useMainline,
|
|
1954
|
+
noCommit,
|
|
1378
1955
|
stashed,
|
|
1379
1956
|
onStashRestored: markStashRestored,
|
|
1380
1957
|
onDone: () => {
|
|
@@ -1382,12 +1959,16 @@ function App({ initialRemote, initialBranch }) {
|
|
|
1382
1959
|
exit();
|
|
1383
1960
|
}
|
|
1384
1961
|
}
|
|
1385
|
-
)
|
|
1962
|
+
),
|
|
1963
|
+
/* @__PURE__ */ jsx11(UpdateBanner, { currentVersion: APP_VERSION })
|
|
1386
1964
|
] });
|
|
1387
1965
|
}
|
|
1388
1966
|
|
|
1389
1967
|
// src/cli-runner.ts
|
|
1390
1968
|
import { createInterface } from "readline";
|
|
1969
|
+
import { createRequire as createRequire2 } from "module";
|
|
1970
|
+
var require3 = createRequire2(import.meta.url);
|
|
1971
|
+
var { version: APP_VERSION2 } = require3("../package.json");
|
|
1391
1972
|
function log(msg) {
|
|
1392
1973
|
process.stdout.write(msg + "\n");
|
|
1393
1974
|
}
|
|
@@ -1542,16 +2123,24 @@ ${stat}`);
|
|
|
1542
2123
|
process.exit(1);
|
|
1543
2124
|
}
|
|
1544
2125
|
}
|
|
2126
|
+
async function printUpdateNotice() {
|
|
2127
|
+
const info = await checkForUpdate(APP_VERSION2);
|
|
2128
|
+
if (info.hasUpdate) {
|
|
2129
|
+
log(`
|
|
2130
|
+
\u{1F4A1} \u65B0\u7248\u672C\u53EF\u7528 ${info.latest} (\u5F53\u524D ${info.current}) \u2192 npm i -g git-sync-tui`);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
1545
2133
|
async function runCli(opts) {
|
|
1546
2134
|
if (opts.list) {
|
|
1547
2135
|
await runList(opts);
|
|
1548
2136
|
} else {
|
|
1549
2137
|
await runExec(opts);
|
|
1550
2138
|
}
|
|
2139
|
+
await printUpdateNotice();
|
|
1551
2140
|
}
|
|
1552
2141
|
|
|
1553
2142
|
// src/cli.tsx
|
|
1554
|
-
import { jsx as
|
|
2143
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1555
2144
|
var cli = meow(
|
|
1556
2145
|
`
|
|
1557
2146
|
\u7528\u6CD5
|
|
@@ -1625,5 +2214,5 @@ if (isCliMode) {
|
|
|
1625
2214
|
process.exit(1);
|
|
1626
2215
|
});
|
|
1627
2216
|
} else {
|
|
1628
|
-
render(/* @__PURE__ */
|
|
2217
|
+
render(/* @__PURE__ */ jsx12(App, { initialRemote: remote, initialBranch: branch }));
|
|
1629
2218
|
}
|