@zjex/git-workflow 0.3.6 → 0.3.8

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
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env -S node --no-warnings=ExperimentalWarning
1
+ #!/usr/bin/env -S node --disable-warning=ExperimentalWarning
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __esm = (fn, res) => function __init() {
@@ -67,6 +67,8 @@ var init_utils = __esm({
67
67
  orange: (s) => `\x1B[38;5;208m${s}\x1B[0m`,
68
68
  lightPurple: (s) => `\x1B[38;5;141m${s}\x1B[0m`,
69
69
  white: (s) => `\x1B[37m${s}\x1B[0m`,
70
+ brightYellow: (s) => `\x1B[93m${s}\x1B[0m`,
71
+ // 亮黄色
70
72
  reset: "\x1B[0m"
71
73
  };
72
74
  TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
@@ -89,9 +91,9 @@ __export(update_notifier_exports, {
89
91
  clearUpdateCache: () => clearUpdateCache
90
92
  });
91
93
  import { execSync as execSync6 } from "child_process";
92
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync3, unlinkSync } from "fs";
94
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync3, unlinkSync as unlinkSync2 } from "fs";
93
95
  import { homedir as homedir3 } from "os";
94
- import { join as join3 } from "path";
96
+ import { join as join4 } from "path";
95
97
  import boxen from "boxen";
96
98
  import { select as select7 } from "@inquirer/prompts";
97
99
  import ora5 from "ora";
@@ -281,16 +283,16 @@ async function performUpdate(packageName) {
281
283
  }
282
284
  function clearUpdateCache() {
283
285
  try {
284
- const cacheFile = join3(homedir3(), CACHE_FILE);
286
+ const cacheFile = join4(homedir3(), CACHE_FILE);
285
287
  if (existsSync3(cacheFile)) {
286
- unlinkSync(cacheFile);
288
+ unlinkSync2(cacheFile);
287
289
  }
288
290
  } catch {
289
291
  }
290
292
  }
291
293
  function readCache() {
292
294
  try {
293
- const cacheFile = join3(homedir3(), CACHE_FILE);
295
+ const cacheFile = join4(homedir3(), CACHE_FILE);
294
296
  if (!existsSync3(cacheFile)) {
295
297
  return null;
296
298
  }
@@ -302,8 +304,8 @@ function readCache() {
302
304
  }
303
305
  function writeCache(cache) {
304
306
  try {
305
- const cacheFile = join3(homedir3(), CACHE_FILE);
306
- writeFileSync3(cacheFile, JSON.stringify(cache), "utf-8");
307
+ const cacheFile = join4(homedir3(), CACHE_FILE);
308
+ writeFileSync4(cacheFile, JSON.stringify(cache), "utf-8");
307
309
  } catch {
308
310
  }
309
311
  }
@@ -1789,6 +1791,9 @@ async function dropStash(index) {
1789
1791
  // src/commands/commit.ts
1790
1792
  init_utils();
1791
1793
  import { execSync as execSync5 } from "child_process";
1794
+ import { writeFileSync as writeFileSync3, unlinkSync } from "fs";
1795
+ import { tmpdir } from "os";
1796
+ import { join as join3 } from "path";
1792
1797
  import { select as select6, input as input5, checkbox } from "@inquirer/prompts";
1793
1798
  import ora4 from "ora";
1794
1799
 
@@ -2196,45 +2201,39 @@ function formatFileStatus(status) {
2196
2201
  }
2197
2202
  async function commit() {
2198
2203
  const config2 = getConfig();
2204
+ const autoStage = config2.autoStage ?? true;
2205
+ if (autoStage) {
2206
+ execSync5("git add -A", { stdio: "pipe" });
2207
+ }
2199
2208
  let { staged, unstaged } = parseGitStatus();
2200
- if (unstaged.length > 0) {
2201
- const autoStage = config2.autoStage ?? true;
2202
- if (autoStage) {
2203
- execSync5("git add -A", { stdio: "pipe" });
2204
- console.log(colors.green("\u2714 \u5DF2\u81EA\u52A8\u6682\u5B58\u6240\u6709\u66F4\u6539"));
2205
- divider();
2206
- const newStatus = parseGitStatus();
2207
- staged = newStatus.staged;
2208
- unstaged = newStatus.unstaged;
2209
- } else if (staged.length === 0) {
2210
- console.log(colors.yellow("\u6CA1\u6709\u6682\u5B58\u7684\u66F4\u6539"));
2211
- divider();
2212
- console.log("\u672A\u6682\u5B58\u7684\u6587\u4EF6:");
2213
- for (const { status, file } of unstaged) {
2214
- console.log(` ${formatFileStatus(status)} ${file}`);
2215
- }
2216
- divider();
2217
- const filesToStage = await checkbox({
2218
- message: "\u9009\u62E9\u8981\u6682\u5B58\u7684\u6587\u4EF6:",
2219
- choices: unstaged.map(({ status, file }) => ({
2220
- name: `${formatFileStatus(status)} ${file}`,
2221
- value: file,
2222
- checked: true
2223
- })),
2224
- theme
2225
- });
2226
- if (filesToStage.length === 0) {
2227
- console.log(colors.yellow("\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6587\u4EF6\uFF0C\u5DF2\u53D6\u6D88"));
2228
- return;
2229
- }
2230
- for (const file of filesToStage) {
2231
- execSync5(`git add "${file}"`, { stdio: "pipe" });
2232
- }
2233
- console.log(colors.green(`\u2714 \u5DF2\u6682\u5B58 ${filesToStage.length} \u4E2A\u6587\u4EF6`));
2234
- divider();
2235
- const newStatus = parseGitStatus();
2236
- staged = newStatus.staged;
2209
+ if (staged.length === 0 && unstaged.length > 0 && !autoStage) {
2210
+ console.log(colors.yellow("\u6CA1\u6709\u6682\u5B58\u7684\u66F4\u6539"));
2211
+ divider();
2212
+ console.log("\u672A\u6682\u5B58\u7684\u6587\u4EF6:");
2213
+ for (const { status, file } of unstaged) {
2214
+ console.log(` ${formatFileStatus(status)} ${file}`);
2215
+ }
2216
+ divider();
2217
+ const filesToStage = await checkbox({
2218
+ message: "\u9009\u62E9\u8981\u6682\u5B58\u7684\u6587\u4EF6:",
2219
+ choices: unstaged.map(({ status, file }) => ({
2220
+ name: `${formatFileStatus(status)} ${file}`,
2221
+ value: file,
2222
+ checked: true
2223
+ })),
2224
+ theme
2225
+ });
2226
+ if (filesToStage.length === 0) {
2227
+ console.log(colors.yellow("\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6587\u4EF6\uFF0C\u5DF2\u53D6\u6D88"));
2228
+ return;
2229
+ }
2230
+ for (const file of filesToStage) {
2231
+ execSync5(`git add "${file}"`, { stdio: "pipe" });
2237
2232
  }
2233
+ console.log(colors.green(`\u2714 \u5DF2\u6682\u5B58 ${filesToStage.length} \u4E2A\u6587\u4EF6`));
2234
+ divider();
2235
+ const newStatus = parseGitStatus();
2236
+ staged = newStatus.staged;
2238
2237
  }
2239
2238
  if (staged.length === 0) {
2240
2239
  console.log(colors.yellow("\u5DE5\u4F5C\u533A\u5E72\u51C0\uFF0C\u6CA1\u6709\u9700\u8981\u63D0\u4EA4\u7684\u66F4\u6539"));
@@ -2320,14 +2319,10 @@ async function commit() {
2320
2319
  }
2321
2320
  const spinner = ora4("\u6B63\u5728\u63D0\u4EA4...").start();
2322
2321
  try {
2323
- let finalStatus = parseGitStatus();
2324
- if (finalStatus.staged.length === 0 && finalStatus.unstaged.length > 0) {
2325
- const autoStage = config2.autoStage ?? true;
2326
- if (autoStage) {
2327
- execSync5("git add -A", { stdio: "pipe" });
2328
- finalStatus = parseGitStatus();
2329
- }
2322
+ if (autoStage) {
2323
+ execSync5("git add -A", { stdio: "pipe" });
2330
2324
  }
2325
+ const finalStatus = parseGitStatus();
2331
2326
  if (finalStatus.staged.length === 0) {
2332
2327
  spinner.fail("\u6CA1\u6709\u6682\u5B58\u7684\u6587\u4EF6\u53EF\u4EE5\u63D0\u4EA4");
2333
2328
  console.log("");
@@ -2338,9 +2333,18 @@ async function commit() {
2338
2333
  console.log("");
2339
2334
  return;
2340
2335
  }
2341
- execSync5(`git commit -F -`, {
2342
- input: message
2343
- });
2336
+ const tmpFile = join3(tmpdir(), `.gw-commit-msg-${Date.now()}`);
2337
+ try {
2338
+ writeFileSync3(tmpFile, message, "utf-8");
2339
+ execSync5(`git commit -F "${tmpFile}"`, {
2340
+ stdio: ["pipe", "pipe", "pipe"]
2341
+ });
2342
+ } finally {
2343
+ try {
2344
+ unlinkSync(tmpFile);
2345
+ } catch {
2346
+ }
2347
+ }
2344
2348
  spinner.succeed("\u63D0\u4EA4\u6210\u529F");
2345
2349
  const commitHash = execOutput("git rev-parse --short HEAD");
2346
2350
  console.log(colors.dim(`commit: ${commitHash}`));
@@ -2447,15 +2451,15 @@ import { execSync as execSync7 } from "child_process";
2447
2451
  import ora6 from "ora";
2448
2452
  import boxen2 from "boxen";
2449
2453
  import semver2 from "semver";
2450
- import { existsSync as existsSync4, unlinkSync as unlinkSync2 } from "fs";
2454
+ import { existsSync as existsSync4, unlinkSync as unlinkSync3 } from "fs";
2451
2455
  import { homedir as homedir4 } from "os";
2452
- import { join as join4 } from "path";
2456
+ import { join as join5 } from "path";
2453
2457
  var CACHE_FILE2 = ".gw-update-check";
2454
2458
  function clearUpdateCache2() {
2455
2459
  try {
2456
- const cacheFile = join4(homedir4(), CACHE_FILE2);
2460
+ const cacheFile = join5(homedir4(), CACHE_FILE2);
2457
2461
  if (existsSync4(cacheFile)) {
2458
- unlinkSync2(cacheFile);
2462
+ unlinkSync3(cacheFile);
2459
2463
  }
2460
2464
  } catch {
2461
2465
  }
@@ -2602,57 +2606,49 @@ function parseGitLog(output) {
2602
2606
  }
2603
2607
  return commits;
2604
2608
  }
2605
- function getCommitTypeIcon(subject) {
2606
- const lowerSubject = subject.toLowerCase();
2607
- if (lowerSubject.includes("feat") || lowerSubject.includes("feature")) return "\u2728";
2608
- if (lowerSubject.includes("fix") || lowerSubject.includes("bug")) return "\u{1F41B}";
2609
- if (lowerSubject.includes("docs") || lowerSubject.includes("doc")) return "\u{1F4DA}";
2610
- if (lowerSubject.includes("style")) return "\u{1F484}";
2611
- if (lowerSubject.includes("refactor")) return "\u267B\uFE0F";
2612
- if (lowerSubject.includes("test")) return "\u{1F9EA}";
2613
- if (lowerSubject.includes("chore")) return "\u{1F527}";
2614
- if (lowerSubject.includes("perf")) return "\u26A1";
2615
- if (lowerSubject.includes("ci")) return "\u{1F477}";
2616
- if (lowerSubject.includes("build")) return "\u{1F4E6}";
2617
- if (lowerSubject.includes("revert")) return "\u23EA";
2618
- if (lowerSubject.includes("merge")) return "\u{1F500}";
2619
- if (lowerSubject.includes("release") || lowerSubject.includes("version")) return "\u{1F516}";
2620
- return "\u{1F4DD}";
2621
- }
2622
2609
  function groupCommitsByDate(commits) {
2623
2610
  const groups = /* @__PURE__ */ new Map();
2611
+ const dateOrder = [];
2624
2612
  for (const commit2 of commits) {
2625
2613
  const date = commit2.date;
2626
2614
  if (!groups.has(date)) {
2627
2615
  groups.set(date, []);
2616
+ dateOrder.push(date);
2628
2617
  }
2629
2618
  groups.get(date).push(commit2);
2630
2619
  }
2631
- return groups;
2620
+ const orderedGroups = /* @__PURE__ */ new Map();
2621
+ for (const date of dateOrder) {
2622
+ orderedGroups.set(date, groups.get(date));
2623
+ }
2624
+ return orderedGroups;
2632
2625
  }
2633
2626
  function formatRelativeTime(relativeDate) {
2634
2627
  let result = relativeDate;
2635
2628
  const timeMap = {
2636
- "second": "\u79D2",
2637
- "seconds": "\u79D2",
2638
- "minute": "\u5206\u949F",
2639
- "minutes": "\u5206\u949F",
2640
- "hour": "\u5C0F\u65F6",
2641
- "hours": "\u5C0F\u65F6",
2642
- "day": "\u5929",
2643
- "days": "\u5929",
2644
- "week": "\u5468",
2645
- "weeks": "\u5468",
2646
- "month": "\u4E2A\u6708",
2647
- "months": "\u4E2A\u6708",
2648
- "year": "\u5E74",
2649
- "years": "\u5E74",
2650
- "ago": "\u524D"
2629
+ second: "\u79D2",
2630
+ seconds: "\u79D2",
2631
+ minute: "\u5206\u949F",
2632
+ minutes: "\u5206\u949F",
2633
+ hour: "\u5C0F\u65F6",
2634
+ hours: "\u5C0F\u65F6",
2635
+ day: "\u5929",
2636
+ days: "\u5929",
2637
+ week: "\u5468",
2638
+ weeks: "\u5468",
2639
+ month: "\u4E2A\u6708",
2640
+ months: "\u4E2A\u6708",
2641
+ year: "\u5E74",
2642
+ years: "\u5E74",
2643
+ ago: "\u524D"
2651
2644
  };
2652
2645
  for (const [en, zh] of Object.entries(timeMap)) {
2653
2646
  result = result.replace(new RegExp(`\\b${en}\\b`, "g"), zh);
2654
2647
  }
2655
- result = result.replace(/(\d+)\s+(秒|分钟|小时|天|周|个月|年)\s+前/g, "$1$2\u524D");
2648
+ result = result.replace(
2649
+ /(\d+)\s+(秒|分钟|小时|天|周|个月|年)\s+前/g,
2650
+ "$1$2\u524D"
2651
+ );
2656
2652
  const match = result.match(/(\d+)(分钟|小时|天|周|个月|年)前/);
2657
2653
  if (match) {
2658
2654
  const num = parseInt(match[1]);
@@ -2699,28 +2695,25 @@ function supportsColor() {
2699
2695
  function formatTimelineStyle(commits) {
2700
2696
  const groupedCommits = groupCommitsByDate(commits);
2701
2697
  let output = "";
2702
- const sortedDates = Array.from(groupedCommits.keys()).sort(
2703
- (a, b) => new Date(b).getTime() - new Date(a).getTime()
2704
- );
2698
+ const sortedDates = Array.from(groupedCommits.keys());
2705
2699
  const useColors = supportsColor() || process.env.FORCE_COLOR;
2706
2700
  for (let dateIndex = 0; dateIndex < sortedDates.length; dateIndex++) {
2707
2701
  const date = sortedDates[dateIndex];
2708
2702
  const dateCommits = groupedCommits.get(date);
2709
2703
  const dateTitle = `\u{1F4C5} Commits on ${date}`;
2710
2704
  if (useColors) {
2711
- output += "\n" + colors.bold(colors.yellow(dateTitle)) + "\n\n";
2705
+ output += colors.bold(colors.yellow(dateTitle)) + "\n";
2712
2706
  } else {
2713
- output += "\n" + dateTitle + "\n\n";
2707
+ output += dateTitle + "\n";
2714
2708
  }
2715
2709
  for (let commitIndex = 0; commitIndex < dateCommits.length; commitIndex++) {
2716
2710
  const commit2 = dateCommits[commitIndex];
2717
- const icon = getCommitTypeIcon(commit2.subject);
2718
2711
  const { title, tasks } = parseCommitSubject(commit2.subject);
2719
2712
  const commitContent = [];
2720
2713
  if (useColors) {
2721
- commitContent.push(`${icon} ${colors.bold(colors.white(title))}`);
2714
+ commitContent.push(colors.bold(colors.white(title)));
2722
2715
  } else {
2723
- commitContent.push(`${icon} ${title}`);
2716
+ commitContent.push(title);
2724
2717
  }
2725
2718
  if (tasks.length > 0) {
2726
2719
  commitContent.push("");
@@ -2734,8 +2727,12 @@ function formatTimelineStyle(commits) {
2734
2727
  }
2735
2728
  commitContent.push("");
2736
2729
  if (useColors) {
2737
- commitContent.push(`${colors.dim("\u{1F464}")} ${colors.blue(commit2.author)} ${colors.dim("committed")} ${colors.green(formatRelativeTime(commit2.relativeDate))}`);
2738
- commitContent.push(`${colors.dim("\u{1F517}")} ${colors.orange("#" + commit2.shortHash)}`);
2730
+ commitContent.push(
2731
+ `${colors.dim("\u{1F464}")} ${colors.blue(commit2.author)} ${colors.dim(
2732
+ "committed"
2733
+ )} ${colors.green(formatRelativeTime(commit2.relativeDate))}`
2734
+ );
2735
+ commitContent.push(`${colors.dim("\u{1F517}")} ${colors.orange(commit2.hash)}`);
2739
2736
  if (commit2.refs && commit2.refs.trim()) {
2740
2737
  const refs = commit2.refs.trim();
2741
2738
  const refParts = refs.split(", ");
@@ -2751,16 +2748,24 @@ function formatTimelineStyle(commits) {
2751
2748
  }
2752
2749
  });
2753
2750
  if (branches.length > 0) {
2754
- commitContent.push(`${colors.dim("\u{1F33F}")} ${colors.lightPurple(branches.join(", "))}`);
2751
+ commitContent.push(
2752
+ `${colors.dim("\u{1F33F}")} ${colors.lightPurple(branches.join(", "))}`
2753
+ );
2755
2754
  }
2756
2755
  if (tags.length > 0) {
2757
2756
  const tagText = tags.map((tag) => `tag ${tag}`).join(", ");
2758
- commitContent.push(`${colors.dim("\u{1F516}")} ${colors.yellow(tagText)}`);
2757
+ commitContent.push(
2758
+ `${colors.dim("\u{1F516}")} ${colors.brightYellow(tagText)}`
2759
+ );
2759
2760
  }
2760
2761
  }
2761
2762
  } else {
2762
- commitContent.push(`\u{1F464} ${commit2.author} committed ${formatRelativeTime(commit2.relativeDate)}`);
2763
- commitContent.push(`\u{1F517} #${commit2.shortHash}`);
2763
+ commitContent.push(
2764
+ `\u{1F464} ${commit2.author} committed ${formatRelativeTime(
2765
+ commit2.relativeDate
2766
+ )}`
2767
+ );
2768
+ commitContent.push(`\u{1F517} ${commit2.hash}`);
2764
2769
  if (commit2.refs && commit2.refs.trim()) {
2765
2770
  const refs = commit2.refs.trim();
2766
2771
  const refParts = refs.split(", ");
@@ -2786,7 +2791,7 @@ function formatTimelineStyle(commits) {
2786
2791
  }
2787
2792
  const commitBox = boxen3(commitContent.join("\n"), {
2788
2793
  padding: { top: 0, bottom: 0, left: 1, right: 1 },
2789
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
2794
+ margin: { top: 0, bottom: 0, left: 0, right: 0 },
2790
2795
  borderStyle: "round",
2791
2796
  borderColor: "gray"
2792
2797
  });
@@ -2802,11 +2807,17 @@ function startInteractivePager(content) {
2802
2807
  stdio: ["pipe", "inherit", "inherit"],
2803
2808
  env: { ...process.env, LESS: "-R -S -F -X -i" }
2804
2809
  });
2810
+ pagerProcess.stdin.on("error", (err) => {
2811
+ if (err.code !== "EPIPE") {
2812
+ console.error(err);
2813
+ }
2814
+ });
2805
2815
  pagerProcess.stdin.write(content);
2806
2816
  pagerProcess.stdin.end();
2807
2817
  pagerProcess.on("exit", () => {
2818
+ process.exit(0);
2808
2819
  });
2809
- pagerProcess.on("error", (err) => {
2820
+ pagerProcess.on("error", () => {
2810
2821
  console.log(content);
2811
2822
  });
2812
2823
  } catch (error) {
@@ -2822,9 +2833,6 @@ function executeTimelineLog(options) {
2822
2833
  if (options.until) cmd += ` --until="${options.until}"`;
2823
2834
  if (options.grep) cmd += ` --grep="${options.grep}"`;
2824
2835
  if (options.all) cmd += ` --all`;
2825
- if (options.interactive && !options.limit) {
2826
- cmd += ` -50`;
2827
- }
2828
2836
  const output = execSync8(cmd, {
2829
2837
  encoding: "utf8",
2830
2838
  stdio: "pipe",
@@ -2833,14 +2841,6 @@ function executeTimelineLog(options) {
2833
2841
  if (output.trim()) {
2834
2842
  const commits = parseGitLog(output);
2835
2843
  let fullOutput = "";
2836
- const title = `\u{1F4CA} \u5171\u663E\u793A ${commits.length} \u4E2A\u63D0\u4EA4`;
2837
- fullOutput += "\n" + boxen3(title, {
2838
- padding: { top: 0, bottom: 0, left: 2, right: 2 },
2839
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
2840
- borderStyle: "double",
2841
- borderColor: "green",
2842
- textAlignment: "center"
2843
- }) + "\n";
2844
2844
  const timelineOutput = formatTimelineStyle(commits);
2845
2845
  fullOutput += timelineOutput;
2846
2846
  if (options.interactive) {
@@ -2916,7 +2916,7 @@ process.on("SIGTERM", () => {
2916
2916
  console.log("");
2917
2917
  process.exit(0);
2918
2918
  });
2919
- var version = true ? "0.3.6" : "0.0.0-dev";
2919
+ var version = true ? "0.3.8" : "0.0.0-dev";
2920
2920
  async function mainMenu() {
2921
2921
  console.log(
2922
2922
  colors.green(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zjex/git-workflow",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,4 +1,7 @@
1
1
  import { execSync } from "child_process";
2
+ import { writeFileSync, unlinkSync } from "fs";
3
+ import { tmpdir } from "os";
4
+ import { join } from "path";
2
5
  import { select, input, checkbox } from "@inquirer/prompts";
3
6
  import ora from "ora";
4
7
  import { colors, theme, execOutput, divider } from "../utils.js";
@@ -102,58 +105,52 @@ function formatFileStatus(status: string): string {
102
105
  */
103
106
  export async function commit(): Promise<void> {
104
107
  const config = getConfig();
105
- let { staged, unstaged } = parseGitStatus();
106
-
107
- // ========== 步骤 1: 处理未暂存的文件 ==========
108
- if (unstaged.length > 0) {
109
- const autoStage = config.autoStage ?? true;
108
+ const autoStage = config.autoStage ?? true;
110
109
 
111
- if (autoStage) {
112
- // 自动暂存所有文件
113
- execSync("git add -A", { stdio: "pipe" });
114
- console.log(colors.green("✔ 已自动暂存所有更改"));
115
- divider();
116
- // 重新获取状态
117
- const newStatus = parseGitStatus();
118
- staged = newStatus.staged;
119
- unstaged = newStatus.unstaged;
120
- } else if (staged.length === 0) {
121
- // 没有暂存的文件,且不自动暂存,让用户选择
122
- console.log(colors.yellow("没有暂存的更改"));
123
- divider();
124
- console.log("未暂存的文件:");
125
- for (const { status, file } of unstaged) {
126
- console.log(` ${formatFileStatus(status)} ${file}`);
127
- }
128
- divider();
110
+ // ========== 步骤 1: 自动暂存(如果启用)==========
111
+ if (autoStage) {
112
+ execSync("git add -A", { stdio: "pipe" });
113
+ }
129
114
 
130
- // 让用户选择要暂存的文件
131
- const filesToStage = await checkbox({
132
- message: "选择要暂存的文件:",
133
- choices: unstaged.map(({ status, file }) => ({
134
- name: `${formatFileStatus(status)} ${file}`,
135
- value: file,
136
- checked: true,
137
- })),
138
- theme,
139
- });
115
+ // 获取当前状态
116
+ let { staged, unstaged } = parseGitStatus();
140
117
 
141
- if (filesToStage.length === 0) {
142
- console.log(colors.yellow("没有选择任何文件,已取消"));
143
- return;
144
- }
118
+ // ========== 步骤 2: 如果没有暂存文件,让用户选择 ==========
119
+ if (staged.length === 0 && unstaged.length > 0 && !autoStage) {
120
+ console.log(colors.yellow("没有暂存的更改"));
121
+ divider();
122
+ console.log("未暂存的文件:");
123
+ for (const { status, file } of unstaged) {
124
+ console.log(` ${formatFileStatus(status)} ${file}`);
125
+ }
126
+ divider();
127
+
128
+ // 让用户选择要暂存的文件
129
+ const filesToStage = await checkbox({
130
+ message: "选择要暂存的文件:",
131
+ choices: unstaged.map(({ status, file }) => ({
132
+ name: `${formatFileStatus(status)} ${file}`,
133
+ value: file,
134
+ checked: true,
135
+ })),
136
+ theme,
137
+ });
145
138
 
146
- // 暂存选中的文件
147
- for (const file of filesToStage) {
148
- execSync(`git add "${file}"`, { stdio: "pipe" });
149
- }
150
- console.log(colors.green(`✔ 已暂存 ${filesToStage.length} 个文件`));
151
- divider();
139
+ if (filesToStage.length === 0) {
140
+ console.log(colors.yellow("没有选择任何文件,已取消"));
141
+ return;
142
+ }
152
143
 
153
- // 重新获取状态
154
- const newStatus = parseGitStatus();
155
- staged = newStatus.staged;
144
+ // 暂存选中的文件
145
+ for (const file of filesToStage) {
146
+ execSync(`git add "${file}"`, { stdio: "pipe" });
156
147
  }
148
+ console.log(colors.green(`✔ 已暂存 ${filesToStage.length} 个文件`));
149
+ divider();
150
+
151
+ // 重新获取状态
152
+ const newStatus = parseGitStatus();
153
+ staged = newStatus.staged;
157
154
  }
158
155
 
159
156
  // ========== 步骤 2: 检查是否有文件可提交 ==========
@@ -264,18 +261,13 @@ export async function commit(): Promise<void> {
264
261
  const spinner = ora("正在提交...").start();
265
262
 
266
263
  try {
267
- // 提交前再次检查是否有暂存的文件
268
- let finalStatus = parseGitStatus();
269
-
270
- // 如果暂存区为空,但有未暂存的更改,且开启了自动暂存,则重新暂存
271
- if (finalStatus.staged.length === 0 && finalStatus.unstaged.length > 0) {
272
- const autoStage = config.autoStage ?? true;
273
- if (autoStage) {
274
- execSync("git add -A", { stdio: "pipe" });
275
- finalStatus = parseGitStatus();
276
- }
264
+ // 提交前再次暂存所有更改(确保不会遗漏)
265
+ if (autoStage) {
266
+ execSync("git add -A", { stdio: "pipe" });
277
267
  }
278
268
 
269
+ // 检查是否有暂存的文件
270
+ const finalStatus = parseGitStatus();
279
271
  if (finalStatus.staged.length === 0) {
280
272
  spinner.fail("没有暂存的文件可以提交");
281
273
  console.log("");
@@ -287,11 +279,21 @@ export async function commit(): Promise<void> {
287
279
  return;
288
280
  }
289
281
 
290
- // 处理多行消息:使用 git commit -F - 通过 stdin 传递
291
- // 这样可以正确处理包含换行符的 commit message
292
- execSync(`git commit -F -`, {
293
- input: message,
294
- });
282
+ // 处理多行消息:使用临时文件传递 commit message
283
+ const tmpFile = join(tmpdir(), `.gw-commit-msg-${Date.now()}`);
284
+ try {
285
+ writeFileSync(tmpFile, message, "utf-8");
286
+ execSync(`git commit -F "${tmpFile}"`, {
287
+ stdio: ["pipe", "pipe", "pipe"],
288
+ });
289
+ } finally {
290
+ // 清理临时文件
291
+ try {
292
+ unlinkSync(tmpFile);
293
+ } catch {
294
+ // 忽略删除失败
295
+ }
296
+ }
295
297
  spinner.succeed("提交成功");
296
298
 
297
299
  // 显示提交信息
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * @zjex/git-workflow - Log 命令
3
- *
3
+ *
4
4
  * 提供GitHub风格的时间线日志查看功能
5
5
  */
6
6
 
@@ -43,13 +43,13 @@ interface CommitInfo {
43
43
  */
44
44
  function parseGitLog(output: string): CommitInfo[] {
45
45
  const commits: CommitInfo[] = [];
46
- const lines = output.trim().split('\n');
47
-
46
+ const lines = output.trim().split("\n");
47
+
48
48
  for (const line of lines) {
49
49
  if (!line.trim()) continue;
50
-
50
+
51
51
  // 使用分隔符解析
52
- const parts = line.split('|');
52
+ const parts = line.split("|");
53
53
  if (parts.length >= 6) {
54
54
  commits.push({
55
55
  hash: parts[0],
@@ -58,11 +58,11 @@ function parseGitLog(output: string): CommitInfo[] {
58
58
  author: parts[3],
59
59
  date: parts[4],
60
60
  relativeDate: parts[5],
61
- refs: parts[6] || ''
61
+ refs: parts[6] || "",
62
62
  });
63
63
  }
64
64
  }
65
-
65
+
66
66
  return commits;
67
67
  }
68
68
 
@@ -71,39 +71,50 @@ function parseGitLog(output: string): CommitInfo[] {
71
71
  */
72
72
  function getCommitTypeIcon(subject: string): string {
73
73
  const lowerSubject = subject.toLowerCase();
74
-
75
- if (lowerSubject.includes('feat') || lowerSubject.includes('feature')) return '✨';
76
- if (lowerSubject.includes('fix') || lowerSubject.includes('bug')) return '🐛';
77
- if (lowerSubject.includes('docs') || lowerSubject.includes('doc')) return '📚';
78
- if (lowerSubject.includes('style')) return '💄';
79
- if (lowerSubject.includes('refactor')) return '♻️';
80
- if (lowerSubject.includes('test')) return '🧪';
81
- if (lowerSubject.includes('chore')) return '🔧';
82
- if (lowerSubject.includes('perf')) return '⚡';
83
- if (lowerSubject.includes('ci')) return '👷';
84
- if (lowerSubject.includes('build')) return '📦';
85
- if (lowerSubject.includes('revert')) return '⏪';
86
- if (lowerSubject.includes('merge')) return '🔀';
87
- if (lowerSubject.includes('release') || lowerSubject.includes('version')) return '🔖';
88
-
89
- return '📝';
74
+
75
+ if (lowerSubject.includes("feat") || lowerSubject.includes("feature"))
76
+ return "✨";
77
+ if (lowerSubject.includes("fix") || lowerSubject.includes("bug")) return "🐛";
78
+ if (lowerSubject.includes("docs") || lowerSubject.includes("doc"))
79
+ return "📚";
80
+ if (lowerSubject.includes("style")) return "💄";
81
+ if (lowerSubject.includes("refactor")) return "♻️";
82
+ if (lowerSubject.includes("test")) return "🧪";
83
+ if (lowerSubject.includes("chore")) return "🔧";
84
+ if (lowerSubject.includes("perf")) return "⚡";
85
+ if (lowerSubject.includes("ci")) return "👷";
86
+ if (lowerSubject.includes("build")) return "📦";
87
+ if (lowerSubject.includes("revert")) return "⏪";
88
+ if (lowerSubject.includes("merge")) return "🔀";
89
+ if (lowerSubject.includes("release") || lowerSubject.includes("version"))
90
+ return "🔖";
91
+
92
+ return "📝";
90
93
  }
91
94
 
92
95
  /**
93
- * 按日期分组提交
96
+ * 按日期分组提交(保持原始提交顺序)
94
97
  */
95
98
  function groupCommitsByDate(commits: CommitInfo[]): Map<string, CommitInfo[]> {
96
99
  const groups = new Map<string, CommitInfo[]>();
97
-
100
+ const dateOrder: string[] = []; // 记录日期出现的顺序
101
+
98
102
  for (const commit of commits) {
99
103
  const date = commit.date;
100
104
  if (!groups.has(date)) {
101
105
  groups.set(date, []);
106
+ dateOrder.push(date); // 按出现顺序记录日期
102
107
  }
103
108
  groups.get(date)!.push(commit);
104
109
  }
105
-
106
- return groups;
110
+
111
+ // 返回按出现顺序排列的 Map
112
+ const orderedGroups = new Map<string, CommitInfo[]>();
113
+ for (const date of dateOrder) {
114
+ orderedGroups.set(date, groups.get(date)!);
115
+ }
116
+
117
+ return orderedGroups;
107
118
  }
108
119
 
109
120
  /**
@@ -111,92 +122,101 @@ function groupCommitsByDate(commits: CommitInfo[]): Map<string, CommitInfo[]> {
111
122
  */
112
123
  function formatRelativeTime(relativeDate: string): string {
113
124
  let result = relativeDate;
114
-
125
+
115
126
  // 先替换英文单词为中文
116
127
  const timeMap: { [key: string]: string } = {
117
- 'second': '',
118
- 'seconds': '',
119
- 'minute': '分钟',
120
- 'minutes': '分钟',
121
- 'hour': '小时',
122
- 'hours': '小时',
123
- 'day': '',
124
- 'days': '',
125
- 'week': '',
126
- 'weeks': '',
127
- 'month': '个月',
128
- 'months': '个月',
129
- 'year': '',
130
- 'years': '',
131
- 'ago': ''
128
+ second: "",
129
+ seconds: "",
130
+ minute: "分钟",
131
+ minutes: "分钟",
132
+ hour: "小时",
133
+ hours: "小时",
134
+ day: "",
135
+ days: "",
136
+ week: "",
137
+ weeks: "",
138
+ month: "个月",
139
+ months: "个月",
140
+ year: "",
141
+ years: "",
142
+ ago: "",
132
143
  };
133
-
144
+
134
145
  for (const [en, zh] of Object.entries(timeMap)) {
135
- result = result.replace(new RegExp(`\\b${en}\\b`, 'g'), zh);
146
+ result = result.replace(new RegExp(`\\b${en}\\b`, "g"), zh);
136
147
  }
137
-
148
+
138
149
  // 去掉数字和单位之间的空格,以及单位和"前"之间的空格
139
150
  // 例如:"22 分钟 前" -> "22分钟前"
140
- result = result.replace(/(\d+)\s+(秒|分钟|小时|天|周|个月|年)\s+前/g, '$1$2前');
141
-
151
+ result = result.replace(
152
+ /(\d+)\s+(秒|分钟|小时|天|周|个月|年)\s+前/g,
153
+ "$1$2前"
154
+ );
155
+
142
156
  // 简化显示格式
143
157
  const match = result.match(/(\d+)(分钟|小时|天|周|个月|年)前/);
144
158
  if (match) {
145
159
  const num = parseInt(match[1]);
146
160
  const unit = match[2];
147
-
161
+
148
162
  // 超过60分钟显示小时
149
- if (unit === '分钟' && num >= 60) {
163
+ if (unit === "分钟" && num >= 60) {
150
164
  const hours = Math.floor(num / 60);
151
165
  return `${hours}小时前`;
152
166
  }
153
-
167
+
154
168
  // 超过24小时显示天数
155
- if (unit === '小时' && num >= 24) {
169
+ if (unit === "小时" && num >= 24) {
156
170
  const days = Math.floor(num / 24);
157
171
  return `${days}天前`;
158
172
  }
159
-
173
+
160
174
  // 超过7天显示周数
161
- if (unit === '' && num >= 7 && num < 30) {
175
+ if (unit === "" && num >= 7 && num < 30) {
162
176
  const weeks = Math.floor(num / 7);
163
177
  return `${weeks}周前`;
164
178
  }
165
-
179
+
166
180
  // 超过30天显示月数
167
- if (unit === '' && num >= 30) {
181
+ if (unit === "" && num >= 30) {
168
182
  const months = Math.floor(num / 30);
169
183
  return `${months}个月前`;
170
184
  }
171
-
185
+
172
186
  // 超过4周显示月数
173
- if (unit === '' && num >= 4) {
187
+ if (unit === "" && num >= 4) {
174
188
  const months = Math.floor(num / 4);
175
189
  return `${months}个月前`;
176
190
  }
177
-
191
+
178
192
  // 超过12个月显示年数
179
- if (unit === '个月' && num >= 12) {
193
+ if (unit === "个月" && num >= 12) {
180
194
  const years = Math.floor(num / 12);
181
195
  return `${years}年前`;
182
196
  }
183
197
  }
184
-
198
+
185
199
  return result;
186
200
  }
187
201
 
188
202
  /**
189
203
  * 解析提交主题,分离标题和子任务
190
204
  */
191
- function parseCommitSubject(subject: string): { title: string; tasks: string[] } {
205
+ function parseCommitSubject(subject: string): {
206
+ title: string;
207
+ tasks: string[];
208
+ } {
192
209
  // 检查是否包含 " - " 分隔的子任务
193
- if (subject.includes(' - ')) {
194
- const parts = subject.split(' - ');
210
+ if (subject.includes(" - ")) {
211
+ const parts = subject.split(" - ");
195
212
  const title = parts[0].trim();
196
- const tasks = parts.slice(1).map(task => task.trim()).filter(task => task.length > 0);
213
+ const tasks = parts
214
+ .slice(1)
215
+ .map((task) => task.trim())
216
+ .filter((task) => task.length > 0);
197
217
  return { title, tasks };
198
218
  }
199
-
219
+
200
220
  return { title: subject, tasks: [] };
201
221
  }
202
222
 
@@ -213,137 +233,146 @@ function supportsColor(): boolean {
213
233
  */
214
234
  function formatTimelineStyle(commits: CommitInfo[]): string {
215
235
  const groupedCommits = groupCommitsByDate(commits);
216
- let output = '';
217
-
218
- // 按日期倒序排列
219
- const sortedDates = Array.from(groupedCommits.keys()).sort((a, b) =>
220
- new Date(b).getTime() - new Date(a).getTime()
221
- );
222
-
236
+ let output = "";
237
+
238
+ // 保持原始提交顺序(已在 groupCommitsByDate 中按出现顺序排列)
239
+ const sortedDates = Array.from(groupedCommits.keys());
240
+
223
241
  const useColors = supportsColor() || process.env.FORCE_COLOR;
224
-
242
+
225
243
  for (let dateIndex = 0; dateIndex < sortedDates.length; dateIndex++) {
226
244
  const date = sortedDates[dateIndex];
227
245
  const dateCommits = groupedCommits.get(date)!;
228
-
246
+
229
247
  // 日期标题 - 使用黄色突出显示
230
248
  const dateTitle = `📅 Commits on ${date}`;
231
249
  if (useColors) {
232
- output += '\n' + colors.bold(colors.yellow(dateTitle)) + '\n\n';
250
+ output += colors.bold(colors.yellow(dateTitle)) + "\n";
233
251
  } else {
234
- output += '\n' + dateTitle + '\n\n';
252
+ output += dateTitle + "\n";
235
253
  }
236
-
254
+
237
255
  // 该日期下的提交
238
256
  for (let commitIndex = 0; commitIndex < dateCommits.length; commitIndex++) {
239
257
  const commit = dateCommits[commitIndex];
240
- const icon = getCommitTypeIcon(commit.subject);
241
258
  const { title, tasks } = parseCommitSubject(commit.subject);
242
-
259
+
243
260
  // 构建提交内容
244
261
  const commitContent = [];
245
-
246
- // 主标题 - 使用白色加粗
262
+
263
+ // 主标题 - 使用白色加粗,保持原始提交信息
247
264
  if (useColors) {
248
- commitContent.push(`${icon} ${colors.bold(colors.white(title))}`);
265
+ commitContent.push(colors.bold(colors.white(title)));
249
266
  } else {
250
- commitContent.push(`${icon} ${title}`);
267
+ commitContent.push(title);
251
268
  }
252
-
269
+
253
270
  // 如果有子任务,添加子任务列表
254
271
  if (tasks.length > 0) {
255
- commitContent.push(''); // 空行分隔
256
- tasks.forEach(task => {
272
+ commitContent.push(""); // 空行分隔
273
+ tasks.forEach((task) => {
257
274
  if (useColors) {
258
- commitContent.push(` ${colors.dim('')} ${colors.dim(task)}`);
275
+ commitContent.push(` ${colors.dim("")} ${colors.dim(task)}`);
259
276
  } else {
260
277
  commitContent.push(` – ${task}`);
261
278
  }
262
279
  });
263
280
  }
264
-
281
+
265
282
  // 空行分隔
266
- commitContent.push('');
267
-
283
+ commitContent.push("");
284
+
268
285
  // 作者和时间信息
269
286
  if (useColors) {
270
- commitContent.push(`${colors.dim('👤')} ${colors.blue(commit.author)} ${colors.dim('committed')} ${colors.green(formatRelativeTime(commit.relativeDate))}`);
271
- // Hash信息 - 使用橙色
272
- commitContent.push(`${colors.dim('🔗')} ${colors.orange('#' + commit.shortHash)}`);
287
+ commitContent.push(
288
+ `${colors.dim("👤")} ${colors.blue(commit.author)} ${colors.dim(
289
+ "committed"
290
+ )} ${colors.green(formatRelativeTime(commit.relativeDate))}`
291
+ );
292
+ // Hash信息 - 使用橙色,显示完整 hash
293
+ commitContent.push(`${colors.dim("🔗")} ${colors.orange(commit.hash)}`);
273
294
  // 如果有分支/标签信息 - 区分显示
274
295
  if (commit.refs && commit.refs.trim()) {
275
296
  const refs = commit.refs.trim();
276
297
  // 解析并分别显示分支和标签
277
- const refParts = refs.split(', ');
298
+ const refParts = refs.split(", ");
278
299
  const branches: string[] = [];
279
300
  const tags: string[] = [];
280
-
281
- refParts.forEach(ref => {
282
- if (ref.startsWith('tag: ')) {
283
- tags.push(ref.replace('tag: ', ''));
284
- } else if (ref.includes('/') || ref === 'HEAD') {
301
+
302
+ refParts.forEach((ref) => {
303
+ if (ref.startsWith("tag: ")) {
304
+ tags.push(ref.replace("tag: ", ""));
305
+ } else if (ref.includes("/") || ref === "HEAD") {
285
306
  branches.push(ref);
286
307
  } else {
287
308
  branches.push(ref);
288
309
  }
289
310
  });
290
-
311
+
291
312
  // 显示分支信息
292
313
  if (branches.length > 0) {
293
- commitContent.push(`${colors.dim('🌿')} ${colors.lightPurple(branches.join(', '))}`);
314
+ commitContent.push(
315
+ `${colors.dim("🌿")} ${colors.lightPurple(branches.join(", "))}`
316
+ );
294
317
  }
295
-
318
+
296
319
  // 显示标签信息
297
320
  if (tags.length > 0) {
298
- const tagText = tags.map(tag => `tag ${tag}`).join(', ');
299
- commitContent.push(`${colors.dim('🔖')} ${colors.yellow(tagText)}`);
321
+ const tagText = tags.map((tag) => `tag ${tag}`).join(", ");
322
+ commitContent.push(
323
+ `${colors.dim("🔖")} ${colors.brightYellow(tagText)}`
324
+ );
300
325
  }
301
326
  }
302
327
  } else {
303
- commitContent.push(`👤 ${commit.author} committed ${formatRelativeTime(commit.relativeDate)}`);
304
- commitContent.push(`🔗 #${commit.shortHash}`);
328
+ commitContent.push(
329
+ `👤 ${commit.author} committed ${formatRelativeTime(
330
+ commit.relativeDate
331
+ )}`
332
+ );
333
+ commitContent.push(`🔗 ${commit.hash}`);
305
334
  if (commit.refs && commit.refs.trim()) {
306
335
  const refs = commit.refs.trim();
307
336
  // 解析并分别显示分支和标签
308
- const refParts = refs.split(', ');
337
+ const refParts = refs.split(", ");
309
338
  const branches: string[] = [];
310
339
  const tags: string[] = [];
311
-
312
- refParts.forEach(ref => {
313
- if (ref.startsWith('tag: ')) {
314
- tags.push(ref.replace('tag: ', ''));
315
- } else if (ref.includes('/') || ref === 'HEAD') {
340
+
341
+ refParts.forEach((ref) => {
342
+ if (ref.startsWith("tag: ")) {
343
+ tags.push(ref.replace("tag: ", ""));
344
+ } else if (ref.includes("/") || ref === "HEAD") {
316
345
  branches.push(ref);
317
346
  } else {
318
347
  branches.push(ref);
319
348
  }
320
349
  });
321
-
350
+
322
351
  // 显示分支信息
323
352
  if (branches.length > 0) {
324
- commitContent.push(`🌿 ${branches.join(', ')}`);
353
+ commitContent.push(`🌿 ${branches.join(", ")}`);
325
354
  }
326
-
355
+
327
356
  // 显示标签信息
328
357
  if (tags.length > 0) {
329
- const tagText = tags.map(tag => `tag ${tag}`).join(', ');
358
+ const tagText = tags.map((tag) => `tag ${tag}`).join(", ");
330
359
  commitContent.push(`🔖 ${tagText}`);
331
360
  }
332
361
  }
333
362
  }
334
-
363
+
335
364
  // 使用boxen
336
- const commitBox = boxen(commitContent.join('\n'), {
365
+ const commitBox = boxen(commitContent.join("\n"), {
337
366
  padding: { top: 0, bottom: 0, left: 1, right: 1 },
338
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
339
- borderStyle: 'round',
340
- borderColor: 'gray'
367
+ margin: { top: 0, bottom: 0, left: 0, right: 0 },
368
+ borderStyle: "round",
369
+ borderColor: "gray",
341
370
  });
342
-
343
- output += commitBox + '\n';
371
+
372
+ output += commitBox + "\n";
344
373
  }
345
374
  }
346
-
375
+
347
376
  return output;
348
377
  }
349
378
 
@@ -352,34 +381,42 @@ function formatTimelineStyle(commits: CommitInfo[]): string {
352
381
  */
353
382
  function startInteractivePager(content: string): void {
354
383
  // 使用系统的 less 命令作为分页器,启用颜色支持
355
- const pager = process.env.PAGER || 'less';
356
-
384
+ const pager = process.env.PAGER || "less";
385
+
357
386
  try {
358
387
  // -R: 支持ANSI颜色代码
359
388
  // -S: 不换行长行
360
389
  // -F: 如果内容少于一屏则直接退出
361
390
  // -X: 不清屏
362
391
  // -i: 忽略大小写搜索
363
- const pagerProcess = spawn(pager, ['-R', '-S', '-F', '-X', '-i'], {
364
- stdio: ['pipe', 'inherit', 'inherit'],
365
- env: { ...process.env, LESS: '-R -S -F -X -i' }
392
+ const pagerProcess = spawn(pager, ["-R", "-S", "-F", "-X", "-i"], {
393
+ stdio: ["pipe", "inherit", "inherit"],
394
+ env: { ...process.env, LESS: "-R -S -F -X -i" },
366
395
  });
367
-
396
+
397
+ // 处理 stdin 的 EPIPE 错误(当 less 提前退出时)
398
+ pagerProcess.stdin.on("error", (err: NodeJS.ErrnoException) => {
399
+ if (err.code !== "EPIPE") {
400
+ // 只忽略 EPIPE 错误,其他错误输出
401
+ console.error(err);
402
+ }
403
+ });
404
+
368
405
  // 将内容写入分页器
369
406
  pagerProcess.stdin.write(content);
370
407
  pagerProcess.stdin.end();
371
-
408
+
372
409
  // 处理分页器退出
373
- pagerProcess.on('exit', () => {
374
- // 分页器退出后不需要额外处理
410
+ pagerProcess.on("exit", () => {
411
+ // 分页器退出后正常退出程序
412
+ process.exit(0);
375
413
  });
376
-
414
+
377
415
  // 处理错误
378
- pagerProcess.on('error', (err) => {
416
+ pagerProcess.on("error", () => {
379
417
  // 如果分页器启动失败,直接输出内容
380
418
  console.log(content);
381
419
  });
382
-
383
420
  } catch (error) {
384
421
  // 如果出错,直接输出内容
385
422
  console.log(content);
@@ -393,7 +430,7 @@ function executeTimelineLog(options: LogOptions): void {
393
430
  try {
394
431
  // 构建Git命令
395
432
  let cmd = 'git log --pretty=format:"%H|%h|%s|%an|%ad|%ar|%D" --date=short';
396
-
433
+
397
434
  // 添加选项
398
435
  if (options.limit && !options.interactive) cmd += ` -${options.limit}`;
399
436
  if (options.author) cmd += ` --author="${options.author}"`;
@@ -401,53 +438,41 @@ function executeTimelineLog(options: LogOptions): void {
401
438
  if (options.until) cmd += ` --until="${options.until}"`;
402
439
  if (options.grep) cmd += ` --grep="${options.grep}"`;
403
440
  if (options.all) cmd += ` --all`;
404
-
405
- // 交互式模式默认显示更多提交
406
- if (options.interactive && !options.limit) {
407
- cmd += ` -50`; // 默认显示50个提交
408
- }
409
441
 
410
- const output = execSync(cmd, {
411
- encoding: 'utf8',
412
- stdio: 'pipe',
413
- maxBuffer: 1024 * 1024 * 10
442
+ // 交互式模式默认显示全部提交(不限制数量)
443
+
444
+ const output = execSync(cmd, {
445
+ encoding: "utf8",
446
+ stdio: "pipe",
447
+ maxBuffer: 1024 * 1024 * 10,
414
448
  });
415
449
 
416
450
  if (output.trim()) {
417
451
  const commits = parseGitLog(output);
418
-
452
+
419
453
  // 构建完整输出
420
- let fullOutput = '';
421
-
422
- // 显示标题
423
- const title = `📊 共显示 ${commits.length} 个提交`;
424
- fullOutput += '\n' + boxen(title, {
425
- padding: { top: 0, bottom: 0, left: 2, right: 2 },
426
- margin: { top: 0, bottom: 1, left: 0, right: 0 },
427
- borderStyle: 'double',
428
- borderColor: 'green',
429
- textAlignment: 'center'
430
- }) + '\n';
431
-
454
+ let fullOutput = "";
455
+
432
456
  // 显示时间线
433
457
  const timelineOutput = formatTimelineStyle(commits);
434
458
  fullOutput += timelineOutput;
435
-
459
+
436
460
  // 根据是否交互式模式选择输出方式
437
461
  if (options.interactive) {
438
462
  startInteractivePager(fullOutput);
439
463
  } else {
440
464
  console.log(fullOutput);
441
465
  }
442
-
443
466
  } else {
444
- const noCommitsMsg = '\n' + boxen('📭 没有找到匹配的提交记录', {
445
- padding: { top: 0, bottom: 0, left: 2, right: 2 },
446
- borderStyle: 'round',
447
- borderColor: 'yellow',
448
- textAlignment: 'center'
449
- });
450
-
467
+ const noCommitsMsg =
468
+ "\n" +
469
+ boxen("📭 没有找到匹配的提交记录", {
470
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
471
+ borderStyle: "round",
472
+ borderColor: "yellow",
473
+ textAlignment: "center",
474
+ });
475
+
451
476
  if (options.interactive) {
452
477
  startInteractivePager(noCommitsMsg);
453
478
  } else {
@@ -455,20 +480,22 @@ function executeTimelineLog(options: LogOptions): void {
455
480
  }
456
481
  }
457
482
  } catch (error: any) {
458
- let errorMessage = '❌ 执行失败';
483
+ let errorMessage = "❌ 执行失败";
459
484
  if (error.status === 128) {
460
- errorMessage = '❌ Git仓库错误或没有提交记录';
485
+ errorMessage = "❌ Git仓库错误或没有提交记录";
461
486
  } else {
462
487
  errorMessage = `❌ 执行失败: ${error.message}`;
463
488
  }
464
-
465
- const errorBox = '\n' + boxen(errorMessage, {
466
- padding: { top: 0, bottom: 0, left: 2, right: 2 },
467
- borderStyle: 'round',
468
- borderColor: 'red',
469
- textAlignment: 'center'
470
- });
471
-
489
+
490
+ const errorBox =
491
+ "\n" +
492
+ boxen(errorMessage, {
493
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
494
+ borderStyle: "round",
495
+ borderColor: "red",
496
+ textAlignment: "center",
497
+ });
498
+
472
499
  if (options.interactive) {
473
500
  startInteractivePager(errorBox);
474
501
  } else {
@@ -485,12 +512,12 @@ export async function log(options: LogOptions = {}): Promise<void> {
485
512
  if (options.interactive === undefined) {
486
513
  options.interactive = true;
487
514
  }
488
-
515
+
489
516
  // 交互式模式下不设置默认limit
490
517
  if (!options.interactive && !options.limit) {
491
518
  options.limit = 10;
492
519
  }
493
-
520
+
494
521
  executeTimelineLog(options);
495
522
  }
496
523
 
@@ -500,4 +527,4 @@ export async function log(options: LogOptions = {}): Promise<void> {
500
527
  export async function quickLog(limit: number = 10): Promise<void> {
501
528
  const options: LogOptions = { limit };
502
529
  executeTimelineLog(options);
503
- }
530
+ }
package/src/index.ts CHANGED
@@ -356,11 +356,11 @@ cli
356
356
  .action(async (options: any) => {
357
357
  await checkForUpdates(version, "@zjex/git-workflow");
358
358
  checkGitRepo();
359
-
359
+
360
360
  // 构建选项对象 - 默认交互式模式
361
361
  const logOptions: any = { interactive: true };
362
362
  if (options.limit) logOptions.limit = parseInt(options.limit);
363
-
363
+
364
364
  return log(logOptions);
365
365
  });
366
366
 
package/src/utils.ts CHANGED
@@ -12,6 +12,7 @@ export interface Colors {
12
12
  orange: (s: string) => string;
13
13
  lightPurple: (s: string) => string;
14
14
  white: (s: string) => string;
15
+ brightYellow: (s: string) => string;
15
16
  reset: string;
16
17
  }
17
18
 
@@ -27,6 +28,7 @@ export const colors: Colors = {
27
28
  orange: (s) => `\x1b[38;5;208m${s}\x1b[0m`,
28
29
  lightPurple: (s) => `\x1b[38;5;141m${s}\x1b[0m`,
29
30
  white: (s) => `\x1b[37m${s}\x1b[0m`,
31
+ brightYellow: (s) => `\x1b[93m${s}\x1b[0m`, // 亮黄色
30
32
  reset: "\x1b[0m",
31
33
  };
32
34
 
package/tsup.config.ts CHANGED
@@ -20,7 +20,7 @@ export default defineConfig({
20
20
  "cac",
21
21
  ],
22
22
  banner: {
23
- js: "#!/usr/bin/env -S node --no-warnings=ExperimentalWarning",
23
+ js: "#!/usr/bin/env -S node --disable-warning=ExperimentalWarning",
24
24
  },
25
25
  define: {
26
26
  __VERSION__: JSON.stringify(pkg.version),