@zjex/git-workflow 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // src/utils.ts
13
- import { execSync } from "child_process";
13
+ import { execSync, spawn } from "child_process";
14
14
  function exec(cmd, silent = false) {
15
15
  try {
16
16
  const options = {
@@ -51,6 +51,37 @@ function getMainBranch() {
51
51
  function divider() {
52
52
  console.log(colors.dim("\u2500".repeat(40)));
53
53
  }
54
+ function execAsync(command, spinner) {
55
+ return new Promise((resolve) => {
56
+ const [cmd, ...args] = command.split(" ");
57
+ const process2 = spawn(cmd, args, {
58
+ stdio: spinner ? "pipe" : "inherit"
59
+ });
60
+ process2.on("close", (code) => {
61
+ resolve(code === 0);
62
+ });
63
+ process2.on("error", () => {
64
+ resolve(false);
65
+ });
66
+ });
67
+ }
68
+ async function execWithSpinner(command, spinner, successMessage, errorMessage) {
69
+ const success = await execAsync(command, spinner);
70
+ if (success) {
71
+ if (successMessage) {
72
+ spinner.succeed(successMessage);
73
+ } else {
74
+ spinner.succeed();
75
+ }
76
+ } else {
77
+ if (errorMessage) {
78
+ spinner.fail(errorMessage);
79
+ } else {
80
+ spinner.fail();
81
+ }
82
+ }
83
+ return success;
84
+ }
54
85
  var colors, TODAY, theme;
55
86
  var init_utils = __esm({
56
87
  "src/utils.ts"() {
@@ -90,7 +121,7 @@ __export(update_notifier_exports, {
90
121
  checkForUpdates: () => checkForUpdates,
91
122
  clearUpdateCache: () => clearUpdateCache
92
123
  });
93
- import { execSync as execSync6 } from "child_process";
124
+ import { execSync as execSync3 } from "child_process";
94
125
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync3, unlinkSync as unlinkSync2 } from "fs";
95
126
  import { homedir as homedir3 } from "os";
96
127
  import { join as join4 } from "path";
@@ -154,7 +185,7 @@ function backgroundCheck(currentVersion, packageName) {
154
185
  }
155
186
  function isUsingVolta() {
156
187
  try {
157
- const whichGw = execSync6("which gw", { encoding: "utf-8" }).trim();
188
+ const whichGw = execSync3("which gw", { encoding: "utf-8" }).trim();
158
189
  return whichGw.includes(".volta");
159
190
  } catch {
160
191
  return false;
@@ -162,7 +193,7 @@ function isUsingVolta() {
162
193
  }
163
194
  async function getLatestVersion(packageName) {
164
195
  try {
165
- const result = execSync6(`npm view ${packageName} version`, {
196
+ const result = execSync3(`npm view ${packageName} version`, {
166
197
  encoding: "utf-8",
167
198
  timeout: 3e3,
168
199
  stdio: ["pipe", "pipe", "ignore"]
@@ -244,7 +275,7 @@ async function performUpdate(packageName) {
244
275
  spinner: "dots"
245
276
  }).start();
246
277
  try {
247
- execSync6(updateCommand, {
278
+ execSync3(updateCommand, {
248
279
  encoding: "utf-8",
249
280
  stdio: ["pipe", "pipe", "pipe"]
250
281
  });
@@ -322,12 +353,11 @@ var init_update_notifier = __esm({
322
353
  // src/index.ts
323
354
  init_utils();
324
355
  import { cac } from "cac";
325
- import { select as select8 } from "@inquirer/prompts";
356
+ import { select as select10 } from "@inquirer/prompts";
326
357
  import { ExitPromptError } from "@inquirer/core";
327
358
 
328
359
  // src/commands/branch.ts
329
360
  init_utils();
330
- import { execSync as execSync2 } from "child_process";
331
361
  import { select, input } from "@inquirer/prompts";
332
362
  import ora from "ora";
333
363
 
@@ -511,12 +541,15 @@ async function createBranch(type, baseBranchArg) {
511
541
  }
512
542
  if (shouldPush) {
513
543
  const pushSpinner = ora("\u6B63\u5728\u63A8\u9001\u5230\u8FDC\u7A0B...").start();
514
- try {
515
- execSync2(`git push -u origin "${branchName}"`, { stdio: "pipe" });
516
- pushSpinner.succeed(`\u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B: origin/${branchName}`);
517
- } catch {
518
- pushSpinner.warn(
519
- "\u8FDC\u7A0B\u63A8\u9001\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push -u origin " + branchName
544
+ const success = await execWithSpinner(
545
+ `git push -u origin "${branchName}"`,
546
+ pushSpinner,
547
+ `\u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B: origin/${branchName}`,
548
+ "\u8FDC\u7A0B\u63A8\u9001\u5931\u8D25"
549
+ );
550
+ if (!success) {
551
+ console.log(
552
+ colors.dim(` \u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push -u origin ${branchName}`)
520
553
  );
521
554
  }
522
555
  }
@@ -573,7 +606,7 @@ async function deleteBranch(branchArg) {
573
606
  }
574
607
  if (branch.startsWith("__remote__")) {
575
608
  const remoteBranch = branch.replace("__remote__", "");
576
- const confirm = await select({
609
+ const confirm3 = await select({
577
610
  message: `\u786E\u8BA4\u5220\u9664\u8FDC\u7A0B\u5206\u652F origin/${remoteBranch}?`,
578
611
  choices: [
579
612
  { name: "\u662F", value: true },
@@ -581,19 +614,17 @@ async function deleteBranch(branchArg) {
581
614
  ],
582
615
  theme
583
616
  });
584
- if (!confirm) {
617
+ if (!confirm3) {
585
618
  console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
586
619
  return;
587
620
  }
588
621
  const spinner = ora(`\u6B63\u5728\u5220\u9664\u8FDC\u7A0B\u5206\u652F: origin/${remoteBranch}`).start();
589
- try {
590
- execSync2(`git push origin --delete "${remoteBranch}"`, {
591
- stdio: "pipe"
592
- });
593
- spinner.succeed(`\u8FDC\u7A0B\u5206\u652F\u5DF2\u5220\u9664: origin/${remoteBranch}`);
594
- } catch {
595
- spinner.fail("\u8FDC\u7A0B\u5206\u652F\u5220\u9664\u5931\u8D25");
596
- }
622
+ await execWithSpinner(
623
+ `git push origin --delete "${remoteBranch}"`,
624
+ spinner,
625
+ `\u8FDC\u7A0B\u5206\u652F\u5DF2\u5220\u9664: origin/${remoteBranch}`,
626
+ "\u8FDC\u7A0B\u5206\u652F\u5220\u9664\u5931\u8D25"
627
+ );
597
628
  return;
598
629
  }
599
630
  }
@@ -618,12 +649,12 @@ async function deleteBranch(branchArg) {
618
649
  });
619
650
  if (deleteRemote) {
620
651
  const spinner = ora(`\u6B63\u5728\u5220\u9664\u8FDC\u7A0B\u5206\u652F: origin/${branch}`).start();
621
- try {
622
- execSync2(`git push origin --delete "${branch}"`, { stdio: "pipe" });
623
- spinner.succeed(`\u8FDC\u7A0B\u5206\u652F\u5DF2\u5220\u9664: origin/${branch}`);
624
- } catch {
625
- spinner.fail("\u8FDC\u7A0B\u5206\u652F\u5220\u9664\u5931\u8D25");
626
- }
652
+ await execWithSpinner(
653
+ `git push origin --delete "${branch}"`,
654
+ spinner,
655
+ `\u8FDC\u7A0B\u5206\u652F\u5DF2\u5220\u9664: origin/${branch}`,
656
+ "\u8FDC\u7A0B\u5206\u652F\u5220\u9664\u5931\u8D25"
657
+ );
627
658
  }
628
659
  } else {
629
660
  console.log(colors.red(`\u5206\u652F\u4E0D\u5B58\u5728: ${branch}`));
@@ -643,27 +674,28 @@ async function deleteBranch(branchArg) {
643
674
  return;
644
675
  }
645
676
  const localSpinner = ora(`\u6B63\u5728\u5220\u9664\u672C\u5730\u5206\u652F: ${branch}`).start();
646
- try {
647
- execSync2(`git branch -D "${branch}"`, { stdio: "pipe" });
648
- localSpinner.succeed(`\u672C\u5730\u5206\u652F\u5DF2\u5220\u9664: ${branch}`);
649
- } catch {
650
- localSpinner.fail("\u672C\u5730\u5206\u652F\u5220\u9664\u5931\u8D25");
677
+ const localSuccess = await execWithSpinner(
678
+ `git branch -D "${branch}"`,
679
+ localSpinner,
680
+ `\u672C\u5730\u5206\u652F\u5DF2\u5220\u9664: ${branch}`,
681
+ "\u672C\u5730\u5206\u652F\u5220\u9664\u5931\u8D25"
682
+ );
683
+ if (!localSuccess) {
651
684
  return;
652
685
  }
653
686
  if (hasRemote) {
654
687
  const remoteSpinner = ora(`\u6B63\u5728\u5220\u9664\u8FDC\u7A0B\u5206\u652F: origin/${branch}`).start();
655
- try {
656
- execSync2(`git push origin --delete "${branch}"`, { stdio: "pipe" });
657
- remoteSpinner.succeed(`\u8FDC\u7A0B\u5206\u652F\u5DF2\u5220\u9664: origin/${branch}`);
658
- } catch {
659
- remoteSpinner.fail("\u8FDC\u7A0B\u5206\u652F\u5220\u9664\u5931\u8D25");
660
- }
688
+ await execWithSpinner(
689
+ `git push origin --delete "${branch}"`,
690
+ remoteSpinner,
691
+ `\u8FDC\u7A0B\u5206\u652F\u5DF2\u5220\u9664: origin/${branch}`,
692
+ "\u8FDC\u7A0B\u5206\u652F\u5220\u9664\u5931\u8D25"
693
+ );
661
694
  }
662
695
  }
663
696
 
664
697
  // src/commands/tag.ts
665
698
  init_utils();
666
- import { execSync as execSync3 } from "child_process";
667
699
  import { select as select2, input as input2 } from "@inquirer/prompts";
668
700
  import ora2 from "ora";
669
701
  async function listTags(prefix) {
@@ -970,28 +1002,29 @@ async function createTag(inputPrefix) {
970
1002
  console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
971
1003
  return;
972
1004
  }
973
- doCreateTag(nextTag);
1005
+ await doCreateTag(nextTag);
974
1006
  }
975
- function doCreateTag(tagName) {
1007
+ async function doCreateTag(tagName) {
976
1008
  divider();
977
1009
  const spinner = ora2(`\u6B63\u5728\u521B\u5EFA tag: ${tagName}`).start();
978
- try {
979
- execSync3(`git tag -a "${tagName}" -m "Release ${tagName}"`, {
980
- stdio: "pipe"
981
- });
982
- spinner.succeed(`Tag \u521B\u5EFA\u6210\u529F: ${tagName}`);
983
- } catch {
984
- spinner.fail("tag \u521B\u5EFA\u5931\u8D25");
1010
+ const success = await execWithSpinner(
1011
+ `git tag -a "${tagName}" -m "Release ${tagName}"`,
1012
+ spinner,
1013
+ `Tag \u521B\u5EFA\u6210\u529F: ${tagName}`,
1014
+ "tag \u521B\u5EFA\u5931\u8D25"
1015
+ );
1016
+ if (!success) {
985
1017
  return;
986
1018
  }
987
1019
  const pushSpinner = ora2("\u6B63\u5728\u63A8\u9001\u5230\u8FDC\u7A0B...").start();
988
- try {
989
- execSync3(`git push origin "${tagName}"`, { stdio: "pipe" });
990
- pushSpinner.succeed(`Tag \u5DF2\u63A8\u9001: ${tagName}`);
991
- } catch {
992
- pushSpinner.warn(
993
- `\u8FDC\u7A0B\u63A8\u9001\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push origin ${tagName}`
994
- );
1020
+ const pushSuccess = await execWithSpinner(
1021
+ `git push origin "${tagName}"`,
1022
+ pushSpinner,
1023
+ `Tag \u5DF2\u63A8\u9001: ${tagName}`,
1024
+ "\u8FDC\u7A0B\u63A8\u9001\u5931\u8D25"
1025
+ );
1026
+ if (!pushSuccess) {
1027
+ console.log(colors.dim(` \u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push origin ${tagName}`));
995
1028
  }
996
1029
  }
997
1030
  async function deleteTag() {
@@ -1015,7 +1048,7 @@ async function deleteTag() {
1015
1048
  console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
1016
1049
  return;
1017
1050
  }
1018
- const confirm = await select2({
1051
+ const confirm3 = await select2({
1019
1052
  message: `\u786E\u8BA4\u5220\u9664 tag: ${colors.red(tagToDelete)}?`,
1020
1053
  choices: [
1021
1054
  { name: "\u662F", value: true },
@@ -1023,17 +1056,19 @@ async function deleteTag() {
1023
1056
  ],
1024
1057
  theme
1025
1058
  });
1026
- if (!confirm) {
1059
+ if (!confirm3) {
1027
1060
  console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
1028
1061
  return;
1029
1062
  }
1030
1063
  divider();
1031
1064
  const spinner = ora2(`\u6B63\u5728\u5220\u9664\u672C\u5730 tag: ${tagToDelete}`).start();
1032
- try {
1033
- execSync3(`git tag -d "${tagToDelete}"`, { stdio: "pipe" });
1034
- spinner.succeed(`\u672C\u5730 tag \u5DF2\u5220\u9664: ${tagToDelete}`);
1035
- } catch {
1036
- spinner.fail("\u672C\u5730 tag \u5220\u9664\u5931\u8D25");
1065
+ const localSuccess = await execWithSpinner(
1066
+ `git tag -d "${tagToDelete}"`,
1067
+ spinner,
1068
+ `\u672C\u5730 tag \u5DF2\u5220\u9664: ${tagToDelete}`,
1069
+ "\u672C\u5730 tag \u5220\u9664\u5931\u8D25"
1070
+ );
1071
+ if (!localSuccess) {
1037
1072
  return;
1038
1073
  }
1039
1074
  const deleteRemote = await select2({
@@ -1046,10 +1081,12 @@ async function deleteTag() {
1046
1081
  });
1047
1082
  if (deleteRemote) {
1048
1083
  const pushSpinner = ora2("\u6B63\u5728\u5220\u9664\u8FDC\u7A0B tag...").start();
1049
- try {
1050
- execSync3(`git push origin --delete "${tagToDelete}"`, { stdio: "pipe" });
1051
- pushSpinner.succeed(`\u8FDC\u7A0B tag \u5DF2\u5220\u9664: ${tagToDelete}`);
1052
- } catch {
1084
+ const remoteSuccess = await execWithSpinner(
1085
+ `git push origin --delete "${tagToDelete}"`,
1086
+ pushSpinner,
1087
+ `\u8FDC\u7A0B tag \u5DF2\u5220\u9664: ${tagToDelete}`
1088
+ );
1089
+ if (!remoteSuccess) {
1053
1090
  pushSpinner.warn(
1054
1091
  `\u8FDC\u7A0B\u5220\u9664\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push origin --delete ${tagToDelete}`
1055
1092
  );
@@ -1096,25 +1133,32 @@ async function updateTag() {
1096
1133
  }
1097
1134
  divider();
1098
1135
  const spinner = ora2(`\u6B63\u5728\u91CD\u547D\u540D tag: ${oldTag} \u2192 ${newTag}`).start();
1099
- try {
1100
- const commit2 = execOutput(`git rev-list -n 1 "${oldTag}"`).trim();
1101
- const message = execOutput(
1102
- `git tag -l --format='%(contents)' "${oldTag}"`
1103
- ).trim();
1104
- if (message) {
1105
- execSync3(`git tag -a "${newTag}" "${commit2}" -m "${message}"`, {
1106
- stdio: "pipe"
1107
- });
1108
- } else {
1109
- execSync3(`git tag "${newTag}" "${commit2}"`, { stdio: "pipe" });
1110
- }
1111
- execSync3(`git tag -d "${oldTag}"`, { stdio: "pipe" });
1112
- spinner.succeed(`Tag \u5DF2\u91CD\u547D\u540D: ${oldTag} \u2192 ${newTag}`);
1113
- } catch (error) {
1136
+ const commit2 = execOutput(`git rev-list -n 1 "${oldTag}"`).trim();
1137
+ const message = execOutput(
1138
+ `git tag -l --format='%(contents)' "${oldTag}"`
1139
+ ).trim();
1140
+ let createSuccess;
1141
+ if (message) {
1142
+ createSuccess = await execWithSpinner(
1143
+ `git tag -a "${newTag}" "${commit2}" -m "${message}"`,
1144
+ spinner
1145
+ );
1146
+ } else {
1147
+ createSuccess = await execWithSpinner(
1148
+ `git tag "${newTag}" "${commit2}"`,
1149
+ spinner
1150
+ );
1151
+ }
1152
+ if (!createSuccess) {
1114
1153
  spinner.fail("tag \u91CD\u547D\u540D\u5931\u8D25");
1115
- console.log(colors.red(String(error)));
1116
1154
  return;
1117
1155
  }
1156
+ const deleteSuccess = await execAsync(`git tag -d "${oldTag}"`, spinner);
1157
+ if (!deleteSuccess) {
1158
+ spinner.fail("\u5220\u9664\u65E7 tag \u5931\u8D25");
1159
+ return;
1160
+ }
1161
+ spinner.succeed(`Tag \u5DF2\u91CD\u547D\u540D: ${oldTag} \u2192 ${newTag}`);
1118
1162
  const pushRemote = await select2({
1119
1163
  message: "\u662F\u5426\u540C\u6B65\u5230\u8FDC\u7A0B?",
1120
1164
  choices: [
@@ -1125,17 +1169,29 @@ async function updateTag() {
1125
1169
  });
1126
1170
  if (pushRemote) {
1127
1171
  const pushSpinner = ora2("\u6B63\u5728\u540C\u6B65\u5230\u8FDC\u7A0B...").start();
1128
- try {
1129
- execSync3(`git push origin "${newTag}"`, { stdio: "pipe" });
1130
- execSync3(`git push origin --delete "${oldTag}"`, { stdio: "pipe" });
1131
- pushSpinner.succeed(`\u8FDC\u7A0B tag \u5DF2\u540C\u6B65: ${oldTag} \u2192 ${newTag}`);
1132
- } catch {
1172
+ const pushNewSuccess = await execAsync(
1173
+ `git push origin "${newTag}"`,
1174
+ pushSpinner
1175
+ );
1176
+ if (!pushNewSuccess) {
1133
1177
  pushSpinner.warn(
1134
1178
  `\u8FDC\u7A0B\u540C\u6B65\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C:
1135
1179
  git push origin ${newTag}
1136
1180
  git push origin --delete ${oldTag}`
1137
1181
  );
1182
+ return;
1183
+ }
1184
+ const deleteOldSuccess = await execAsync(
1185
+ `git push origin --delete "${oldTag}"`,
1186
+ pushSpinner
1187
+ );
1188
+ if (!deleteOldSuccess) {
1189
+ pushSpinner.warn(
1190
+ `\u8FDC\u7A0B\u65E7 tag \u5220\u9664\u5931\u8D25\uFF0C\u53EF\u7A0D\u540E\u624B\u52A8\u6267\u884C: git push origin --delete ${oldTag}`
1191
+ );
1192
+ return;
1138
1193
  }
1194
+ pushSpinner.succeed(`\u8FDC\u7A0B tag \u5DF2\u540C\u6B65: ${oldTag} \u2192 ${newTag}`);
1139
1195
  }
1140
1196
  }
1141
1197
  async function cleanInvalidTags() {
@@ -1185,10 +1241,10 @@ async function cleanInvalidTags() {
1185
1241
  let localSuccess = 0;
1186
1242
  let localFailed = 0;
1187
1243
  for (const tag of invalidTags) {
1188
- try {
1189
- execSync3(`git tag -d "${tag}"`, { stdio: "pipe" });
1244
+ const success = await execAsync(`git tag -d "${tag}"`, localSpinner);
1245
+ if (success) {
1190
1246
  localSuccess++;
1191
- } catch {
1247
+ } else {
1192
1248
  localFailed++;
1193
1249
  }
1194
1250
  }
@@ -1212,10 +1268,13 @@ async function cleanInvalidTags() {
1212
1268
  let remoteSuccess = 0;
1213
1269
  let remoteFailed = 0;
1214
1270
  for (const tag of invalidTags) {
1215
- try {
1216
- execSync3(`git push origin --delete "${tag}"`, { stdio: "pipe" });
1271
+ const success = await execAsync(
1272
+ `git push origin --delete "${tag}"`,
1273
+ remoteSpinner
1274
+ );
1275
+ if (success) {
1217
1276
  remoteSuccess++;
1218
- } catch {
1277
+ } else {
1219
1278
  remoteFailed++;
1220
1279
  }
1221
1280
  }
@@ -1675,7 +1734,7 @@ async function init() {
1675
1734
 
1676
1735
  // src/commands/stash.ts
1677
1736
  init_utils();
1678
- import { execSync as execSync4, spawn } from "child_process";
1737
+ import { spawn as spawn2 } from "child_process";
1679
1738
  import { select as select5, input as input4 } from "@inquirer/prompts";
1680
1739
  import ora3 from "ora";
1681
1740
  import boxen from "boxen";
@@ -1779,10 +1838,10 @@ async function showStashActions(entry) {
1779
1838
  });
1780
1839
  switch (action) {
1781
1840
  case "apply":
1782
- applyStash(entry.index, false);
1841
+ await applyStash(entry.index, false);
1783
1842
  break;
1784
1843
  case "pop":
1785
- applyStash(entry.index, true);
1844
+ await applyStash(entry.index, true);
1786
1845
  break;
1787
1846
  case "branch":
1788
1847
  await createBranchFromStash(entry.index);
@@ -1822,25 +1881,29 @@ async function createStash() {
1822
1881
  theme
1823
1882
  });
1824
1883
  const spinner = ora3("\u521B\u5EFA stash...").start();
1825
- try {
1826
- let cmd = "git stash push";
1827
- if (includeUntracked) cmd += " -u";
1828
- if (message) cmd += ` -m "${message.replace(/"/g, '\\"')}"`;
1829
- execSync4(cmd, { stdio: "pipe" });
1830
- spinner.succeed("Stash \u521B\u5EFA\u6210\u529F");
1884
+ let cmd = "git stash push";
1885
+ if (includeUntracked) cmd += " -u";
1886
+ if (message) cmd += ` -m "${message.replace(/"/g, '\\"')}"`;
1887
+ const success = await execWithSpinner(
1888
+ cmd,
1889
+ spinner,
1890
+ "Stash \u521B\u5EFA\u6210\u529F",
1891
+ "Stash \u521B\u5EFA\u5931\u8D25"
1892
+ );
1893
+ if (success) {
1831
1894
  await stash();
1832
- } catch {
1833
- spinner.fail("Stash \u521B\u5EFA\u5931\u8D25");
1834
1895
  }
1835
1896
  }
1836
- function applyStash(index, pop) {
1897
+ async function applyStash(index, pop) {
1837
1898
  const action = pop ? "pop" : "apply";
1838
1899
  const spinner = ora3(`${pop ? "\u5F39\u51FA" : "\u5E94\u7528"} stash...`).start();
1839
- try {
1840
- execSync4(`git stash ${action} stash@{${index}}`, { stdio: "pipe" });
1841
- spinner.succeed(`Stash ${pop ? "\u5DF2\u5F39\u51FA" : "\u5DF2\u5E94\u7528"}`);
1842
- } catch {
1843
- spinner.fail("\u64CD\u4F5C\u5931\u8D25\uFF0C\u53EF\u80FD\u5B58\u5728\u51B2\u7A81");
1900
+ const success = await execWithSpinner(
1901
+ `git stash ${action} stash@{${index}}`,
1902
+ spinner,
1903
+ `Stash ${pop ? "\u5DF2\u5F39\u51FA" : "\u5DF2\u5E94\u7528"}`,
1904
+ "\u64CD\u4F5C\u5931\u8D25\uFF0C\u53EF\u80FD\u5B58\u5728\u51B2\u7A81"
1905
+ );
1906
+ if (!success) {
1844
1907
  const status = execOutput("git status --porcelain");
1845
1908
  if (status.includes("UU") || status.includes("AA")) {
1846
1909
  console.log(colors.yellow("\n\u5B58\u5728\u51B2\u7A81\uFF0C\u8BF7\u624B\u52A8\u89E3\u51B3\u540E\u63D0\u4EA4"));
@@ -1938,7 +2001,7 @@ function startPager(content) {
1938
2001
  return new Promise((resolve) => {
1939
2002
  const pager = process.env.PAGER || "less";
1940
2003
  try {
1941
- const pagerProcess = spawn(pager, ["-R", "-S", "-F", "-X", "-i"], {
2004
+ const pagerProcess = spawn2(pager, ["-R", "-S", "-F", "-X", "-i"], {
1942
2005
  stdio: ["pipe", "inherit", "inherit"],
1943
2006
  env: { ...process.env, LESS: "-R -S -F -X -i" }
1944
2007
  });
@@ -1979,14 +2042,12 @@ async function createBranchFromStash(index) {
1979
2042
  const branchName = await getBranchName(type);
1980
2043
  if (!branchName) return;
1981
2044
  const spinner = ora3(`\u521B\u5EFA\u5206\u652F ${branchName}...`).start();
1982
- try {
1983
- execSync4(`git stash branch "${branchName}" stash@{${index}}`, {
1984
- stdio: "pipe"
1985
- });
1986
- spinner.succeed(`\u5206\u652F\u5DF2\u521B\u5EFA: ${branchName} (stash \u5DF2\u81EA\u52A8\u5F39\u51FA)`);
1987
- } catch {
1988
- spinner.fail("\u521B\u5EFA\u5206\u652F\u5931\u8D25");
1989
- }
2045
+ await execWithSpinner(
2046
+ `git stash branch "${branchName}" stash@{${index}}`,
2047
+ spinner,
2048
+ `\u5206\u652F\u5DF2\u521B\u5EFA: ${branchName} (stash \u5DF2\u81EA\u52A8\u5F39\u51FA)`,
2049
+ "\u521B\u5EFA\u5206\u652F\u5931\u8D25"
2050
+ );
1990
2051
  }
1991
2052
  async function dropStash(index) {
1992
2053
  const confirmed = await select5({
@@ -2002,17 +2063,17 @@ async function dropStash(index) {
2002
2063
  return;
2003
2064
  }
2004
2065
  const spinner = ora3("\u5220\u9664 stash...").start();
2005
- try {
2006
- execSync4(`git stash drop stash@{${index}}`, { stdio: "pipe" });
2007
- spinner.succeed("Stash \u5DF2\u5220\u9664");
2008
- } catch {
2009
- spinner.fail("\u5220\u9664\u5931\u8D25");
2010
- }
2066
+ await execWithSpinner(
2067
+ `git stash drop stash@{${index}}`,
2068
+ spinner,
2069
+ "Stash \u5DF2\u5220\u9664",
2070
+ "\u5220\u9664\u5931\u8D25"
2071
+ );
2011
2072
  }
2012
2073
 
2013
2074
  // src/commands/commit.ts
2014
2075
  init_utils();
2015
- import { execSync as execSync5 } from "child_process";
2076
+ import { execSync as execSync2 } from "child_process";
2016
2077
  import { writeFileSync as writeFileSync3, unlinkSync } from "fs";
2017
2078
  import { tmpdir } from "os";
2018
2079
  import { join as join3 } from "path";
@@ -2430,7 +2491,7 @@ async function commit() {
2430
2491
  const config2 = getConfig();
2431
2492
  const autoStage = config2.autoStage ?? true;
2432
2493
  if (autoStage) {
2433
- execSync5("git add -A", { stdio: "pipe" });
2494
+ execSync2("git add -A", { stdio: "pipe" });
2434
2495
  }
2435
2496
  let { staged, unstaged } = parseGitStatus();
2436
2497
  if (staged.length === 0 && unstaged.length > 0 && !autoStage) {
@@ -2455,7 +2516,7 @@ async function commit() {
2455
2516
  return;
2456
2517
  }
2457
2518
  for (const file of filesToStage) {
2458
- execSync5(`git add "${file}"`, { stdio: "pipe" });
2519
+ execSync2(`git add "${file}"`, { stdio: "pipe" });
2459
2520
  }
2460
2521
  console.log(colors.green(`\u2714 \u5DF2\u6682\u5B58 ${filesToStage.length} \u4E2A\u6587\u4EF6`));
2461
2522
  divider();
@@ -2547,7 +2608,7 @@ async function commit() {
2547
2608
  const spinner = ora4("\u6B63\u5728\u63D0\u4EA4...").start();
2548
2609
  try {
2549
2610
  if (autoStage) {
2550
- execSync5("git add -A", { stdio: "pipe" });
2611
+ execSync2("git add -A", { stdio: "pipe" });
2551
2612
  }
2552
2613
  const finalStatus = parseGitStatus();
2553
2614
  if (finalStatus.staged.length === 0) {
@@ -2563,7 +2624,7 @@ async function commit() {
2563
2624
  const tmpFile = join3(tmpdir(), `.gw-commit-msg-${Date.now()}`);
2564
2625
  try {
2565
2626
  writeFileSync3(tmpFile, message, "utf-8");
2566
- execSync5(`git commit -F "${tmpFile}"`, {
2627
+ execSync2(`git commit -F "${tmpFile}"`, {
2567
2628
  stdio: ["pipe", "pipe", "pipe"]
2568
2629
  });
2569
2630
  } finally {
@@ -2674,7 +2735,7 @@ init_update_notifier();
2674
2735
 
2675
2736
  // src/commands/update.ts
2676
2737
  init_utils();
2677
- import { execSync as execSync7 } from "child_process";
2738
+ import { execSync as execSync4, spawn as spawn3 } from "child_process";
2678
2739
  import ora6 from "ora";
2679
2740
  import boxen3 from "boxen";
2680
2741
  import semver2 from "semver";
@@ -2693,7 +2754,7 @@ function clearUpdateCache2() {
2693
2754
  }
2694
2755
  async function getLatestVersion2(packageName) {
2695
2756
  try {
2696
- const result = execSync7(`npm view ${packageName} version`, {
2757
+ const result = execSync4(`npm view ${packageName} version`, {
2697
2758
  encoding: "utf-8",
2698
2759
  timeout: 3e3,
2699
2760
  stdio: ["pipe", "pipe", "ignore"]
@@ -2705,7 +2766,7 @@ async function getLatestVersion2(packageName) {
2705
2766
  }
2706
2767
  function isUsingVolta2() {
2707
2768
  try {
2708
- const whichGw = execSync7("which gw", { encoding: "utf-8" }).trim();
2769
+ const whichGw = execSync4("which gw", { encoding: "utf-8" }).trim();
2709
2770
  return whichGw.includes(".volta");
2710
2771
  } catch {
2711
2772
  return false;
@@ -2765,44 +2826,73 @@ async function update(currentVersion) {
2765
2826
  }
2766
2827
  )
2767
2828
  );
2768
- const updateSpinner = ora6("\u6B63\u5728\u66F4\u65B0...").start();
2829
+ console.log("");
2830
+ console.log(colors.cyan("\u{1F4E6} \u5F00\u59CB\u5B89\u88C5\u65B0\u7248\u672C..."));
2831
+ console.log("");
2769
2832
  const updateCommand = usingVolta ? `volta install ${packageName}@latest` : `npm install -g ${packageName}@latest`;
2770
- execSync7(updateCommand, {
2771
- encoding: "utf-8",
2772
- stdio: ["pipe", "pipe", "pipe"]
2833
+ const [command, ...args] = updateCommand.split(" ");
2834
+ const updateProcess = spawn3(command, args, {
2835
+ stdio: "inherit"
2836
+ // 继承父进程的 stdio,显示实时输出
2837
+ });
2838
+ updateProcess.on("close", (code) => {
2839
+ console.log("");
2840
+ if (code === 0) {
2841
+ console.log(colors.green("\u2714 \u66F4\u65B0\u6210\u529F\uFF01"));
2842
+ clearUpdateCache2();
2843
+ console.log("");
2844
+ console.log(
2845
+ boxen3(
2846
+ [
2847
+ colors.green(colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01")),
2848
+ "",
2849
+ `\u65B0\u7248\u672C: ${colors.green(colors.bold(latestVersion))}`,
2850
+ "",
2851
+ colors.dim("\u8BF7\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u9A8C\u8BC1:"),
2852
+ colors.cyan(" hash -r && gw --version"),
2853
+ "",
2854
+ colors.dim("\u6216\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF")
2855
+ ].join("\n"),
2856
+ {
2857
+ padding: { top: 1, bottom: 1, left: 2, right: 2 },
2858
+ margin: { top: 0, bottom: 1, left: 2, right: 2 },
2859
+ borderStyle: "round",
2860
+ borderColor: "green",
2861
+ align: "left",
2862
+ width: 40
2863
+ }
2864
+ )
2865
+ );
2866
+ process.exit(0);
2867
+ } else {
2868
+ console.log(colors.red("\u2716 \u66F4\u65B0\u5931\u8D25"));
2869
+ console.log("");
2870
+ console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
2871
+ console.log(colors.cyan(` ${updateCommand}`));
2872
+ console.log("");
2873
+ process.exit(1);
2874
+ }
2875
+ });
2876
+ updateProcess.on("error", (error) => {
2877
+ console.log("");
2878
+ console.log(colors.red("\u2716 \u66F4\u65B0\u5931\u8D25"));
2879
+ console.log("");
2880
+ console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
2881
+ console.log(colors.cyan(` ${updateCommand}`));
2882
+ console.log("");
2883
+ console.log(colors.dim(` \u9519\u8BEF\u4FE1\u606F: ${error.message}`));
2884
+ console.log("");
2885
+ process.exit(1);
2773
2886
  });
2774
- updateSpinner.succeed(colors.green("\u66F4\u65B0\u6210\u529F\uFF01"));
2775
- clearUpdateCache2();
2776
- console.log("");
2777
- console.log(
2778
- boxen3(
2779
- [
2780
- colors.green(colors.bold("\u2728 \u66F4\u65B0\u5B8C\u6210\uFF01")),
2781
- "",
2782
- `\u65B0\u7248\u672C: ${colors.green(colors.bold(latestVersion))}`,
2783
- "",
2784
- colors.dim("\u8BF7\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4\u9A8C\u8BC1:"),
2785
- colors.cyan(" hash -r && gw --version"),
2786
- "",
2787
- colors.dim("\u6216\u91CD\u65B0\u6253\u5F00\u7EC8\u7AEF")
2788
- ].join("\n"),
2789
- {
2790
- padding: { top: 1, bottom: 1, left: 2, right: 2 },
2791
- margin: { top: 0, bottom: 1, left: 2, right: 2 },
2792
- borderStyle: "round",
2793
- borderColor: "green",
2794
- align: "left",
2795
- width: 40
2796
- }
2797
- )
2798
- );
2799
2887
  process.exit(0);
2800
2888
  } catch (error) {
2801
- spinner.fail(colors.red("\u66F4\u65B0\u5931\u8D25"));
2889
+ spinner.fail(colors.red("\u83B7\u53D6\u7248\u672C\u4FE1\u606F\u5931\u8D25"));
2802
2890
  console.log("");
2803
- console.log(colors.dim(" \u4F60\u53EF\u4EE5\u624B\u52A8\u8FD0\u884C\u4EE5\u4E0B\u547D\u4EE4\u66F4\u65B0:"));
2804
- const updateCommand = usingVolta ? `volta install ${packageName}@latest` : `npm install -g ${packageName}@latest`;
2805
- console.log(colors.cyan(` ${updateCommand}`));
2891
+ console.log(colors.dim(" \u8BF7\u68C0\u67E5\u7F51\u7EDC\u8FDE\u63A5\u540E\u91CD\u8BD5"));
2892
+ console.log("");
2893
+ if (error instanceof Error) {
2894
+ console.log(colors.dim(` \u9519\u8BEF\u4FE1\u606F: ${error.message}`));
2895
+ }
2806
2896
  console.log("");
2807
2897
  process.exit(1);
2808
2898
  }
@@ -2810,9 +2900,9 @@ async function update(currentVersion) {
2810
2900
 
2811
2901
  // src/commands/log.ts
2812
2902
  init_utils();
2813
- import { execSync as execSync8 } from "child_process";
2903
+ import { execSync as execSync5 } from "child_process";
2814
2904
  import boxen4 from "boxen";
2815
- import { spawn as spawn2 } from "child_process";
2905
+ import { spawn as spawn4 } from "child_process";
2816
2906
  function parseGitLog(output) {
2817
2907
  const commits = [];
2818
2908
  const lines = output.trim().split("\n");
@@ -3030,7 +3120,7 @@ function formatTimelineStyle(commits) {
3030
3120
  function startInteractivePager(content) {
3031
3121
  const pager = process.env.PAGER || "less";
3032
3122
  try {
3033
- const pagerProcess = spawn2(pager, ["-R", "-S", "-F", "-X", "-i"], {
3123
+ const pagerProcess = spawn4(pager, ["-R", "-S", "-F", "-X", "-i"], {
3034
3124
  stdio: ["pipe", "inherit", "inherit"],
3035
3125
  env: { ...process.env, LESS: "-R -S -F -X -i" }
3036
3126
  });
@@ -3060,7 +3150,7 @@ function executeTimelineLog(options) {
3060
3150
  if (options.until) cmd += ` --until="${options.until}"`;
3061
3151
  if (options.grep) cmd += ` --grep="${options.grep}"`;
3062
3152
  if (options.all) cmd += ` --all`;
3063
- const output = execSync8(cmd, {
3153
+ const output = execSync5(cmd, {
3064
3154
  encoding: "utf8",
3065
3155
  stdio: "pipe",
3066
3156
  maxBuffer: 1024 * 1024 * 10
@@ -3118,6 +3208,292 @@ async function log(options = {}) {
3118
3208
  executeTimelineLog(options);
3119
3209
  }
3120
3210
 
3211
+ // src/commands/amend-date.ts
3212
+ init_utils();
3213
+ import { execSync as execSync6 } from "child_process";
3214
+ import { select as select8, input as input6, confirm } from "@inquirer/prompts";
3215
+ function formatGitDate(date) {
3216
+ const year = date.getFullYear();
3217
+ const month = String(date.getMonth() + 1).padStart(2, "0");
3218
+ const day = String(date.getDate()).padStart(2, "0");
3219
+ const hours = String(date.getHours()).padStart(2, "0");
3220
+ const minutes = String(date.getMinutes()).padStart(2, "0");
3221
+ const seconds = String(date.getSeconds()).padStart(2, "0");
3222
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
3223
+ }
3224
+ function parseDate(input8) {
3225
+ const trimmed = input8.trim();
3226
+ const dateMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
3227
+ if (dateMatch) {
3228
+ const [, year, month, day] = dateMatch;
3229
+ return new Date(
3230
+ parseInt(year),
3231
+ parseInt(month) - 1,
3232
+ parseInt(day),
3233
+ 0,
3234
+ 0,
3235
+ 0
3236
+ );
3237
+ }
3238
+ return null;
3239
+ }
3240
+ function getRecentCommits(limit = 20) {
3241
+ const output = execOutput(`git log -${limit} --pretty=format:"%H|%s|%ai"`);
3242
+ if (!output) return [];
3243
+ return output.split("\n").map((line) => {
3244
+ const [hash, message, date] = line.split("|");
3245
+ return { hash, message, date };
3246
+ });
3247
+ }
3248
+ function getCommitByHash(hash) {
3249
+ try {
3250
+ const output = execOutput(`git log -1 ${hash} --pretty=format:"%H|%s|%ai"`);
3251
+ if (!output) return null;
3252
+ const [fullHash, message, date] = output.split("|");
3253
+ return { hash: fullHash, message, date };
3254
+ } catch {
3255
+ return null;
3256
+ }
3257
+ }
3258
+ async function amendDate(commitHash) {
3259
+ console.log(colors.cyan("\u4FEE\u6539 Commit \u63D0\u4EA4\u65F6\u95F4"));
3260
+ divider();
3261
+ let selectedCommit;
3262
+ if (commitHash) {
3263
+ const commit2 = getCommitByHash(commitHash);
3264
+ if (!commit2) {
3265
+ console.log(colors.red(`\u2716 \u627E\u4E0D\u5230 commit: ${commitHash}`));
3266
+ return;
3267
+ }
3268
+ selectedCommit = commit2;
3269
+ } else {
3270
+ const commits = getRecentCommits(20);
3271
+ if (commits.length === 0) {
3272
+ console.log(colors.yellow("\u6CA1\u6709\u627E\u5230\u4EFB\u4F55 commit"));
3273
+ return;
3274
+ }
3275
+ selectedCommit = await select8({
3276
+ message: "\u9009\u62E9\u8981\u4FEE\u6539\u65F6\u95F4\u7684 commit:",
3277
+ choices: commits.map((c) => ({
3278
+ name: `${colors.yellow(c.hash.slice(0, 7))} ${c.message.slice(0, 60)} ${colors.dim(c.date)}`,
3279
+ value: c,
3280
+ description: c.date
3281
+ })),
3282
+ pageSize: 15,
3283
+ theme
3284
+ });
3285
+ }
3286
+ console.log("");
3287
+ console.log("\u5F53\u524D commit \u4FE1\u606F:");
3288
+ console.log(` Hash: ${colors.yellow(selectedCommit.hash.slice(0, 7))}`);
3289
+ console.log(` Message: ${selectedCommit.message}`);
3290
+ console.log(` Date: ${colors.cyan(selectedCommit.date)}`);
3291
+ divider();
3292
+ console.log(colors.dim("\u8F93\u5165\u65E5\u671F\u683C\u5F0F: YYYY-MM-DD (\u5982: 2026-01-19)"));
3293
+ console.log("");
3294
+ const dateInput = await input6({
3295
+ message: "\u8F93\u5165\u65B0\u7684\u65E5\u671F:",
3296
+ validate: (value) => {
3297
+ const parsed = parseDate(value);
3298
+ if (!parsed) {
3299
+ return "\u65E5\u671F\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u8BF7\u4F7F\u7528 YYYY-MM-DD \u683C\u5F0F";
3300
+ }
3301
+ return true;
3302
+ },
3303
+ theme
3304
+ });
3305
+ const newDate = parseDate(dateInput);
3306
+ const formattedDate = formatGitDate(newDate);
3307
+ divider();
3308
+ console.log("\u4FEE\u6539\u9884\u89C8:");
3309
+ console.log(` Commit: ${colors.yellow(selectedCommit.hash.slice(0, 7))}`);
3310
+ console.log(` \u65E7\u65F6\u95F4: ${colors.dim(selectedCommit.date)}`);
3311
+ console.log(` \u65B0\u65F6\u95F4: ${colors.green(formattedDate)}`);
3312
+ console.log(` \u4FEE\u6539\u7C7B\u578B: Author + Committer (\u4E24\u8005\u90FD\u4FEE\u6539)`);
3313
+ divider();
3314
+ const latestHash = execOutput("git rev-parse HEAD");
3315
+ const isLatestCommit = selectedCommit.hash === latestHash;
3316
+ if (!isLatestCommit) {
3317
+ console.log(
3318
+ colors.yellow(
3319
+ "\u26A0\uFE0F \u8B66\u544A: \u4FEE\u6539\u975E\u6700\u65B0 commit \u9700\u8981\u4F7F\u7528 rebase\uFF0C\u53EF\u80FD\u4F1A\u6539\u53D8 commit hash"
3320
+ )
3321
+ );
3322
+ console.log(colors.dim(" \u8FD9\u4F1A\u5F71\u54CD\u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u7684 commit\uFF0C\u8BF7\u8C28\u614E\u64CD\u4F5C"));
3323
+ console.log("");
3324
+ }
3325
+ const shouldProceed = await confirm({
3326
+ message: "\u786E\u8BA4\u4FEE\u6539?",
3327
+ default: false,
3328
+ theme
3329
+ });
3330
+ if (!shouldProceed) {
3331
+ console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
3332
+ return;
3333
+ }
3334
+ try {
3335
+ if (isLatestCommit) {
3336
+ execSync6(
3337
+ `GIT_AUTHOR_DATE="${formattedDate}" GIT_COMMITTER_DATE="${formattedDate}" git commit --amend --no-edit --reset-author`,
3338
+ { stdio: "pipe", shell: "/bin/bash" }
3339
+ );
3340
+ console.log("");
3341
+ console.log(colors.green("\u2714 \u4FEE\u6539\u6210\u529F"));
3342
+ } else {
3343
+ console.log("");
3344
+ console.log(colors.cyan("\u6B63\u5728\u6267\u884C rebase..."));
3345
+ const parentHash = execOutput(`git rev-parse ${selectedCommit.hash}^`);
3346
+ const filterCmd = `git filter-branch -f --env-filter 'if [ "$GIT_COMMIT" = "${selectedCommit.hash}" ]; then export GIT_AUTHOR_DATE="${formattedDate}"; export GIT_COMMITTER_DATE="${formattedDate}"; fi' ${parentHash}..HEAD`;
3347
+ execSync6(filterCmd, { stdio: "pipe" });
3348
+ console.log(colors.green("\u2714 \u4FEE\u6539\u6210\u529F"));
3349
+ console.log("");
3350
+ console.log(colors.yellow("\u26A0\uFE0F \u6CE8\u610F: commit hash \u5DF2\u6539\u53D8"));
3351
+ console.log(colors.dim(" \u5982\u679C\u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\uFF0C\u9700\u8981\u4F7F\u7528 force push:"));
3352
+ console.log(colors.cyan(" git push --force-with-lease"));
3353
+ }
3354
+ console.log("");
3355
+ } catch (error) {
3356
+ console.log("");
3357
+ console.log(colors.red("\u2716 \u4FEE\u6539\u5931\u8D25"));
3358
+ if (error instanceof Error) {
3359
+ console.log(colors.dim(error.message));
3360
+ }
3361
+ console.log("");
3362
+ throw error;
3363
+ }
3364
+ }
3365
+
3366
+ // src/commands/amend.ts
3367
+ init_utils();
3368
+ import { execSync as execSync7 } from "child_process";
3369
+ import { select as select9, input as input7, confirm as confirm2 } from "@inquirer/prompts";
3370
+ function getRecentCommits2(limit = 20) {
3371
+ const output = execOutput(`git log -${limit} --pretty=format:"%H|%s|%ai"`);
3372
+ if (!output) return [];
3373
+ return output.split("\n").map((line) => {
3374
+ const [hash, message, date] = line.split("|");
3375
+ return { hash, message, date };
3376
+ });
3377
+ }
3378
+ function getCommitByHash2(hash) {
3379
+ try {
3380
+ const output = execOutput(`git log -1 ${hash} --pretty=format:"%H|%s|%ai"`);
3381
+ if (!output) return null;
3382
+ const [fullHash, message, date] = output.split("|");
3383
+ return { hash: fullHash, message, date };
3384
+ } catch {
3385
+ return null;
3386
+ }
3387
+ }
3388
+ async function amend(commitHash) {
3389
+ console.log(colors.cyan("\u4FEE\u6539 Commit \u63D0\u4EA4\u4FE1\u606F"));
3390
+ divider();
3391
+ let selectedCommit;
3392
+ if (commitHash) {
3393
+ const commit2 = getCommitByHash2(commitHash);
3394
+ if (!commit2) {
3395
+ console.log(colors.red(`\u2716 \u627E\u4E0D\u5230 commit: ${commitHash}`));
3396
+ return;
3397
+ }
3398
+ selectedCommit = commit2;
3399
+ } else {
3400
+ const commits = getRecentCommits2(20);
3401
+ if (commits.length === 0) {
3402
+ console.log(colors.yellow("\u6CA1\u6709\u627E\u5230\u4EFB\u4F55 commit"));
3403
+ return;
3404
+ }
3405
+ selectedCommit = await select9({
3406
+ message: "\u9009\u62E9\u8981\u4FEE\u6539\u7684 commit:",
3407
+ choices: commits.map((c) => ({
3408
+ name: `${colors.yellow(c.hash.slice(0, 7))} ${c.message} ${colors.dim(c.date)}`,
3409
+ value: c,
3410
+ description: c.message
3411
+ })),
3412
+ pageSize: 15,
3413
+ theme
3414
+ });
3415
+ }
3416
+ console.log("");
3417
+ console.log("\u5F53\u524D commit \u4FE1\u606F:");
3418
+ console.log(` Hash: ${colors.yellow(selectedCommit.hash.slice(0, 7))}`);
3419
+ console.log(` Message: ${selectedCommit.message}`);
3420
+ console.log(` Date: ${colors.dim(selectedCommit.date)}`);
3421
+ divider();
3422
+ const newMessage = await input7({
3423
+ message: "\u8F93\u5165\u65B0\u7684 commit message:",
3424
+ default: selectedCommit.message,
3425
+ validate: (value) => {
3426
+ if (!value.trim()) {
3427
+ return "commit message \u4E0D\u80FD\u4E3A\u7A7A";
3428
+ }
3429
+ return true;
3430
+ },
3431
+ theme
3432
+ });
3433
+ divider();
3434
+ console.log("\u4FEE\u6539\u9884\u89C8:");
3435
+ console.log(
3436
+ ` Commit: ${colors.yellow(selectedCommit.hash.slice(0, 7))}`
3437
+ );
3438
+ console.log(` \u65E7 Message: ${colors.dim(selectedCommit.message)}`);
3439
+ console.log(` \u65B0 Message: ${colors.green(newMessage)}`);
3440
+ divider();
3441
+ const latestHash = execOutput("git rev-parse HEAD");
3442
+ const isLatestCommit = selectedCommit.hash === latestHash;
3443
+ if (!isLatestCommit) {
3444
+ console.log(
3445
+ colors.yellow(
3446
+ "\u26A0\uFE0F \u8B66\u544A: \u4FEE\u6539\u975E\u6700\u65B0 commit \u9700\u8981\u4F7F\u7528 rebase\uFF0C\u53EF\u80FD\u4F1A\u6539\u53D8 commit hash"
3447
+ )
3448
+ );
3449
+ console.log(colors.dim(" \u8FD9\u4F1A\u5F71\u54CD\u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\u7684 commit\uFF0C\u8BF7\u8C28\u614E\u64CD\u4F5C"));
3450
+ console.log("");
3451
+ }
3452
+ const shouldProceed = await confirm2({
3453
+ message: "\u786E\u8BA4\u4FEE\u6539?",
3454
+ default: false,
3455
+ theme
3456
+ });
3457
+ if (!shouldProceed) {
3458
+ console.log(colors.yellow("\u5DF2\u53D6\u6D88"));
3459
+ return;
3460
+ }
3461
+ try {
3462
+ if (isLatestCommit) {
3463
+ execSync7(`git commit --amend -m "${newMessage.replace(/"/g, '\\"')}"`, {
3464
+ stdio: "pipe"
3465
+ });
3466
+ console.log("");
3467
+ console.log(colors.green("\u2714 \u4FEE\u6539\u6210\u529F"));
3468
+ } else {
3469
+ console.log("");
3470
+ console.log(colors.cyan("\u6B63\u5728\u6267\u884C rebase..."));
3471
+ const parentHash = execOutput(`git rev-parse ${selectedCommit.hash}^`);
3472
+ const rebaseScript = `#!/bin/sh
3473
+ if [ "$1" = "${selectedCommit.hash}" ]; then
3474
+ echo "${newMessage}" > "$2"
3475
+ fi
3476
+ `;
3477
+ const filterCmd = `git filter-branch -f --msg-filter 'if [ "$GIT_COMMIT" = "${selectedCommit.hash}" ]; then echo "${newMessage.replace(/"/g, '\\"')}"; else cat; fi' ${parentHash}..HEAD`;
3478
+ execSync7(filterCmd, { stdio: "pipe" });
3479
+ console.log(colors.green("\u2714 \u4FEE\u6539\u6210\u529F"));
3480
+ console.log("");
3481
+ console.log(colors.yellow("\u26A0\uFE0F \u6CE8\u610F: commit hash \u5DF2\u6539\u53D8"));
3482
+ console.log(colors.dim(" \u5982\u679C\u5DF2\u63A8\u9001\u5230\u8FDC\u7A0B\uFF0C\u9700\u8981\u4F7F\u7528 force push:"));
3483
+ console.log(colors.cyan(" git push --force-with-lease"));
3484
+ }
3485
+ console.log("");
3486
+ } catch (error) {
3487
+ console.log("");
3488
+ console.log(colors.red("\u2716 \u4FEE\u6539\u5931\u8D25"));
3489
+ if (error instanceof Error) {
3490
+ console.log(colors.dim(error.message));
3491
+ }
3492
+ console.log("");
3493
+ throw error;
3494
+ }
3495
+ }
3496
+
3121
3497
  // src/index.ts
3122
3498
  process.on("uncaughtException", (err) => {
3123
3499
  if (err instanceof ExitPromptError) {
@@ -3143,7 +3519,7 @@ process.on("SIGTERM", () => {
3143
3519
  console.log("");
3144
3520
  process.exit(0);
3145
3521
  });
3146
- var version = true ? "0.4.2" : "0.0.0-dev";
3522
+ var version = true ? "0.4.4" : "0.0.0-dev";
3147
3523
  async function mainMenu() {
3148
3524
  console.log(
3149
3525
  colors.green(`
@@ -3157,7 +3533,7 @@ async function mainMenu() {
3157
3533
  );
3158
3534
  console.log(colors.dim(` git-workflow v${colors.yellow(version)}
3159
3535
  `));
3160
- const action = await select8({
3536
+ const action = await select10({
3161
3537
  message: "\u9009\u62E9\u64CD\u4F5C:",
3162
3538
  choices: [
3163
3539
  {
@@ -3201,11 +3577,19 @@ async function mainMenu() {
3201
3577
  value: "stash"
3202
3578
  },
3203
3579
  {
3204
- name: `[b] \uFFFD \u67E5\u770B\u65E5\u5FD7 ${colors.dim("gw log")}`,
3580
+ name: `[b] \u{1F4DC} \u67E5\u770B\u65E5\u5FD7 ${colors.dim("gw log")}`,
3205
3581
  value: "log"
3206
3582
  },
3207
3583
  {
3208
- name: `[c] \u2699\uFE0F \u521D\u59CB\u5316\u914D\u7F6E ${colors.dim("gw init")}`,
3584
+ name: `[c] \u{1F550} \u4FEE\u6539\u63D0\u4EA4\u65F6\u95F4 ${colors.dim("gw ad")}`,
3585
+ value: "amend-date"
3586
+ },
3587
+ {
3588
+ name: `[d] \u270F\uFE0F \u4FEE\u6539\u63D0\u4EA4\u4FE1\u606F ${colors.dim("gw amend")}`,
3589
+ value: "amend"
3590
+ },
3591
+ {
3592
+ name: `[e] \u2699\uFE0F \u521D\u59CB\u5316\u914D\u7F6E ${colors.dim("gw init")}`,
3209
3593
  value: "init"
3210
3594
  },
3211
3595
  { name: "[0] \u2753 \u5E2E\u52A9", value: "help" },
@@ -3258,6 +3642,14 @@ async function mainMenu() {
3258
3642
  checkGitRepo();
3259
3643
  await log();
3260
3644
  break;
3645
+ case "amend-date":
3646
+ checkGitRepo();
3647
+ await amendDate();
3648
+ break;
3649
+ case "amend":
3650
+ checkGitRepo();
3651
+ await amend();
3652
+ break;
3261
3653
  case "init":
3262
3654
  await init();
3263
3655
  break;
@@ -3341,18 +3733,28 @@ cli.command("log", "\u4EA4\u4E92\u5F0FGit\u65E5\u5FD7\u67E5\u770B (\u5206\u9875\
3341
3733
  if (options.limit) logOptions.limit = parseInt(options.limit);
3342
3734
  return log(logOptions);
3343
3735
  });
3736
+ cli.command("amend:date [hash]", "\u4FEE\u6539\u6307\u5B9A commit \u7684\u63D0\u4EA4\u65F6\u95F4").alias("ad").action(async (hash) => {
3737
+ await checkForUpdates(version, "@zjex/git-workflow");
3738
+ checkGitRepo();
3739
+ return amendDate(hash);
3740
+ });
3741
+ cli.command("amend [hash]", "\u4FEE\u6539\u6307\u5B9A commit \u7684\u63D0\u4EA4\u4FE1\u606F").action(async (hash) => {
3742
+ await checkForUpdates(version, "@zjex/git-workflow");
3743
+ checkGitRepo();
3744
+ return amend(hash);
3745
+ });
3344
3746
  cli.command("clean", "\u6E05\u7406\u7F13\u5B58\u548C\u4E34\u65F6\u6587\u4EF6").alias("cc").action(async () => {
3345
3747
  const { clearUpdateCache: clearUpdateCache3 } = await Promise.resolve().then(() => (init_update_notifier(), update_notifier_exports));
3346
3748
  const { existsSync: existsSync5, unlinkSync: unlinkSync4, readdirSync } = await import("fs");
3347
3749
  const { homedir: homedir5, tmpdir: tmpdir2 } = await import("os");
3348
3750
  const { join: join6 } = await import("path");
3349
- const { select: select9 } = await import("@inquirer/prompts");
3751
+ const { select: select11 } = await import("@inquirer/prompts");
3350
3752
  let cleanedCount = 0;
3351
3753
  let deletedGlobalConfig = false;
3352
3754
  const globalConfig = join6(homedir5(), ".gwrc.json");
3353
3755
  const hasGlobalConfig = existsSync5(globalConfig);
3354
3756
  if (hasGlobalConfig) {
3355
- const shouldDeleteConfig = await select9({
3757
+ const shouldDeleteConfig = await select11({
3356
3758
  message: "\u68C0\u6D4B\u5230\u5168\u5C40\u914D\u7F6E\u6587\u4EF6\uFF0C\u662F\u5426\u5220\u9664\uFF1F",
3357
3759
  choices: [
3358
3760
  { name: "\u5426\uFF0C\u4FDD\u7559\u914D\u7F6E\u6587\u4EF6", value: false },