git-sync-tui 0.1.5 → 0.1.6

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.
Files changed (2) hide show
  1. package/dist/cli.js +558 -112
  2. 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 useState7, useEffect as useEffect5, useRef as useRef3, useCallback as useCallback2 } from "react";
15
- import { Box as Box9, useApp } from "ink";
16
- import { Spinner as Spinner6 } from "@inkjs/ui";
14
+ import { useState as useState8, useEffect as useEffect6, useRef as useRef5, useCallback as useCallback3 } from "react";
15
+ import { Box as Box10, 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__ */ jsx(Text, { color: "gray", children: "cherry-pick --no-commit" }),
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/confirm-panel.tsx
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
- function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline, onToggleMainline, onConfirm, onCancel }) {
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
- return /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", gap: 1, children: [
1063
- /* @__PURE__ */ jsx7(SectionHeader, { title: "\u786E\u8BA4\u6267\u884C" }),
1064
- /* @__PURE__ */ jsx7(StatusPanel, { type: "info", title: `cherry-pick --no-commit \xB7 ${selectedCommits.length} \u4E2A commit`, children: selectedCommits.map((c) => /* @__PURE__ */ jsxs7(Box7, { children: [
1065
- /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
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__ */ jsxs7(Text7, { children: [
1293
+ /* @__PURE__ */ jsxs8(Text8, { children: [
1070
1294
  " ",
1071
1295
  c.message
1072
1296
  ] }),
1073
- /* @__PURE__ */ jsxs7(Text7, { color: "gray", dimColor: true, children: [
1297
+ /* @__PURE__ */ jsxs8(Text8, { color: "gray", dimColor: true, children: [
1074
1298
  " ",
1075
1299
  c.author
1076
1300
  ] })
1077
1301
  ] }, c.hash)) }),
1078
- hasMerge && /* @__PURE__ */ jsxs7(StatusPanel, { type: "warn", title: "\u68C0\u6D4B\u5230 Merge Commit", children: [
1079
- /* @__PURE__ */ jsx7(Text7, { color: "gray", children: " Cherry-pick \u5408\u5E76\u63D0\u4EA4\u9700\u8981\u6307\u5B9A\u7236\u8282\u70B9 (-m 1)" }),
1080
- /* @__PURE__ */ jsxs7(Box7, { children: [
1081
- /* @__PURE__ */ jsx7(Text7, { children: " " }),
1082
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: "[m]" }),
1083
- /* @__PURE__ */ jsx7(Text7, { children: " \u5207\u6362 -m 1: " }),
1084
- useMainline ? /* @__PURE__ */ jsx7(Text7, { color: "green", bold: true, children: "\u5DF2\u542F\u7528" }) : /* @__PURE__ */ jsx7(Text7, { color: "gray", children: "\u672A\u542F\u7528" })
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__ */ jsxs7(Box7, { children: [
1088
- /* @__PURE__ */ jsx7(Text7, { color: "yellow", children: "\u25B2 " }),
1089
- /* @__PURE__ */ jsx7(Text7, { color: "gray", children: "--no-commit \u6A21\u5F0F\uFF0C\u6539\u52A8\u5C06\u6682\u5B58\u5230\u5DE5\u4F5C\u533A\uFF0C\u9700\u624B\u52A8 commit" })
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__ */ jsxs7(Box7, { children: [
1092
- /* @__PURE__ */ jsx7(Text7, { bold: true, children: "\u786E\u8BA4\u6267\u884C? " }),
1093
- /* @__PURE__ */ jsx7(InlineKeys, { hints: [
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,295 @@ function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline
1101
1333
  }
1102
1334
 
1103
1335
  // src/components/result-panel.tsx
1104
- import { useState as useState6, useEffect as useEffect4 } from "react";
1105
- import { Box as Box8, Text as Text8 } from "ink";
1106
- import { Spinner as Spinner5 } from "@inkjs/ui";
1107
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
1108
- function ResultPanel({ selectedHashes, useMainline, stashed, onStashRestored, onDone }) {
1109
- const [phase, setPhase] = useState6("executing");
1110
- const [result, setResult] = useState6(null);
1111
- const [stagedStat, setStagedStat] = useState6("");
1112
- const [stashRestored, setStashRestored] = useState6(null);
1113
- const tryRestoreStash = async () => {
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
- useEffect4(() => {
1122
- async function run() {
1123
- const res = await cherryPick(selectedHashes, useMainline);
1124
- setResult(res);
1125
- if (res.success) {
1126
- const stat = await getStagedStat();
1127
- setStagedStat(stat);
1128
- await tryRestoreStash();
1129
- setPhase("done");
1130
- } else {
1131
- await tryRestoreStash();
1132
- setPhase("error");
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
- run();
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 === "conflict") {
1467
+ if (input === "c" || input === "C") {
1468
+ handleContinue();
1469
+ } else if (input === "a" || input === "A") {
1470
+ handleAbort();
1471
+ } else if (input === "q" || input === "Q") {
1472
+ onDone();
1473
+ }
1474
+ } else if (phase === "empty") {
1475
+ if (input === "s" || input === "S") {
1476
+ handleSkip();
1477
+ } else if (input === "a" || input === "A") {
1478
+ handleAbort();
1479
+ } else if (input === "q" || input === "Q") {
1480
+ onDone();
1481
+ }
1482
+ }
1483
+ });
1137
1484
  if (phase === "executing") {
1138
- return /* @__PURE__ */ jsx8(Spinner5, { label: `cherry-pick --no-commit (${selectedHashes.length} \u4E2A commit)...` });
1485
+ const mode = noCommit ? "--no-commit" : "";
1486
+ return /* @__PURE__ */ jsx9(Spinner6, { label: `cherry-pick ${mode} (${currentIndex + 1}/${orderedHashes.current.length})...` });
1487
+ }
1488
+ if (phase === "continuing") {
1489
+ return /* @__PURE__ */ jsx9(Spinner6, { label: "\u7EE7\u7EED cherry-pick..." });
1490
+ }
1491
+ if (phase === "aborting") {
1492
+ return /* @__PURE__ */ jsx9(Spinner6, { label: "\u6B63\u5728\u653E\u5F03 cherry-pick..." });
1139
1493
  }
1140
1494
  if (phase === "restoring") {
1141
- return /* @__PURE__ */ jsx8(Spinner5, { label: "\u6062\u590D\u5DE5\u4F5C\u533A (git stash pop)..." });
1495
+ return /* @__PURE__ */ jsx9(Spinner6, { label: "\u6062\u590D\u5DE5\u4F5C\u533A (git stash pop)..." });
1496
+ }
1497
+ if (phase === "empty") {
1498
+ const total2 = orderedHashes.current.length;
1499
+ const currentHash = orderedHashes.current[total2 - remainingRef.current.length - 1];
1500
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
1501
+ /* @__PURE__ */ jsx9(SectionHeader, { title: "\u7A7A\u63D0\u4EA4" }),
1502
+ /* @__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" }) }),
1503
+ /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(InlineKeys, { hints: [
1504
+ { key: "s", label: "\u8DF3\u8FC7\u6B64 commit (skip)" },
1505
+ { key: "a", label: "\u653E\u5F03\u5168\u90E8 (abort)" },
1506
+ { key: "q", label: "\u9000\u51FA (\u4FDD\u7559\u5F53\u524D\u72B6\u6001)" }
1507
+ ] }) })
1508
+ ] });
1142
1509
  }
1143
- if (phase === "error" && result) {
1144
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
1145
- /* @__PURE__ */ jsx8(SectionHeader, { title: "Cherry-pick \u9047\u5230\u51B2\u7A81" }),
1146
- result.conflictFiles && result.conflictFiles.length > 0 && /* @__PURE__ */ jsx8(StatusPanel, { type: "error", title: "\u51B2\u7A81\u6587\u4EF6", children: result.conflictFiles.map((f) => /* @__PURE__ */ jsxs8(Text8, { color: "red", children: [
1510
+ if (phase === "conflict") {
1511
+ const total2 = orderedHashes.current.length;
1512
+ const doneCount = total2 - remainingRef.current.length - 1;
1513
+ const currentHash = orderedHashes.current[total2 - remainingRef.current.length - 1];
1514
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
1515
+ /* @__PURE__ */ jsx9(SectionHeader, { title: "Cherry-pick \u9047\u5230\u51B2\u7A81" }),
1516
+ /* @__PURE__ */ jsxs9(Box9, { gap: 2, children: [
1517
+ /* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
1518
+ "\u8FDB\u5EA6: ",
1519
+ doneCount,
1520
+ "/",
1521
+ total2
1522
+ ] }),
1523
+ /* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
1524
+ "\u51B2\u7A81 commit: ",
1525
+ currentHash?.substring(0, 7)
1526
+ ] }),
1527
+ remainingRef.current.length > 0 && /* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
1528
+ "\u5269\u4F59: ",
1529
+ remainingRef.current.length
1530
+ ] })
1531
+ ] }),
1532
+ conflictFiles.length > 0 && /* @__PURE__ */ jsx9(StatusPanel, { type: "error", title: "\u51B2\u7A81\u6587\u4EF6", children: conflictFiles.map((f) => /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
1147
1533
  " ",
1148
1534
  f
1149
1535
  ] }, f)) }),
1150
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1151
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 \u624B\u52A8\u89E3\u51B3\u51B2\u7A81\u540E\u6267\u884C git add \u548C git commit" }),
1152
- /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "\u25B8 \u6216\u6267\u884C git cherry-pick --abort \u653E\u5F03\u64CD\u4F5C" })
1536
+ /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1537
+ /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B8 \u8BF7\u5728\u53E6\u4E00\u4E2A\u7EC8\u7AEF\u4E2D\u624B\u52A8\u89E3\u51B3\u51B2\u7A81" }),
1538
+ /* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
1539
+ " 1. \u7F16\u8F91\u51B2\u7A81\u6587\u4EF6\uFF0C\u89E3\u51B3\u51B2\u7A81\u6807\u8BB0 ",
1540
+ "<<<<<<< / ======= / >>>>>>>"
1541
+ ] }),
1542
+ /* @__PURE__ */ jsxs9(Text9, { color: "gray", dimColor: true, children: [
1543
+ " 2. \u6267\u884C git add ",
1544
+ "<file>",
1545
+ " \u6807\u8BB0\u5DF2\u89E3\u51B3"
1546
+ ] }),
1547
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: " 3. \u56DE\u5230\u6B64\u5904\u6309 [c] \u7EE7\u7EED" })
1153
1548
  ] }),
1154
- stashed && stashRestored === false && /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 stash \u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" }),
1155
- stashed && stashRestored === true && /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)" })
1549
+ errorMsg && /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
1550
+ "\u2716 ",
1551
+ errorMsg
1552
+ ] }),
1553
+ /* @__PURE__ */ jsx9(Box9, { children: /* @__PURE__ */ jsx9(InlineKeys, { hints: [
1554
+ { key: "c", label: "\u7EE7\u7EED (\u51B2\u7A81\u5DF2\u89E3\u51B3)" },
1555
+ { key: "a", label: "\u653E\u5F03 (abort)" },
1556
+ { key: "q", label: "\u9000\u51FA (\u4FDD\u7559\u5F53\u524D\u72B6\u6001)" }
1557
+ ] }) })
1156
1558
  ] });
1157
1559
  }
1158
- return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", gap: 1, children: [
1159
- /* @__PURE__ */ jsx8(SectionHeader, { title: "\u540C\u6B65\u5B8C\u6210" }),
1160
- /* @__PURE__ */ jsx8(StatusPanel, { type: "success", title: "\u6682\u5B58\u533A\u53D8\u66F4 (git diff --cached --stat)", children: /* @__PURE__ */ jsx8(Text8, { color: "gray", children: stagedStat || "(\u65E0\u53D8\u66F4)" }) }),
1161
- stashed && (stashRestored ? /* @__PURE__ */ jsx8(Text8, { color: "green", children: "\u2714 \u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)" }) : /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 stash pop \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" })),
1162
- /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
1163
- /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)" }),
1164
- /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: " \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:" }),
1165
- /* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
1560
+ if (phase === "aborted") {
1561
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
1562
+ /* @__PURE__ */ jsx9(SectionHeader, { title: "\u5DF2\u653E\u5F03\u64CD\u4F5C" }),
1563
+ errorMsg ? /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
1564
+ "\u2716 ",
1565
+ errorMsg
1566
+ ] }) }) : /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
1567
+ "\u2714 ",
1568
+ "\u5DF2\u56DE\u9000\u5230 cherry-pick \u524D\u7684\u72B6\u6001"
1569
+ ] }),
1570
+ stashed && stashRestored === false && /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B2 stash \u6062\u590D\u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" }),
1571
+ stashed && stashRestored === true && /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
1572
+ "\u2714 ",
1573
+ "\u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)"
1574
+ ] })
1575
+ ] });
1576
+ }
1577
+ const total = orderedHashes.current.length;
1578
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", gap: 1, children: [
1579
+ /* @__PURE__ */ jsx9(SectionHeader, { title: "\u540C\u6B65\u5B8C\u6210" }),
1580
+ /* @__PURE__ */ jsxs9(Box9, { gap: 2, children: [
1581
+ /* @__PURE__ */ jsxs9(Text9, { color: "green", bold: true, children: [
1582
+ "\u2714 ",
1583
+ completedCount,
1584
+ " \u4E2A commit \u5DF2\u540C\u6B65"
1585
+ ] }),
1586
+ skippedCount > 0 && /* @__PURE__ */ jsxs9(Text9, { color: "yellow", children: [
1587
+ skippedCount,
1588
+ " \u4E2A\u5DF2\u8DF3\u8FC7"
1589
+ ] })
1590
+ ] }),
1591
+ 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)" }) }),
1592
+ stashed && (stashRestored ? /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
1593
+ "\u2714 ",
1594
+ "\u5DF2\u6062\u590D\u5DE5\u4F5C\u533A\u53D8\u66F4 (stash pop)"
1595
+ ] }) : /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B2 stash pop \u5931\u8D25\uFF0C\u8BF7\u624B\u52A8 git stash pop" })),
1596
+ noCommit ? /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1597
+ /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "\u25B2 \u6539\u52A8\u5DF2\u6682\u5B58\u5230\u5DE5\u4F5C\u533A (--no-commit \u6A21\u5F0F)" }),
1598
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: " \u5BA1\u67E5\u540E\u624B\u52A8\u6267\u884C:" }),
1599
+ /* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
1166
1600
  " git diff --cached ",
1167
- /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u67E5\u770B\u8BE6\u7EC6 diff" })
1601
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "# \u67E5\u770B\u8BE6\u7EC6 diff" })
1168
1602
  ] }),
1169
- /* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
1603
+ /* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
1170
1604
  ' git commit -m "sync: ..." ',
1171
- /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u63D0\u4EA4" })
1605
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "# \u63D0\u4EA4" })
1172
1606
  ] }),
1173
- /* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
1607
+ /* @__PURE__ */ jsxs9(Text9, { color: "cyan", children: [
1174
1608
  " git reset HEAD ",
1175
- /* @__PURE__ */ jsx8(Text8, { color: "gray", dimColor: true, children: "# \u6216\u653E\u5F03" })
1609
+ /* @__PURE__ */ jsx9(Text9, { color: "gray", dimColor: true, children: "# \u6216\u653E\u5F03" })
1176
1610
  ] })
1177
- ] })
1611
+ ] }) : /* @__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" }) })
1178
1612
  ] });
1179
1613
  }
1180
1614
 
1181
1615
  // src/app.tsx
1182
1616
  import { execSync } from "child_process";
1183
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
1617
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
1184
1618
  var STEP_NUMBER = {
1185
1619
  checking: 0,
1186
1620
  "stash-recovery": 0,
1187
1621
  "stash-prompt": 0,
1188
1622
  remote: 1,
1189
1623
  branch: 2,
1624
+ "branch-check": 2,
1190
1625
  commits: 3,
1191
1626
  confirm: 4,
1192
1627
  result: 5
@@ -1194,22 +1629,23 @@ var STEP_NUMBER = {
1194
1629
  var STEP_DEBOUNCE = 100;
1195
1630
  function App({ initialRemote, initialBranch }) {
1196
1631
  const { exit } = useApp();
1197
- const entryStep = initialRemote && initialBranch ? "commits" : initialRemote ? "branch" : "remote";
1198
- const [step, setStepRaw] = useState7("checking");
1199
- const [inputReady, setInputReady] = useState7(true);
1200
- const [remote2, setRemote] = useState7(initialRemote || "");
1201
- const [branch2, setBranch] = useState7(initialBranch || "");
1202
- const [selectedHashes, setSelectedHashes] = useState7([]);
1203
- const [commits2, setCommits] = useState7([]);
1204
- const [hasMerge, setHasMerge] = useState7(false);
1205
- const [useMainline, setUseMainline] = useState7(false);
1206
- const [stashed, setStashed] = useState7(false);
1207
- const [guardTimestamp, setGuardTimestamp] = useState7();
1208
- const stashedRef = useRef3(false);
1209
- const stashRestoredRef = useRef3(false);
1210
- const mountedRef = useRef3(true);
1211
- const debounceTimer = useRef3(null);
1212
- const setStep = useCallback2((newStep) => {
1632
+ const entryStep = initialRemote && initialBranch ? "branch-check" : initialRemote ? "branch" : "remote";
1633
+ const [step, setStepRaw] = useState8("checking");
1634
+ const [inputReady, setInputReady] = useState8(true);
1635
+ const [remote2, setRemote] = useState8(initialRemote || "");
1636
+ const [branch2, setBranch] = useState8(initialBranch || "");
1637
+ const [selectedHashes, setSelectedHashes] = useState8([]);
1638
+ const [commits2, setCommits] = useState8([]);
1639
+ const [hasMerge, setHasMerge] = useState8(false);
1640
+ const [useMainline, setUseMainline] = useState8(false);
1641
+ const [noCommit, setNoCommit] = useState8(false);
1642
+ const [stashed, setStashed] = useState8(false);
1643
+ const [guardTimestamp, setGuardTimestamp] = useState8();
1644
+ const stashedRef = useRef5(false);
1645
+ const stashRestoredRef = useRef5(false);
1646
+ const mountedRef = useRef5(true);
1647
+ const debounceTimer = useRef5(null);
1648
+ const setStep = useCallback3((newStep) => {
1213
1649
  setInputReady(false);
1214
1650
  setStepRaw(newStep);
1215
1651
  if (debounceTimer.current) clearTimeout(debounceTimer.current);
@@ -1217,7 +1653,7 @@ function App({ initialRemote, initialBranch }) {
1217
1653
  if (mountedRef.current) setInputReady(true);
1218
1654
  }, STEP_DEBOUNCE);
1219
1655
  }, []);
1220
- const restoreStashSync = useCallback2(() => {
1656
+ const restoreStashSync = useCallback3(() => {
1221
1657
  if (stashedRef.current && !stashRestoredRef.current) {
1222
1658
  try {
1223
1659
  execSync("git stash pop", { stdio: "ignore" });
@@ -1231,11 +1667,11 @@ function App({ initialRemote, initialBranch }) {
1231
1667
  }
1232
1668
  }
1233
1669
  }, []);
1234
- const markStashRestored = useCallback2(() => {
1670
+ const markStashRestored = useCallback3(() => {
1235
1671
  stashRestoredRef.current = true;
1236
1672
  removeStashGuard();
1237
1673
  }, []);
1238
- useEffect5(() => {
1674
+ useEffect6(() => {
1239
1675
  mountedRef.current = true;
1240
1676
  async function check() {
1241
1677
  const guard = await checkStashGuard();
@@ -1290,9 +1726,10 @@ function App({ initialRemote, initialBranch }) {
1290
1726
  const clean = await isWorkingDirClean();
1291
1727
  if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
1292
1728
  };
1293
- const goBack = useCallback2((fromStep) => {
1729
+ const goBack = useCallback3((fromStep) => {
1294
1730
  const backMap = {
1295
1731
  branch: "remote",
1732
+ "branch-check": "branch",
1296
1733
  commits: "branch",
1297
1734
  confirm: "commits"
1298
1735
  };
@@ -1304,10 +1741,10 @@ function App({ initialRemote, initialBranch }) {
1304
1741
  exit();
1305
1742
  }
1306
1743
  }, [setStep, restoreStashSync, exit]);
1307
- return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "column", children: [
1308
- /* @__PURE__ */ jsx9(AppHeader, { step: STEP_NUMBER[step], stashed }),
1309
- step === "checking" && /* @__PURE__ */ jsx9(Spinner6, { label: "\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001..." }),
1310
- step === "stash-recovery" && inputReady && /* @__PURE__ */ jsx9(
1744
+ return /* @__PURE__ */ jsxs10(Box10, { flexDirection: "column", children: [
1745
+ /* @__PURE__ */ jsx10(AppHeader, { step: STEP_NUMBER[step], stashed, noCommit }),
1746
+ step === "checking" && /* @__PURE__ */ jsx10(Spinner7, { label: "\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001..." }),
1747
+ step === "stash-recovery" && inputReady && /* @__PURE__ */ jsx10(
1311
1748
  StashRecovery,
1312
1749
  {
1313
1750
  timestamp: guardTimestamp,
@@ -1315,14 +1752,14 @@ function App({ initialRemote, initialBranch }) {
1315
1752
  onSkip: skipStashRecover
1316
1753
  }
1317
1754
  ),
1318
- step === "stash-prompt" && inputReady && /* @__PURE__ */ jsx9(
1755
+ step === "stash-prompt" && inputReady && /* @__PURE__ */ jsx10(
1319
1756
  StashPrompt,
1320
1757
  {
1321
1758
  onConfirm: doStash,
1322
1759
  onSkip: () => setStep(entryStep)
1323
1760
  }
1324
1761
  ),
1325
- step === "remote" && inputReady && /* @__PURE__ */ jsx9(
1762
+ step === "remote" && inputReady && /* @__PURE__ */ jsx10(
1326
1763
  RemoteSelect,
1327
1764
  {
1328
1765
  onSelect: (r) => {
@@ -1332,49 +1769,58 @@ function App({ initialRemote, initialBranch }) {
1332
1769
  onBack: () => goBack("remote")
1333
1770
  }
1334
1771
  ),
1335
- step === "branch" && inputReady && /* @__PURE__ */ jsx9(
1772
+ step === "branch" && inputReady && /* @__PURE__ */ jsx10(
1336
1773
  BranchSelect,
1337
1774
  {
1338
1775
  remote: remote2,
1339
1776
  onSelect: (b) => {
1340
1777
  setBranch(b);
1341
- setStep("commits");
1778
+ setStep("branch-check");
1342
1779
  },
1343
1780
  onBack: () => goBack("branch")
1344
1781
  }
1345
1782
  ),
1346
- step === "commits" && inputReady && /* @__PURE__ */ jsx9(
1783
+ step === "branch-check" && inputReady && /* @__PURE__ */ jsx10(
1784
+ BranchCheck,
1785
+ {
1786
+ targetBranch: branch2,
1787
+ onContinue: () => setStep("commits"),
1788
+ onBack: () => goBack("branch-check")
1789
+ }
1790
+ ),
1791
+ step === "commits" && inputReady && /* @__PURE__ */ jsx10(
1347
1792
  CommitList,
1348
1793
  {
1349
1794
  remote: remote2,
1350
1795
  branch: branch2,
1351
- onSelect: async (hashes, loadedCommits) => {
1796
+ onSelect: (hashes, loadedCommits) => {
1352
1797
  setSelectedHashes(hashes);
1353
1798
  setCommits(loadedCommits);
1354
- const merge = await hasMergeCommits(hashes);
1355
- setHasMerge(merge);
1356
1799
  setStep("confirm");
1357
1800
  },
1358
1801
  onBack: () => goBack("commits")
1359
1802
  }
1360
1803
  ),
1361
- step === "confirm" && inputReady && /* @__PURE__ */ jsx9(
1804
+ step === "confirm" && inputReady && /* @__PURE__ */ jsx10(
1362
1805
  ConfirmPanel,
1363
1806
  {
1364
1807
  commits: commits2,
1365
1808
  selectedHashes,
1366
1809
  hasMerge,
1367
1810
  useMainline,
1811
+ noCommit,
1368
1812
  onToggleMainline: () => setUseMainline((v) => !v),
1813
+ onToggleNoCommit: () => setNoCommit((v) => !v),
1369
1814
  onConfirm: () => setStep("result"),
1370
1815
  onCancel: () => goBack("confirm")
1371
1816
  }
1372
1817
  ),
1373
- step === "result" && /* @__PURE__ */ jsx9(
1818
+ step === "result" && /* @__PURE__ */ jsx10(
1374
1819
  ResultPanel,
1375
1820
  {
1376
1821
  selectedHashes,
1377
1822
  useMainline,
1823
+ noCommit,
1378
1824
  stashed,
1379
1825
  onStashRestored: markStashRestored,
1380
1826
  onDone: () => {
@@ -1551,7 +1997,7 @@ async function runCli(opts) {
1551
1997
  }
1552
1998
 
1553
1999
  // src/cli.tsx
1554
- import { jsx as jsx10 } from "react/jsx-runtime";
2000
+ import { jsx as jsx11 } from "react/jsx-runtime";
1555
2001
  var cli = meow(
1556
2002
  `
1557
2003
  \u7528\u6CD5
@@ -1625,5 +2071,5 @@ if (isCliMode) {
1625
2071
  process.exit(1);
1626
2072
  });
1627
2073
  } else {
1628
- render(/* @__PURE__ */ jsx10(App, { initialRemote: remote, initialBranch: branch }));
2074
+ render(/* @__PURE__ */ jsx11(App, { initialRemote: remote, initialBranch: branch }));
1629
2075
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "git-sync-tui",
3
3
  "type": "module",
4
- "version": "0.1.5",
4
+ "version": "0.1.6",
5
5
  "packageManager": "pnpm@10.32.1",
6
6
  "description": "Interactive TUI tool for cross-repo git commit synchronization (cherry-pick --no-commit)",
7
7
  "author": "KiWi233333",