@zjex/git-workflow 0.2.24 → 0.3.2

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 (99) hide show
  1. package/.github/workflows/deploy-docs.yml +68 -0
  2. package/.github/workflows/test.yml +24 -4
  3. package/.husky/pre-commit +17 -0
  4. package/README.md +72 -1066
  5. package/ROADMAP.md +275 -0
  6. package/dist/index.js +450 -99
  7. package/docs/.vitepress/cache/deps/_metadata.json +52 -0
  8. package/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js +9719 -0
  9. package/docs/.vitepress/cache/deps/chunk-2CLQ7TTZ.js.map +7 -0
  10. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js +12824 -0
  11. package/docs/.vitepress/cache/deps/chunk-LE5NDSFD.js.map +7 -0
  12. package/docs/.vitepress/cache/deps/package.json +3 -0
  13. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js +4505 -0
  14. package/docs/.vitepress/cache/deps/vitepress___@vue_devtools-api.js.map +7 -0
  15. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js +583 -0
  16. package/docs/.vitepress/cache/deps/vitepress___@vueuse_core.js.map +7 -0
  17. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js +1352 -0
  18. package/docs/.vitepress/cache/deps/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  19. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js +1665 -0
  20. package/docs/.vitepress/cache/deps/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  21. package/docs/.vitepress/cache/deps/vitepress___minisearch.js +1813 -0
  22. package/docs/.vitepress/cache/deps/vitepress___minisearch.js.map +7 -0
  23. package/docs/.vitepress/cache/deps/vue.js +347 -0
  24. package/docs/.vitepress/cache/deps/vue.js.map +7 -0
  25. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js +9719 -0
  26. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-2CLQ7TTZ.js.map +7 -0
  27. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js +12824 -0
  28. package/docs/.vitepress/cache/deps_temp_44e2fb0f/chunk-LE5NDSFD.js.map +7 -0
  29. package/docs/.vitepress/cache/deps_temp_44e2fb0f/package.json +3 -0
  30. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js +4505 -0
  31. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vue_devtools-api.js.map +7 -0
  32. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js +583 -0
  33. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_core.js.map +7 -0
  34. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js +1352 -0
  35. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___@vueuse_integrations_useFocusTrap.js.map +7 -0
  36. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js +1665 -0
  37. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___mark__js_src_vanilla__js.js.map +7 -0
  38. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js +1813 -0
  39. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vitepress___minisearch.js.map +7 -0
  40. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js +347 -0
  41. package/docs/.vitepress/cache/deps_temp_44e2fb0f/vue.js.map +7 -0
  42. package/docs/.vitepress/config.ts +167 -0
  43. package/docs/.vitepress/theme/custom.css +39 -0
  44. package/docs/.vitepress/theme/index.ts +4 -0
  45. package/docs/README.md +82 -0
  46. package/docs/commands/branch.md +468 -0
  47. package/docs/commands/commit.md +554 -0
  48. package/docs/commands/config.md +346 -0
  49. package/docs/commands/index.md +312 -0
  50. package/docs/commands/interactive.md +384 -0
  51. package/docs/commands/release.md +300 -0
  52. package/docs/commands/stash.md +309 -0
  53. package/docs/commands/tag.md +278 -0
  54. package/docs/commands/update.md +347 -0
  55. package/docs/config/ai-config.md +160 -0
  56. package/docs/config/branch-config.md +133 -0
  57. package/docs/config/commit-config.md +185 -0
  58. package/docs/config/config-file.md +776 -0
  59. package/docs/config/examples.md +279 -0
  60. package/docs/config/index.md +478 -0
  61. package/docs/features/git-wrapped.md +199 -0
  62. package/docs/guide/ai-commit.md +576 -0
  63. package/docs/guide/basic-usage.md +522 -0
  64. package/docs/guide/best-practices.md +426 -0
  65. package/docs/guide/branch-management.md +712 -0
  66. package/docs/guide/getting-started.md +294 -0
  67. package/docs/guide/index.md +168 -0
  68. package/docs/guide/installation.md +449 -0
  69. package/docs/guide/release-management.md +744 -0
  70. package/docs/guide/stash-management.md +608 -0
  71. package/docs/guide/tag-management.md +614 -0
  72. package/docs/index.md +205 -0
  73. package/docs/public/favicon.svg +21 -0
  74. package/docs/public/hero-logo.svg +43 -0
  75. package/docs/public/logo.svg +20 -0
  76. package/package.json +12 -2
  77. package/scripts/publish.js +55 -8
  78. package/scripts/publish.sh +20 -2
  79. package/scripts/release.sh +81 -3
  80. package/scripts/update-test-count.js +55 -0
  81. package/src/ai-service.ts +107 -15
  82. package/src/commands/commit.ts +4 -0
  83. package/src/commands/init.ts +18 -0
  84. package/src/commands/log.ts +503 -0
  85. package/src/config.ts +1 -0
  86. package/src/index.ts +37 -13
  87. package/src/utils.ts +10 -0
  88. package/tests/ai-service.test.ts +237 -2
  89. package/tests/init.test.ts +582 -0
  90. package/tests/log.test.ts +106 -0
  91. package/tests/release.test.ts +333 -0
  92. package/tests/setup.ts +21 -0
  93. package/tests/stash.test.ts +376 -0
  94. package/tests/update.test.ts +402 -0
  95. package/vitest.config.ts +3 -0
  96. package/zjex-logo.svg +22 -0
  97. package/zjex-optimized.svg +34 -0
  98. package/zjex.svg +1 -0
  99. package/src/commands/help.ts +0 -76
package/dist/index.js CHANGED
@@ -59,9 +59,14 @@ var init_utils = __esm({
59
59
  red: (s) => `\x1B[31m${s}\x1B[0m`,
60
60
  green: (s) => `\x1B[32m${s}\x1B[0m`,
61
61
  yellow: (s) => `\x1B[33m${s}\x1B[0m`,
62
+ blue: (s) => `\x1B[34m${s}\x1B[0m`,
62
63
  cyan: (s) => `\x1B[36m${s}\x1B[0m`,
63
64
  dim: (s) => `\x1B[2m${s}\x1B[0m`,
64
65
  bold: (s) => `\x1B[1m${s}\x1B[0m`,
66
+ purple: (s) => `\x1B[35m${s}\x1B[0m`,
67
+ orange: (s) => `\x1B[38;5;208m${s}\x1B[0m`,
68
+ lightPurple: (s) => `\x1B[38;5;141m${s}\x1B[0m`,
69
+ white: (s) => `\x1B[37m${s}\x1B[0m`,
65
70
  reset: "\x1B[0m"
66
71
  };
67
72
  TODAY = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
@@ -1442,11 +1447,28 @@ async function init() {
1442
1447
  ],
1443
1448
  theme
1444
1449
  });
1450
+ const detailedDescription = await select4({
1451
+ message: "\u662F\u5426\u751F\u6210\u8BE6\u7EC6\u7684\u4FEE\u6539\u70B9\u63CF\u8FF0?",
1452
+ choices: [
1453
+ {
1454
+ name: "\u662F\uFF08\u5305\u542B\u4FEE\u6539\u70B9\u5217\u8868\uFF0C\u63A8\u8350\uFF09",
1455
+ value: true,
1456
+ description: "\u5982\uFF1Afeat(auth): \u6DFB\u52A0\u7528\u6237\u767B\u5F55\u529F\u80FD\n\n- \u5B9E\u73B0\u7528\u6237\u540D\u5BC6\u7801\u767B\u5F55\u63A5\u53E3\n- \u6DFB\u52A0\u767B\u5F55\u72B6\u6001\u9A8C\u8BC1\u4E2D\u95F4\u4EF6"
1457
+ },
1458
+ {
1459
+ name: "\u5426\uFF08\u4EC5\u751F\u6210\u6807\u9898\uFF09",
1460
+ value: false,
1461
+ description: "\u5982\uFF1Afeat(auth): \u6DFB\u52A0\u7528\u6237\u767B\u5F55\u529F\u80FD"
1462
+ }
1463
+ ],
1464
+ theme
1465
+ });
1445
1466
  config2.aiCommit = {
1446
1467
  enabled: true,
1447
1468
  provider: aiProvider,
1448
1469
  apiKey: apiKey || void 0,
1449
- language
1470
+ language,
1471
+ detailedDescription
1450
1472
  };
1451
1473
  const defaultModels = {
1452
1474
  github: "gpt-4o-mini",
@@ -1790,9 +1812,62 @@ function getGitDiff() {
1790
1812
  return "";
1791
1813
  }
1792
1814
  }
1793
- function buildPrompt(diff, language) {
1815
+ function buildPrompt(diff, language, detailedDescription = false) {
1794
1816
  const isZh = language === "zh-CN";
1795
- const systemPrompt = isZh ? `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 Git commit message \u751F\u6210\u52A9\u624B\u3002\u8BF7\u6839\u636E\u63D0\u4F9B\u7684 git diff \u751F\u6210\u7B26\u5408 Conventional Commits \u89C4\u8303\u7684 commit message\u3002
1817
+ if (detailedDescription) {
1818
+ const systemPrompt = isZh ? `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 Git commit message \u751F\u6210\u52A9\u624B\u3002\u8BF7\u6839\u636E\u63D0\u4F9B\u7684 git diff \u751F\u6210\u7B26\u5408 Conventional Commits \u89C4\u8303\u7684\u8BE6\u7EC6 commit message\u3002
1819
+
1820
+ \u683C\u5F0F\u8981\u6C42\uFF1A
1821
+ 1. \u7B2C\u4E00\u884C\uFF1A<type>(<scope>): <subject>
1822
+ 2. \u7A7A\u884C
1823
+ 3. \u8BE6\u7EC6\u63CF\u8FF0\uFF1A\u5217\u51FA\u4E3B\u8981\u4FEE\u6539\u70B9\uFF0C\u6BCF\u4E2A\u4FEE\u6539\u70B9\u4E00\u884C\uFF0C\u4EE5 "- " \u5F00\u5934
1824
+
1825
+ \u89C4\u5219\uFF1A
1826
+ - type \u5FC5\u987B\u662F\u4EE5\u4E0B\u4E4B\u4E00\uFF1Afeat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
1827
+ - scope \u662F\u53EF\u9009\u7684\uFF0C\u8868\u793A\u5F71\u54CD\u8303\u56F4
1828
+ - subject \u7528\u4E2D\u6587\u63CF\u8FF0\uFF0C\u7B80\u6D01\u660E\u4E86\uFF0C\u4E0D\u8D85\u8FC7 50 \u5B57
1829
+ - \u8BE6\u7EC6\u63CF\u8FF0\u8981\u5217\u51FA 3-6 \u4E2A\u4E3B\u8981\u4FEE\u6539\u70B9\uFF0C\u6BCF\u4E2A\u4FEE\u6539\u70B9\u7B80\u6D01\u660E\u4E86
1830
+ - \u5982\u679C\u4FEE\u6539\u8F83\u5C11\uFF0C\u53EF\u4EE5\u53EA\u5217\u51FA 2-3 \u4E2A\u4FEE\u6539\u70B9
1831
+ - \u4E0D\u8981\u6709\u5176\u4ED6\u89E3\u91CA\u6216\u591A\u4F59\u5185\u5BB9
1832
+
1833
+ \u793A\u4F8B\uFF1A
1834
+ feat(auth): \u6DFB\u52A0\u7528\u6237\u767B\u5F55\u529F\u80FD
1835
+
1836
+ - \u5B9E\u73B0\u7528\u6237\u540D\u5BC6\u7801\u767B\u5F55\u63A5\u53E3
1837
+ - \u6DFB\u52A0\u767B\u5F55\u72B6\u6001\u9A8C\u8BC1\u4E2D\u95F4\u4EF6
1838
+ - \u5B8C\u5584\u767B\u5F55\u9519\u8BEF\u5904\u7406\u903B\u8F91
1839
+ - \u66F4\u65B0\u7528\u6237\u8BA4\u8BC1\u76F8\u5173\u6587\u6863` : `You are a professional Git commit message generator. Generate a detailed commit message following Conventional Commits specification based on the provided git diff.
1840
+
1841
+ Format requirements:
1842
+ 1. First line: <type>(<scope>): <subject>
1843
+ 2. Empty line
1844
+ 3. Detailed description: List main changes, one per line, starting with "- "
1845
+
1846
+ Rules:
1847
+ - type must be one of: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert
1848
+ - scope is optional, indicates the affected area
1849
+ - subject should be concise, no more than 50 characters
1850
+ - Detailed description should list 3-6 main changes, each change should be concise
1851
+ - If changes are minimal, list 2-3 changes
1852
+ - No explanations or extra content
1853
+
1854
+ Example:
1855
+ feat(auth): add user login functionality
1856
+
1857
+ - Implement username/password login API
1858
+ - Add login status validation middleware
1859
+ - Improve login error handling logic
1860
+ - Update user authentication documentation`;
1861
+ const userPrompt = isZh ? `\u8BF7\u6839\u636E\u4EE5\u4E0B git diff \u751F\u6210\u8BE6\u7EC6\u7684 commit message\uFF08\u5305\u542B\u4FEE\u6539\u70B9\u5217\u8868\uFF09\uFF1A
1862
+
1863
+ ${diff}` : `Generate a detailed commit message (including change list) based on the following git diff:
1864
+
1865
+ ${diff}`;
1866
+ return `${systemPrompt}
1867
+
1868
+ ${userPrompt}`;
1869
+ } else {
1870
+ const systemPrompt = isZh ? `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684 Git commit message \u751F\u6210\u52A9\u624B\u3002\u8BF7\u6839\u636E\u63D0\u4F9B\u7684 git diff \u751F\u6210\u7B26\u5408 Conventional Commits \u89C4\u8303\u7684 commit message\u3002
1796
1871
 
1797
1872
  \u89C4\u5219\uFF1A
1798
1873
  1. \u683C\u5F0F\uFF1A<type>(<scope>): <subject>
@@ -1819,14 +1894,15 @@ Examples:
1819
1894
  - feat(auth): add user login functionality
1820
1895
  - fix(api): resolve data fetching failure
1821
1896
  - docs(readme): update installation guide`;
1822
- const userPrompt = isZh ? `\u8BF7\u6839\u636E\u4EE5\u4E0B git diff \u751F\u6210 commit message\uFF1A
1897
+ const userPrompt = isZh ? `\u8BF7\u6839\u636E\u4EE5\u4E0B git diff \u751F\u6210 commit message\uFF1A
1823
1898
 
1824
1899
  ${diff}` : `Generate a commit message based on the following git diff:
1825
1900
 
1826
1901
  ${diff}`;
1827
- return `${systemPrompt}
1902
+ return `${systemPrompt}
1828
1903
 
1829
1904
  ${userPrompt}`;
1905
+ }
1830
1906
  }
1831
1907
  async function callGitHubAPI(prompt, apiKey, model, maxTokens) {
1832
1908
  const response = await fetch(AI_PROVIDERS.github.endpoint, {
@@ -1921,18 +1997,33 @@ async function callOllamaAPI(prompt, model, maxTokens) {
1921
1997
  );
1922
1998
  }
1923
1999
  }
2000
+ function cleanAIResponse(response) {
2001
+ let cleaned = response.replace(/^[.\s`~-]+/, "").trim();
2002
+ cleaned = cleaned.replace(/[.\s`~-]+$/, "").trim();
2003
+ const lines = cleaned.split("\n").map((line) => line.trim()).filter((line) => line);
2004
+ const uniqueLines = [];
2005
+ const seen = /* @__PURE__ */ new Set();
2006
+ for (const line of lines) {
2007
+ if (!seen.has(line)) {
2008
+ seen.add(line);
2009
+ uniqueLines.push(line);
2010
+ }
2011
+ }
2012
+ return uniqueLines.join("\n");
2013
+ }
1924
2014
  async function generateAICommitMessage(config2) {
1925
2015
  const aiConfig = config2.aiCommit || {};
1926
2016
  const provider = aiConfig.provider || "github";
1927
2017
  const language = aiConfig.language || "zh-CN";
1928
- const maxTokens = aiConfig.maxTokens || 200;
2018
+ const detailedDescription = aiConfig.detailedDescription !== false;
2019
+ const maxTokens = aiConfig.maxTokens || (detailedDescription ? 400 : 200);
1929
2020
  const diff = getGitDiff();
1930
2021
  if (!diff) {
1931
2022
  throw new Error("\u6CA1\u6709\u68C0\u6D4B\u5230\u4EE3\u7801\u66F4\u6539");
1932
2023
  }
1933
- const maxDiffLength = 4e3;
2024
+ const maxDiffLength = detailedDescription ? 6e3 : 4e3;
1934
2025
  const truncatedDiff = diff.length > maxDiffLength ? diff.slice(0, maxDiffLength) + "\n..." : diff;
1935
- const prompt = buildPrompt(truncatedDiff, language);
2026
+ const prompt = buildPrompt(truncatedDiff, language, detailedDescription);
1936
2027
  const providerInfo = AI_PROVIDERS[provider];
1937
2028
  if (!providerInfo) {
1938
2029
  throw new Error(`\u4E0D\u652F\u6301\u7684 AI \u63D0\u4F9B\u5546: ${provider}`);
@@ -1944,18 +2035,24 @@ async function generateAICommitMessage(config2) {
1944
2035
  `${providerInfo.name} \u9700\u8981 API key\u3002\u8BF7\u8FD0\u884C 'gw init' \u914D\u7F6E AI commit\uFF0C\u6216\u5728 .gwrc.json \u4E2D\u8BBE\u7F6E aiCommit.apiKey`
1945
2036
  );
1946
2037
  }
2038
+ let response;
1947
2039
  switch (provider) {
1948
2040
  case "github":
1949
- return await callGitHubAPI(prompt, apiKey, model, maxTokens);
2041
+ response = await callGitHubAPI(prompt, apiKey, model, maxTokens);
2042
+ break;
1950
2043
  case "openai":
1951
- return await callOpenAIAPI(prompt, apiKey, model, maxTokens);
2044
+ response = await callOpenAIAPI(prompt, apiKey, model, maxTokens);
2045
+ break;
1952
2046
  case "claude":
1953
- return await callClaudeAPI(prompt, apiKey, model, maxTokens);
2047
+ response = await callClaudeAPI(prompt, apiKey, model, maxTokens);
2048
+ break;
1954
2049
  case "ollama":
1955
- return await callOllamaAPI(prompt, model, maxTokens);
2050
+ response = await callOllamaAPI(prompt, model, maxTokens);
2051
+ break;
1956
2052
  default:
1957
2053
  throw new Error(`\u4E0D\u652F\u6301\u7684 AI \u63D0\u4F9B\u5546: ${provider}`);
1958
2054
  }
2055
+ return cleanAIResponse(response);
1959
2056
  }
1960
2057
  function isAICommitAvailable(config2) {
1961
2058
  const aiConfig = config2.aiCommit;
@@ -2163,6 +2260,7 @@ async function commit() {
2163
2260
  spinner.succeed("\u63D0\u4EA4\u6210\u529F");
2164
2261
  const commitHash = execOutput("git rev-parse --short HEAD");
2165
2262
  console.log(colors.dim(`commit: ${commitHash}`));
2263
+ console.log("");
2166
2264
  } catch (error) {
2167
2265
  spinner.fail("\u63D0\u4EA4\u5931\u8D25");
2168
2266
  console.log("");
@@ -2174,6 +2272,7 @@ async function commit() {
2174
2272
  console.log(colors.yellow("\u4F60\u53EF\u4EE5\u624B\u52A8\u6267\u884C\u4EE5\u4E0B\u547D\u4EE4:"));
2175
2273
  console.log(colors.cyan(` git commit -m "${message}"`));
2176
2274
  console.log("");
2275
+ throw error;
2177
2276
  }
2178
2277
  }
2179
2278
  async function buildManualCommitMessage(config2) {
@@ -2255,83 +2354,6 @@ ${issues}`;
2255
2354
  return message;
2256
2355
  }
2257
2356
 
2258
- // src/commands/help.ts
2259
- init_utils();
2260
- function showHelp() {
2261
- return `
2262
- \u5206\u652F\u547D\u4EE4:
2263
- gw feature [--base <branch>] \u521B\u5EFA feature \u5206\u652F
2264
- gw feat [--base <branch>] \u540C\u4E0A (\u522B\u540D)
2265
- gw f [--base <branch>] \u540C\u4E0A (\u522B\u540D)
2266
-
2267
- gw hotfix [--base <branch>] \u521B\u5EFA hotfix \u5206\u652F
2268
- gw fix [--base <branch>] \u540C\u4E0A (\u522B\u540D)
2269
- gw h [--base <branch>] \u540C\u4E0A (\u522B\u540D)
2270
-
2271
- gw delete [branch] \u5220\u9664\u672C\u5730/\u8FDC\u7A0B\u5206\u652F
2272
- gw del [branch] \u540C\u4E0A (\u522B\u540D)
2273
- gw d [branch] \u540C\u4E0A (\u522B\u540D)
2274
-
2275
- Tag \u547D\u4EE4:
2276
- gw tags [prefix] \u5217\u51FA\u6240\u6709 tag\uFF0C\u53EF\u6309\u524D\u7F00\u8FC7\u6EE4
2277
- gw ts [prefix] \u540C\u4E0A (\u522B\u540D)
2278
-
2279
- gw tag [prefix] \u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u7C7B\u578B\u5E76\u521B\u5EFA tag
2280
- gw t [prefix] \u540C\u4E0A (\u522B\u540D)
2281
-
2282
- gw tag:delete \u5220\u9664 tag
2283
- gw td \u540C\u4E0A (\u522B\u540D)
2284
-
2285
- gw tag:update \u91CD\u547D\u540D tag
2286
- gw tu \u540C\u4E0A (\u522B\u540D)
2287
-
2288
- \u53D1\u5E03\u547D\u4EE4:
2289
- gw release \u4EA4\u4E92\u5F0F\u9009\u62E9\u7248\u672C\u53F7\u5E76\u66F4\u65B0 package.json
2290
- gw r \u540C\u4E0A (\u522B\u540D)
2291
-
2292
- \u914D\u7F6E\u547D\u4EE4:
2293
- gw init \u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6
2294
- \u2022 \u5168\u5C40\u914D\u7F6E: ~/.gwrc.json (\u6240\u6709\u9879\u76EE\u751F\u6548)
2295
- \u2022 \u9879\u76EE\u914D\u7F6E: .gwrc.json (\u4EC5\u5F53\u524D\u9879\u76EE)
2296
- \u8FD0\u884C\u65F6\u53EF\u9009\u62E9\u914D\u7F6E\u8303\u56F4
2297
-
2298
- \u66F4\u65B0\u547D\u4EE4:
2299
- gw update \u68C0\u67E5\u5E76\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C
2300
- gw upt \u540C\u4E0A (\u522B\u540D)
2301
-
2302
- \u6E05\u7406\u547D\u4EE4:
2303
- gw clean \u6E05\u7406\u7F13\u5B58\u6587\u4EF6
2304
-
2305
- Stash \u547D\u4EE4:
2306
- gw stash \u4EA4\u4E92\u5F0F\u7BA1\u7406 stash
2307
- gw s \u540C\u4E0A (\u522B\u540D)
2308
- gw st \u540C\u4E0A (\u522B\u540D)
2309
-
2310
- Commit \u547D\u4EE4:
2311
- gw commit \u4EA4\u4E92\u5F0F\u63D0\u4EA4 (Conventional Commits + Gitmoji)
2312
- gw c \u540C\u4E0A (\u522B\u540D)
2313
- gw cm \u540C\u4E0A (\u522B\u540D)
2314
-
2315
- \u793A\u4F8B:
2316
- gw f \u57FA\u4E8E main/master \u521B\u5EFA feature \u5206\u652F
2317
- gw f --base develop \u57FA\u4E8E develop \u5206\u652F\u521B\u5EFA feature \u5206\u652F
2318
- gw h --base release \u57FA\u4E8E release \u5206\u652F\u521B\u5EFA hotfix \u5206\u652F
2319
- gw d \u4EA4\u4E92\u5F0F\u9009\u62E9\u5E76\u5220\u9664\u5206\u652F
2320
- gw d feature/xxx \u76F4\u63A5\u5220\u9664\u6307\u5B9A\u5206\u652F
2321
- gw ts v \u5217\u51FA\u6240\u6709 v \u5F00\u5934\u7684 tag
2322
- gw t \u4EA4\u4E92\u5F0F\u521B\u5EFA tag
2323
- gw td \u4EA4\u4E92\u5F0F\u5220\u9664 tag
2324
- gw tu \u4EA4\u4E92\u5F0F\u4FEE\u6539 tag
2325
- gw r \u4EA4\u4E92\u5F0F\u53D1\u5E03\u7248\u672C
2326
- gw s \u4EA4\u4E92\u5F0F\u7BA1\u7406 stash
2327
- gw c \u4EA4\u4E92\u5F0F\u63D0\u4EA4\u4EE3\u7801
2328
-
2329
- \u5206\u652F\u547D\u540D\u683C\u5F0F:
2330
- feature/${TODAY}-<Story ID>-<\u63CF\u8FF0>
2331
- hotfix/${TODAY}-<Issue ID>-<\u63CF\u8FF0>
2332
- `;
2333
- }
2334
-
2335
2357
  // src/index.ts
2336
2358
  init_update_notifier();
2337
2359
 
@@ -2471,6 +2493,320 @@ async function update(currentVersion) {
2471
2493
  }
2472
2494
  }
2473
2495
 
2496
+ // src/commands/log.ts
2497
+ init_utils();
2498
+ import { execSync as execSync8 } from "child_process";
2499
+ import boxen3 from "boxen";
2500
+ import { spawn } from "child_process";
2501
+ function parseGitLog(output) {
2502
+ const commits = [];
2503
+ const lines = output.trim().split("\n");
2504
+ for (const line of lines) {
2505
+ if (!line.trim()) continue;
2506
+ const parts = line.split("|");
2507
+ if (parts.length >= 6) {
2508
+ commits.push({
2509
+ hash: parts[0],
2510
+ shortHash: parts[1],
2511
+ subject: parts[2],
2512
+ author: parts[3],
2513
+ date: parts[4],
2514
+ relativeDate: parts[5],
2515
+ refs: parts[6] || ""
2516
+ });
2517
+ }
2518
+ }
2519
+ return commits;
2520
+ }
2521
+ function getCommitTypeIcon(subject) {
2522
+ const lowerSubject = subject.toLowerCase();
2523
+ if (lowerSubject.includes("feat") || lowerSubject.includes("feature")) return "\u2728";
2524
+ if (lowerSubject.includes("fix") || lowerSubject.includes("bug")) return "\u{1F41B}";
2525
+ if (lowerSubject.includes("docs") || lowerSubject.includes("doc")) return "\u{1F4DA}";
2526
+ if (lowerSubject.includes("style")) return "\u{1F484}";
2527
+ if (lowerSubject.includes("refactor")) return "\u267B\uFE0F";
2528
+ if (lowerSubject.includes("test")) return "\u{1F9EA}";
2529
+ if (lowerSubject.includes("chore")) return "\u{1F527}";
2530
+ if (lowerSubject.includes("perf")) return "\u26A1";
2531
+ if (lowerSubject.includes("ci")) return "\u{1F477}";
2532
+ if (lowerSubject.includes("build")) return "\u{1F4E6}";
2533
+ if (lowerSubject.includes("revert")) return "\u23EA";
2534
+ if (lowerSubject.includes("merge")) return "\u{1F500}";
2535
+ if (lowerSubject.includes("release") || lowerSubject.includes("version")) return "\u{1F516}";
2536
+ return "\u{1F4DD}";
2537
+ }
2538
+ function groupCommitsByDate(commits) {
2539
+ const groups = /* @__PURE__ */ new Map();
2540
+ for (const commit2 of commits) {
2541
+ const date = commit2.date;
2542
+ if (!groups.has(date)) {
2543
+ groups.set(date, []);
2544
+ }
2545
+ groups.get(date).push(commit2);
2546
+ }
2547
+ return groups;
2548
+ }
2549
+ function formatRelativeTime(relativeDate) {
2550
+ let result = relativeDate;
2551
+ const timeMap = {
2552
+ "second": "\u79D2",
2553
+ "seconds": "\u79D2",
2554
+ "minute": "\u5206\u949F",
2555
+ "minutes": "\u5206\u949F",
2556
+ "hour": "\u5C0F\u65F6",
2557
+ "hours": "\u5C0F\u65F6",
2558
+ "day": "\u5929",
2559
+ "days": "\u5929",
2560
+ "week": "\u5468",
2561
+ "weeks": "\u5468",
2562
+ "month": "\u4E2A\u6708",
2563
+ "months": "\u4E2A\u6708",
2564
+ "year": "\u5E74",
2565
+ "years": "\u5E74",
2566
+ "ago": "\u524D"
2567
+ };
2568
+ for (const [en, zh] of Object.entries(timeMap)) {
2569
+ result = result.replace(new RegExp(`\\b${en}\\b`, "g"), zh);
2570
+ }
2571
+ result = result.replace(/(\d+)\s+(秒|分钟|小时|天|周|个月|年)\s+前/g, "$1$2\u524D");
2572
+ const match = result.match(/(\d+)(分钟|小时|天|周|个月|年)前/);
2573
+ if (match) {
2574
+ const num = parseInt(match[1]);
2575
+ const unit = match[2];
2576
+ if (unit === "\u5206\u949F" && num >= 60) {
2577
+ const hours = Math.floor(num / 60);
2578
+ return `${hours}\u5C0F\u65F6\u524D`;
2579
+ }
2580
+ if (unit === "\u5C0F\u65F6" && num >= 24) {
2581
+ const days = Math.floor(num / 24);
2582
+ return `${days}\u5929\u524D`;
2583
+ }
2584
+ if (unit === "\u5929" && num >= 7 && num < 30) {
2585
+ const weeks = Math.floor(num / 7);
2586
+ return `${weeks}\u5468\u524D`;
2587
+ }
2588
+ if (unit === "\u5929" && num >= 30) {
2589
+ const months = Math.floor(num / 30);
2590
+ return `${months}\u4E2A\u6708\u524D`;
2591
+ }
2592
+ if (unit === "\u5468" && num >= 4) {
2593
+ const months = Math.floor(num / 4);
2594
+ return `${months}\u4E2A\u6708\u524D`;
2595
+ }
2596
+ if (unit === "\u4E2A\u6708" && num >= 12) {
2597
+ const years = Math.floor(num / 12);
2598
+ return `${years}\u5E74\u524D`;
2599
+ }
2600
+ }
2601
+ return result;
2602
+ }
2603
+ function parseCommitSubject(subject) {
2604
+ if (subject.includes(" - ")) {
2605
+ const parts = subject.split(" - ");
2606
+ const title = parts[0].trim();
2607
+ const tasks = parts.slice(1).map((task) => task.trim()).filter((task) => task.length > 0);
2608
+ return { title, tasks };
2609
+ }
2610
+ return { title: subject, tasks: [] };
2611
+ }
2612
+ function supportsColor() {
2613
+ return true;
2614
+ }
2615
+ function formatTimelineStyle(commits) {
2616
+ const groupedCommits = groupCommitsByDate(commits);
2617
+ let output = "";
2618
+ const sortedDates = Array.from(groupedCommits.keys()).sort(
2619
+ (a, b) => new Date(b).getTime() - new Date(a).getTime()
2620
+ );
2621
+ const useColors = supportsColor() || process.env.FORCE_COLOR;
2622
+ for (let dateIndex = 0; dateIndex < sortedDates.length; dateIndex++) {
2623
+ const date = sortedDates[dateIndex];
2624
+ const dateCommits = groupedCommits.get(date);
2625
+ const dateTitle = `\u{1F4C5} Commits on ${date}`;
2626
+ if (useColors) {
2627
+ output += "\n" + colors.bold(colors.yellow(dateTitle)) + "\n\n";
2628
+ } else {
2629
+ output += "\n" + dateTitle + "\n\n";
2630
+ }
2631
+ for (let commitIndex = 0; commitIndex < dateCommits.length; commitIndex++) {
2632
+ const commit2 = dateCommits[commitIndex];
2633
+ const icon = getCommitTypeIcon(commit2.subject);
2634
+ const { title, tasks } = parseCommitSubject(commit2.subject);
2635
+ const commitContent = [];
2636
+ if (useColors) {
2637
+ commitContent.push(`${icon} ${colors.bold(colors.white(title))}`);
2638
+ } else {
2639
+ commitContent.push(`${icon} ${title}`);
2640
+ }
2641
+ if (tasks.length > 0) {
2642
+ commitContent.push("");
2643
+ tasks.forEach((task) => {
2644
+ if (useColors) {
2645
+ commitContent.push(` ${colors.dim("\u2013")} ${colors.dim(task)}`);
2646
+ } else {
2647
+ commitContent.push(` \u2013 ${task}`);
2648
+ }
2649
+ });
2650
+ }
2651
+ commitContent.push("");
2652
+ if (useColors) {
2653
+ commitContent.push(`${colors.dim("\u{1F464}")} ${colors.blue(commit2.author)} ${colors.dim("committed")} ${colors.green(formatRelativeTime(commit2.relativeDate))}`);
2654
+ commitContent.push(`${colors.dim("\u{1F517}")} ${colors.orange("#" + commit2.shortHash)}`);
2655
+ if (commit2.refs && commit2.refs.trim()) {
2656
+ const refs = commit2.refs.trim();
2657
+ const refParts = refs.split(", ");
2658
+ const branches = [];
2659
+ const tags = [];
2660
+ refParts.forEach((ref) => {
2661
+ if (ref.startsWith("tag: ")) {
2662
+ tags.push(ref.replace("tag: ", ""));
2663
+ } else if (ref.includes("/") || ref === "HEAD") {
2664
+ branches.push(ref);
2665
+ } else {
2666
+ branches.push(ref);
2667
+ }
2668
+ });
2669
+ if (branches.length > 0) {
2670
+ commitContent.push(`${colors.dim("\u{1F33F}")} ${colors.lightPurple(branches.join(", "))}`);
2671
+ }
2672
+ if (tags.length > 0) {
2673
+ const tagText = tags.map((tag) => `tag ${tag}`).join(", ");
2674
+ commitContent.push(`${colors.dim("\u{1F516}")} ${colors.yellow(tagText)}`);
2675
+ }
2676
+ }
2677
+ } else {
2678
+ commitContent.push(`\u{1F464} ${commit2.author} committed ${formatRelativeTime(commit2.relativeDate)}`);
2679
+ commitContent.push(`\u{1F517} #${commit2.shortHash}`);
2680
+ if (commit2.refs && commit2.refs.trim()) {
2681
+ const refs = commit2.refs.trim();
2682
+ const refParts = refs.split(", ");
2683
+ const branches = [];
2684
+ const tags = [];
2685
+ refParts.forEach((ref) => {
2686
+ if (ref.startsWith("tag: ")) {
2687
+ tags.push(ref.replace("tag: ", ""));
2688
+ } else if (ref.includes("/") || ref === "HEAD") {
2689
+ branches.push(ref);
2690
+ } else {
2691
+ branches.push(ref);
2692
+ }
2693
+ });
2694
+ if (branches.length > 0) {
2695
+ commitContent.push(`\u{1F33F} ${branches.join(", ")}`);
2696
+ }
2697
+ if (tags.length > 0) {
2698
+ const tagText = tags.map((tag) => `tag ${tag}`).join(", ");
2699
+ commitContent.push(`\u{1F516} ${tagText}`);
2700
+ }
2701
+ }
2702
+ }
2703
+ const commitBox = boxen3(commitContent.join("\n"), {
2704
+ padding: { top: 0, bottom: 0, left: 1, right: 1 },
2705
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
2706
+ borderStyle: "round",
2707
+ borderColor: "gray"
2708
+ });
2709
+ output += commitBox + "\n";
2710
+ }
2711
+ }
2712
+ return output;
2713
+ }
2714
+ function startInteractivePager(content) {
2715
+ const pager = process.env.PAGER || "less";
2716
+ try {
2717
+ const pagerProcess = spawn(pager, ["-R", "-S", "-F", "-X", "-i"], {
2718
+ stdio: ["pipe", "inherit", "inherit"],
2719
+ env: { ...process.env, LESS: "-R -S -F -X -i" }
2720
+ });
2721
+ pagerProcess.stdin.write(content);
2722
+ pagerProcess.stdin.end();
2723
+ pagerProcess.on("exit", () => {
2724
+ });
2725
+ pagerProcess.on("error", (err) => {
2726
+ console.log(content);
2727
+ });
2728
+ } catch (error) {
2729
+ console.log(content);
2730
+ }
2731
+ }
2732
+ function executeTimelineLog(options) {
2733
+ try {
2734
+ let cmd = 'git log --pretty=format:"%H|%h|%s|%an|%ad|%ar|%D" --date=short';
2735
+ if (options.limit && !options.interactive) cmd += ` -${options.limit}`;
2736
+ if (options.author) cmd += ` --author="${options.author}"`;
2737
+ if (options.since) cmd += ` --since="${options.since}"`;
2738
+ if (options.until) cmd += ` --until="${options.until}"`;
2739
+ if (options.grep) cmd += ` --grep="${options.grep}"`;
2740
+ if (options.all) cmd += ` --all`;
2741
+ if (options.interactive && !options.limit) {
2742
+ cmd += ` -50`;
2743
+ }
2744
+ const output = execSync8(cmd, {
2745
+ encoding: "utf8",
2746
+ stdio: "pipe",
2747
+ maxBuffer: 1024 * 1024 * 10
2748
+ });
2749
+ if (output.trim()) {
2750
+ const commits = parseGitLog(output);
2751
+ let fullOutput = "";
2752
+ const title = `\u{1F4CA} \u5171\u663E\u793A ${commits.length} \u4E2A\u63D0\u4EA4`;
2753
+ fullOutput += "\n" + boxen3(title, {
2754
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
2755
+ margin: { top: 0, bottom: 1, left: 0, right: 0 },
2756
+ borderStyle: "double",
2757
+ borderColor: "green",
2758
+ textAlignment: "center"
2759
+ }) + "\n";
2760
+ const timelineOutput = formatTimelineStyle(commits);
2761
+ fullOutput += timelineOutput;
2762
+ if (options.interactive) {
2763
+ startInteractivePager(fullOutput);
2764
+ } else {
2765
+ console.log(fullOutput);
2766
+ }
2767
+ } else {
2768
+ const noCommitsMsg = "\n" + boxen3("\u{1F4ED} \u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u63D0\u4EA4\u8BB0\u5F55", {
2769
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
2770
+ borderStyle: "round",
2771
+ borderColor: "yellow",
2772
+ textAlignment: "center"
2773
+ });
2774
+ if (options.interactive) {
2775
+ startInteractivePager(noCommitsMsg);
2776
+ } else {
2777
+ console.log(noCommitsMsg);
2778
+ }
2779
+ }
2780
+ } catch (error) {
2781
+ let errorMessage = "\u274C \u6267\u884C\u5931\u8D25";
2782
+ if (error.status === 128) {
2783
+ errorMessage = "\u274C Git\u4ED3\u5E93\u9519\u8BEF\u6216\u6CA1\u6709\u63D0\u4EA4\u8BB0\u5F55";
2784
+ } else {
2785
+ errorMessage = `\u274C \u6267\u884C\u5931\u8D25: ${error.message}`;
2786
+ }
2787
+ const errorBox = "\n" + boxen3(errorMessage, {
2788
+ padding: { top: 0, bottom: 0, left: 2, right: 2 },
2789
+ borderStyle: "round",
2790
+ borderColor: "red",
2791
+ textAlignment: "center"
2792
+ });
2793
+ if (options.interactive) {
2794
+ startInteractivePager(errorBox);
2795
+ } else {
2796
+ console.log(errorBox);
2797
+ }
2798
+ }
2799
+ }
2800
+ async function log(options = {}) {
2801
+ if (options.interactive === void 0) {
2802
+ options.interactive = true;
2803
+ }
2804
+ if (!options.interactive && !options.limit) {
2805
+ options.limit = 10;
2806
+ }
2807
+ executeTimelineLog(options);
2808
+ }
2809
+
2474
2810
  // src/index.ts
2475
2811
  process.on("uncaughtException", (err) => {
2476
2812
  if (err instanceof ExitPromptError) {
@@ -2496,7 +2832,7 @@ process.on("SIGTERM", () => {
2496
2832
  console.log("");
2497
2833
  process.exit(0);
2498
2834
  });
2499
- var version = true ? "0.2.24" : "0.0.0-dev";
2835
+ var version = true ? "0.3.2" : "0.0.0-dev";
2500
2836
  async function mainMenu() {
2501
2837
  console.log(
2502
2838
  colors.green(`
@@ -2554,7 +2890,11 @@ async function mainMenu() {
2554
2890
  value: "stash"
2555
2891
  },
2556
2892
  {
2557
- name: `[b] \u2699\uFE0F \u521D\u59CB\u5316\u914D\u7F6E ${colors.dim("gw init")}`,
2893
+ name: `[b] \u{1F4CA} \u67E5\u770B\u65E5\u5FD7 ${colors.dim("gw log")}`,
2894
+ value: "log"
2895
+ },
2896
+ {
2897
+ name: `[c] \u2699\uFE0F \u521D\u59CB\u5316\u914D\u7F6E ${colors.dim("gw init")}`,
2558
2898
  value: "init"
2559
2899
  },
2560
2900
  { name: "[0] \u2753 \u5E2E\u52A9", value: "help" },
@@ -2603,11 +2943,15 @@ async function mainMenu() {
2603
2943
  checkGitRepo();
2604
2944
  await stash();
2605
2945
  break;
2946
+ case "log":
2947
+ checkGitRepo();
2948
+ await log();
2949
+ break;
2606
2950
  case "init":
2607
2951
  await init();
2608
2952
  break;
2609
2953
  case "help":
2610
- console.log(showHelp());
2954
+ cli.outputHelp();
2611
2955
  break;
2612
2956
  case "exit":
2613
2957
  break;
@@ -2674,6 +3018,13 @@ cli.command("commit", "\u4EA4\u4E92\u5F0F\u63D0\u4EA4 (Conventional Commits + Gi
2674
3018
  cli.command("update", "\u68C0\u67E5\u5E76\u66F4\u65B0\u5230\u6700\u65B0\u7248\u672C").alias("upt").action(async () => {
2675
3019
  return update(version);
2676
3020
  });
3021
+ cli.command("log", "\u4EA4\u4E92\u5F0FGit\u65E5\u5FD7\u67E5\u770B (\u5206\u9875\u6A21\u5F0F)").alias("ls").alias("l").option("--limit <number>", "\u9650\u5236\u663E\u793A\u6570\u91CF").action(async (options) => {
3022
+ await checkForUpdates(version, "@zjex/git-workflow");
3023
+ checkGitRepo();
3024
+ const logOptions = { interactive: true };
3025
+ if (options.limit) logOptions.limit = parseInt(options.limit);
3026
+ return log(logOptions);
3027
+ });
2677
3028
  cli.command("clean", "\u6E05\u7406\u7F13\u5B58\u6587\u4EF6").action(async () => {
2678
3029
  const { clearUpdateCache: clearUpdateCache3 } = await Promise.resolve().then(() => (init_update_notifier(), update_notifier_exports));
2679
3030
  clearUpdateCache3();
@@ -2681,15 +3032,15 @@ cli.command("clean", "\u6E05\u7406\u7F13\u5B58\u6587\u4EF6").action(async () =>
2681
3032
  console.log(colors.green("\u2714 \u7F13\u5B58\u5DF2\u6E05\u7406"));
2682
3033
  console.log("");
2683
3034
  });
2684
- cli.help((sections) => {
2685
- sections.push({
2686
- body: showHelp()
2687
- });
2688
- });
2689
3035
  cli.option("-v, --version", "\u663E\u793A\u7248\u672C\u53F7");
2690
- var args = process.argv.slice(2);
2691
- if (args.includes("-v") || args.includes("--version")) {
3036
+ cli.option("-h, --help", "\u663E\u793A\u5E2E\u52A9\u4FE1\u606F");
3037
+ var processArgs = process.argv.slice(2);
3038
+ if (processArgs.includes("-v") || processArgs.includes("--version")) {
2692
3039
  console.log(colors.yellow(`v${version}`));
2693
3040
  process.exit(0);
2694
3041
  }
3042
+ if (processArgs.includes("-h") || processArgs.includes("--help")) {
3043
+ cli.outputHelp();
3044
+ process.exit(0);
3045
+ }
2695
3046
  cli.parse();