git-sync-tui 0.1.7 → 0.1.9

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 +53 -51
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1,17 +1,11 @@
1
1
  #!/usr/bin/env node
2
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
- }) : x)(function(x) {
5
- if (typeof require !== "undefined") return require.apply(this, arguments);
6
- throw Error('Dynamic require of "' + x + '" is not supported');
7
- });
8
2
 
9
3
  // src/cli.tsx
10
4
  import { render } from "ink";
11
5
  import meow from "meow";
12
6
 
13
7
  // src/app.tsx
14
- import { useState as useState9, useEffect as useEffect7, useRef as useRef5, useCallback as useCallback3 } from "react";
8
+ import { useState as useState9, useEffect as useEffect7, useRef as useRef5, useCallback as useCallback4 } from "react";
15
9
  import { Box as Box11, useApp } from "ink";
16
10
  import { Spinner as Spinner7 } from "@inkjs/ui";
17
11
 
@@ -92,10 +86,14 @@ function StatusPanel({ type, title, children }) {
92
86
  children
93
87
  ] });
94
88
  }
95
- function AppHeader({ step, stashed, noCommit }) {
89
+ function AppHeader({ step, stashed, noCommit, version }) {
96
90
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
97
91
  /* @__PURE__ */ jsxs(Box, { gap: 1, children: [
98
92
  /* @__PURE__ */ jsx(Text, { backgroundColor: "cyan", color: "white", bold: true, children: " git-sync-tui " }),
93
+ version && /* @__PURE__ */ jsxs(Text, { color: "gray", dimColor: true, children: [
94
+ "v",
95
+ version
96
+ ] }),
99
97
  /* @__PURE__ */ jsxs(Text, { color: "gray", children: [
100
98
  "cherry-pick",
101
99
  noCommit ? " --no-commit" : ""
@@ -140,11 +138,13 @@ import { Spinner } from "@inkjs/ui";
140
138
  // src/utils/git.ts
141
139
  import simpleGit from "simple-git";
142
140
  import { existsSync, writeFileSync, unlinkSync, readFileSync } from "fs";
141
+ import { execSync } from "child_process";
143
142
  import { join } from "path";
144
143
  var gitInstance = null;
145
144
  function getGit(cwd) {
146
- if (!gitInstance || cwd) {
147
- gitInstance = simpleGit(cwd);
145
+ if (cwd) return simpleGit(cwd);
146
+ if (!gitInstance) {
147
+ gitInstance = simpleGit();
148
148
  }
149
149
  return gitInstance;
150
150
  }
@@ -227,13 +227,14 @@ async function getMultiCommitStat(hashes) {
227
227
  if (hashes.length === 0) return "";
228
228
  const git = getGit();
229
229
  try {
230
- const stats = [];
231
- for (const hash of hashes) {
232
- const result = await git.raw(["diff-tree", "--stat", "--no-commit-id", "-r", hash]);
233
- if (result.trim()) stats.push(`${hash.substring(0, 7)}:
234
- ${result.trim()}`);
235
- }
236
- return stats.join("\n\n");
230
+ const results = await Promise.all(
231
+ hashes.map(async (hash) => {
232
+ const result = await git.raw(["diff-tree", "--stat", "--no-commit-id", "-r", hash]);
233
+ return result.trim() ? `${hash.substring(0, 7)}:
234
+ ${result.trim()}` : "";
235
+ })
236
+ );
237
+ return results.filter(Boolean).join("\n\n");
237
238
  } catch {
238
239
  return "(\u65E0\u6CD5\u83B7\u53D6 stat \u4FE1\u606F)";
239
240
  }
@@ -454,8 +455,7 @@ async function removeStashGuard() {
454
455
  }
455
456
  function removeStashGuardSync() {
456
457
  try {
457
- const { execSync: execSync2 } = __require("child_process");
458
- const gitDir = String(execSync2("git rev-parse --git-dir", { encoding: "utf-8" })).trim();
458
+ const gitDir = String(execSync("git rev-parse --git-dir", { encoding: "utf-8" })).trim();
459
459
  const guardPath = join(gitDir, STASH_GUARD_FILE);
460
460
  if (existsSync(guardPath)) {
461
461
  unlinkSync(guardPath);
@@ -542,10 +542,12 @@ function useAsync(fn, deps = []) {
542
542
  loading: true,
543
543
  error: null
544
544
  });
545
+ const fnRef = useRef(fn);
546
+ fnRef.current = fn;
545
547
  const load = useCallback(async () => {
546
548
  setState({ data: null, loading: true, error: null });
547
549
  try {
548
- const data = await fn();
550
+ const data = await fnRef.current();
549
551
  setState({ data, loading: false, error: null });
550
552
  } catch (err) {
551
553
  setState({ data: null, loading: false, error: err.message });
@@ -612,20 +614,23 @@ function useCommits(remote2, branch2, pageSize = 100) {
612
614
  function useCommitStat(hashes) {
613
615
  const [stat, setStat] = useState2("");
614
616
  const [loading, setLoading] = useState2(false);
617
+ const hashKey = hashes.join(",");
618
+ const stableHashes = useRef(hashes);
619
+ stableHashes.current = hashes;
615
620
  useEffect2(() => {
616
- if (hashes.length === 0) {
621
+ if (stableHashes.current.length === 0) {
617
622
  setStat("");
618
623
  return;
619
624
  }
620
625
  setLoading(true);
621
- getMultiCommitStat(hashes).then((s) => {
626
+ getMultiCommitStat(stableHashes.current).then((s) => {
622
627
  setStat(s);
623
628
  setLoading(false);
624
629
  }).catch(() => {
625
630
  setStat("(\u83B7\u53D6\u5931\u8D25)");
626
631
  setLoading(false);
627
632
  });
628
- }, [hashes.join(",")]);
633
+ }, [hashKey]);
629
634
  return { stat, loading };
630
635
  }
631
636
 
@@ -1180,6 +1185,8 @@ function BranchCheck({ targetBranch, onContinue, onBack }) {
1180
1185
  const [error2, setError] = useState6(null);
1181
1186
  const [matched, setMatched] = useState6(false);
1182
1187
  const autoCreated = useRef3(false);
1188
+ const onContinueRef = useRef3(onContinue);
1189
+ onContinueRef.current = onContinue;
1183
1190
  useEffect4(() => {
1184
1191
  getCurrentBranch().then((branch2) => {
1185
1192
  setCurrentBranch(branch2);
@@ -1189,7 +1196,7 @@ function BranchCheck({ targetBranch, onContinue, onBack }) {
1189
1196
  });
1190
1197
  }, [targetBranch]);
1191
1198
  useEffect4(() => {
1192
- if (matched) onContinue();
1199
+ if (matched) onContinueRef.current();
1193
1200
  }, [matched]);
1194
1201
  useEffect4(() => {
1195
1202
  if (currentBranch === null || matched || autoCreated.current) return;
@@ -1197,7 +1204,7 @@ function BranchCheck({ targetBranch, onContinue, onBack }) {
1197
1204
  autoCreated.current = true;
1198
1205
  setCreating(true);
1199
1206
  createBranchFrom(targetBranch, currentBranch).then(() => {
1200
- onContinue();
1207
+ onContinueRef.current();
1201
1208
  }).catch((err) => {
1202
1209
  setCreating(false);
1203
1210
  setError(err.message);
@@ -1333,7 +1340,7 @@ function ConfirmPanel({ commits: commits2, selectedHashes, hasMerge, useMainline
1333
1340
  }
1334
1341
 
1335
1342
  // src/components/result-panel.tsx
1336
- import { useState as useState7, useEffect as useEffect5, useCallback as useCallback2, useRef as useRef4 } from "react";
1343
+ import { useState as useState7, useEffect as useEffect5, useCallback as useCallback3, useRef as useRef4 } from "react";
1337
1344
  import { Box as Box9, Text as Text9, useInput as useInput8 } from "ink";
1338
1345
  import { Spinner as Spinner6 } from "@inkjs/ui";
1339
1346
  import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
@@ -1349,7 +1356,7 @@ function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRe
1349
1356
  const remainingRef = useRef4([]);
1350
1357
  const backupBranchRef = useRef4("");
1351
1358
  const orderedHashes = useRef4([...selectedHashes].reverse());
1352
- const tryRestoreStash = useCallback2(async () => {
1359
+ const tryRestoreStash = useCallback3(async () => {
1353
1360
  if (!stashed) return true;
1354
1361
  setPhase("restoring");
1355
1362
  const ok = await stashPop();
@@ -1357,7 +1364,7 @@ function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRe
1357
1364
  if (ok) onStashRestored();
1358
1365
  return ok;
1359
1366
  }, [stashed, onStashRestored]);
1360
- const finishAll = useCallback2(async () => {
1367
+ const finishAll = useCallback3(async () => {
1361
1368
  if (noCommit) {
1362
1369
  const stat = await getStagedStat();
1363
1370
  setStagedStat(stat);
@@ -1368,7 +1375,7 @@ function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRe
1368
1375
  await tryRestoreStash();
1369
1376
  setPhase("done");
1370
1377
  }, [noCommit, tryRestoreStash]);
1371
- const executeFrom = useCallback2(async (startIndex) => {
1378
+ const executeFrom = useCallback3(async (startIndex) => {
1372
1379
  const hashes = orderedHashes.current;
1373
1380
  for (let i = startIndex; i < hashes.length; i++) {
1374
1381
  setCurrentIndex(i);
@@ -1389,7 +1396,7 @@ function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRe
1389
1396
  executeFrom(0);
1390
1397
  });
1391
1398
  }, []);
1392
- const continueRemaining = useCallback2(async () => {
1399
+ const continueRemaining = useCallback3(async () => {
1393
1400
  const remaining = remainingRef.current;
1394
1401
  if (remaining.length === 0) {
1395
1402
  await finishAll();
@@ -1409,7 +1416,7 @@ function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRe
1409
1416
  }
1410
1417
  await finishAll();
1411
1418
  }, [useMainline, noCommit, finishAll]);
1412
- const handleContinue = useCallback2(async () => {
1419
+ const handleContinue = useCallback3(async () => {
1413
1420
  const conflicts = await getConflictFiles();
1414
1421
  if (conflicts.length > 0) {
1415
1422
  setConflictFiles(conflicts);
@@ -1442,14 +1449,14 @@ function ResultPanel({ selectedHashes, useMainline, noCommit, stashed, onStashRe
1442
1449
  await continueRemaining();
1443
1450
  }
1444
1451
  }, [noCommit, continueRemaining]);
1445
- const handleSkip = useCallback2(async () => {
1452
+ const handleSkip = useCallback3(async () => {
1446
1453
  setPhase("continuing");
1447
1454
  setErrorMsg("");
1448
1455
  await skipCherryPick();
1449
1456
  setSkippedCount((c) => c + 1);
1450
1457
  await continueRemaining();
1451
1458
  }, [continueRemaining]);
1452
- const handleAbort = useCallback2(async () => {
1459
+ const handleAbort = useCallback3(async () => {
1453
1460
  setPhase("aborting");
1454
1461
  await abortCherryPick();
1455
1462
  if (backupBranchRef.current) {
@@ -1741,11 +1748,9 @@ function UpdateBanner({ currentVersion }) {
1741
1748
  }
1742
1749
 
1743
1750
  // src/app.tsx
1744
- import { execSync } from "child_process";
1745
- import { createRequire } from "module";
1751
+ import { execSync as execSync2 } from "child_process";
1746
1752
  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");
1753
+ var APP_VERSION = "0.1.9";
1749
1754
  var STEP_NUMBER = {
1750
1755
  checking: 0,
1751
1756
  "stash-recovery": 0,
@@ -1776,7 +1781,7 @@ function App({ initialRemote, initialBranch }) {
1776
1781
  const stashRestoredRef = useRef5(false);
1777
1782
  const mountedRef = useRef5(true);
1778
1783
  const debounceTimer = useRef5(null);
1779
- const setStep = useCallback3((newStep) => {
1784
+ const setStep = useCallback4((newStep) => {
1780
1785
  setInputReady(false);
1781
1786
  setStepRaw(newStep);
1782
1787
  if (debounceTimer.current) clearTimeout(debounceTimer.current);
@@ -1784,10 +1789,10 @@ function App({ initialRemote, initialBranch }) {
1784
1789
  if (mountedRef.current) setInputReady(true);
1785
1790
  }, STEP_DEBOUNCE);
1786
1791
  }, []);
1787
- const restoreStashSync = useCallback3(() => {
1792
+ const restoreStashSync = useCallback4(() => {
1788
1793
  if (stashedRef.current && !stashRestoredRef.current) {
1789
1794
  try {
1790
- execSync("git stash pop", { stdio: "ignore" });
1795
+ execSync2("git stash pop", { stdio: "ignore" });
1791
1796
  stashRestoredRef.current = true;
1792
1797
  removeStashGuardSync();
1793
1798
  } catch {
@@ -1798,7 +1803,7 @@ function App({ initialRemote, initialBranch }) {
1798
1803
  }
1799
1804
  }
1800
1805
  }, []);
1801
- const markStashRestored = useCallback3(() => {
1806
+ const markStashRestored = useCallback4(() => {
1802
1807
  stashRestoredRef.current = true;
1803
1808
  removeStashGuard();
1804
1809
  }, []);
@@ -1857,7 +1862,7 @@ function App({ initialRemote, initialBranch }) {
1857
1862
  const clean = await isWorkingDirClean();
1858
1863
  if (mountedRef.current) setStep(clean ? entryStep : "stash-prompt");
1859
1864
  };
1860
- const goBack = useCallback3((fromStep) => {
1865
+ const goBack = useCallback4((fromStep) => {
1861
1866
  const backMap = {
1862
1867
  branch: "remote",
1863
1868
  "branch-check": "branch",
@@ -1873,7 +1878,7 @@ function App({ initialRemote, initialBranch }) {
1873
1878
  }
1874
1879
  }, [setStep, restoreStashSync, exit]);
1875
1880
  return /* @__PURE__ */ jsxs11(Box11, { flexDirection: "column", children: [
1876
- /* @__PURE__ */ jsx11(AppHeader, { step: STEP_NUMBER[step], stashed, noCommit }),
1881
+ /* @__PURE__ */ jsx11(AppHeader, { step: STEP_NUMBER[step], stashed, noCommit, version: APP_VERSION }),
1877
1882
  step === "checking" && /* @__PURE__ */ jsx11(Spinner7, { label: "\u68C0\u67E5\u5DE5\u4F5C\u533A\u72B6\u6001..." }),
1878
1883
  step === "stash-recovery" && inputReady && /* @__PURE__ */ jsx11(
1879
1884
  StashRecovery,
@@ -1924,9 +1929,11 @@ function App({ initialRemote, initialBranch }) {
1924
1929
  {
1925
1930
  remote: remote2,
1926
1931
  branch: branch2,
1927
- onSelect: (hashes, loadedCommits) => {
1932
+ onSelect: async (hashes, loadedCommits) => {
1928
1933
  setSelectedHashes(hashes);
1929
1934
  setCommits(loadedCommits);
1935
+ const merge = await hasMergeCommits(hashes);
1936
+ setHasMerge(merge);
1930
1937
  setStep("confirm");
1931
1938
  },
1932
1939
  onBack: () => goBack("commits")
@@ -1966,18 +1973,13 @@ function App({ initialRemote, initialBranch }) {
1966
1973
 
1967
1974
  // src/cli-runner.ts
1968
1975
  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");
1976
+ var APP_VERSION2 = "0.1.9";
1972
1977
  function log(msg) {
1973
1978
  process.stdout.write(msg + "\n");
1974
1979
  }
1975
1980
  function error(msg) {
1976
1981
  process.stderr.write(msg + "\n");
1977
1982
  }
1978
- function padEnd(str, len) {
1979
- return str.length >= len ? str : str + " ".repeat(len - str.length);
1980
- }
1981
1983
  async function confirm(message) {
1982
1984
  const rl = createInterface({ input: process.stdin, output: process.stdout });
1983
1985
  return new Promise((resolve) => {
@@ -2039,7 +2041,7 @@ async function validateBranch(remote2, branch2) {
2039
2041
  log(`\u2714 \u5206\u652F '${remote2}/${branch2}'`);
2040
2042
  }
2041
2043
  function formatCommitLine(c) {
2042
- return ` ${c.shortHash} ${padEnd(c.message.slice(0, 60), 62)} ${padEnd(c.author, 16)} ${c.date}`;
2044
+ return ` ${c.shortHash} ${c.message.slice(0, 60).padEnd(62)} ${c.author.padEnd(16)} ${c.date}`;
2043
2045
  }
2044
2046
  async function runList(opts) {
2045
2047
  await validateRemote(opts.remote);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "git-sync-tui",
3
3
  "type": "module",
4
- "version": "0.1.7",
4
+ "version": "0.1.9",
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",