@zjex/git-workflow 0.3.7 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.js +94 -64
- package/package.json +1 -1
- package/src/commands/branch.ts +22 -5
- package/src/commands/commit.ts +63 -61
- package/src/commands/init.ts +39 -15
- package/src/config.ts +4 -0
- package/src/update-notifier.ts +1 -2
- package/tests/branch.test.ts +116 -0
- package/tests/commit.test.ts +186 -1
- package/tests/init.test.ts +240 -297
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
<a href="https://github.com/iamzjt-front-end/git-workflow"><img src="https://img.shields.io/github/stars/iamzjt-front-end/git-workflow?style=flat&colorA=18181B&colorB=F59E0B" alt="github stars"></a>
|
|
13
13
|
<a href="https://github.com/iamzjt-front-end/git-workflow/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@zjex/git-workflow?style=flat&colorA=18181B&colorB=10B981" alt="license"></a>
|
|
14
14
|
<a href="https://nodejs.org"><img src="https://img.shields.io/badge/node-%3E%3D18-339933?style=flat&logo=node.js&logoColor=white&colorA=18181B" alt="node version"></a>
|
|
15
|
-
<a href="https://github.com/iamzjt-front-end/git-workflow/actions"><img src="https://img.shields.io/badge/tests-
|
|
15
|
+
<a href="https://github.com/iamzjt-front-end/git-workflow/actions"><img src="https://img.shields.io/badge/tests-290%20passed-success?style=flat&colorA=18181B" alt="tests"></a>
|
|
16
16
|
<a href="https://github.com/iamzjt-front-end/git-workflow/issues"><img src="https://img.shields.io/github/issues/iamzjt-front-end/git-workflow?style=flat&colorA=18181B&colorB=EC4899" alt="issues"></a>
|
|
17
17
|
</p>
|
|
18
18
|
|
package/dist/index.js
CHANGED
|
@@ -91,9 +91,9 @@ __export(update_notifier_exports, {
|
|
|
91
91
|
clearUpdateCache: () => clearUpdateCache
|
|
92
92
|
});
|
|
93
93
|
import { execSync as execSync6 } from "child_process";
|
|
94
|
-
import { readFileSync as readFileSync3, writeFileSync as
|
|
94
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, existsSync as existsSync3, unlinkSync as unlinkSync2 } from "fs";
|
|
95
95
|
import { homedir as homedir3 } from "os";
|
|
96
|
-
import { join as
|
|
96
|
+
import { join as join4 } from "path";
|
|
97
97
|
import boxen from "boxen";
|
|
98
98
|
import { select as select7 } from "@inquirer/prompts";
|
|
99
99
|
import ora5 from "ora";
|
|
@@ -179,11 +179,10 @@ function showSimpleNotification(current, latest) {
|
|
|
179
179
|
)} \u2192 ${colors.green(latest)} ${colors.dim("\u8FD0\u884C")} ${colors.cyan(
|
|
180
180
|
"gw update"
|
|
181
181
|
)} ${colors.dim("\u66F4\u65B0")}`;
|
|
182
|
-
console.log("");
|
|
183
182
|
console.log(
|
|
184
183
|
boxen(message, {
|
|
185
184
|
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
186
|
-
margin: { top: 0, bottom:
|
|
185
|
+
margin: { top: 0, bottom: 0, left: 0, right: 0 },
|
|
187
186
|
borderStyle: "round",
|
|
188
187
|
borderColor: "yellow",
|
|
189
188
|
align: "center"
|
|
@@ -283,16 +282,16 @@ async function performUpdate(packageName) {
|
|
|
283
282
|
}
|
|
284
283
|
function clearUpdateCache() {
|
|
285
284
|
try {
|
|
286
|
-
const cacheFile =
|
|
285
|
+
const cacheFile = join4(homedir3(), CACHE_FILE);
|
|
287
286
|
if (existsSync3(cacheFile)) {
|
|
288
|
-
|
|
287
|
+
unlinkSync2(cacheFile);
|
|
289
288
|
}
|
|
290
289
|
} catch {
|
|
291
290
|
}
|
|
292
291
|
}
|
|
293
292
|
function readCache() {
|
|
294
293
|
try {
|
|
295
|
-
const cacheFile =
|
|
294
|
+
const cacheFile = join4(homedir3(), CACHE_FILE);
|
|
296
295
|
if (!existsSync3(cacheFile)) {
|
|
297
296
|
return null;
|
|
298
297
|
}
|
|
@@ -304,8 +303,8 @@ function readCache() {
|
|
|
304
303
|
}
|
|
305
304
|
function writeCache(cache) {
|
|
306
305
|
try {
|
|
307
|
-
const cacheFile =
|
|
308
|
-
|
|
306
|
+
const cacheFile = join4(homedir3(), CACHE_FILE);
|
|
307
|
+
writeFileSync4(cacheFile, JSON.stringify(cache), "utf-8");
|
|
309
308
|
} catch {
|
|
310
309
|
}
|
|
311
310
|
}
|
|
@@ -431,12 +430,22 @@ async function getBranchName(type) {
|
|
|
431
430
|
console.log(colors.red(`${idLabel}\u4E0D\u80FD\u4E3A\u7A7A`));
|
|
432
431
|
return null;
|
|
433
432
|
}
|
|
434
|
-
const
|
|
435
|
-
|
|
433
|
+
const requireDescription = type === "feature" ? config2.featureRequireDescription ?? false : config2.hotfixRequireDescription ?? false;
|
|
434
|
+
const descMessage = requireDescription ? "\u8BF7\u8F93\u5165\u63CF\u8FF0:" : "\u8BF7\u8F93\u5165\u63CF\u8FF0 (\u53EF\u8DF3\u8FC7):";
|
|
435
|
+
const description = await input({ message: descMessage, theme });
|
|
436
|
+
if (requireDescription && !description) {
|
|
436
437
|
console.log(colors.red("\u63CF\u8FF0\u4E0D\u80FD\u4E3A\u7A7A"));
|
|
437
438
|
return null;
|
|
438
439
|
}
|
|
439
|
-
|
|
440
|
+
if (id && description) {
|
|
441
|
+
return `${branchPrefix}/${TODAY}-${id}-${description}`;
|
|
442
|
+
} else if (id) {
|
|
443
|
+
return `${branchPrefix}/${TODAY}-${id}`;
|
|
444
|
+
} else if (description) {
|
|
445
|
+
return `${branchPrefix}/${TODAY}-${description}`;
|
|
446
|
+
} else {
|
|
447
|
+
return `${branchPrefix}/${TODAY}`;
|
|
448
|
+
}
|
|
440
449
|
}
|
|
441
450
|
async function createBranch(type, baseBranchArg) {
|
|
442
451
|
const config2 = getConfig();
|
|
@@ -1327,6 +1336,25 @@ async function init() {
|
|
|
1327
1336
|
});
|
|
1328
1337
|
config2.hotfixIdLabel = hotfixIdLabel;
|
|
1329
1338
|
divider();
|
|
1339
|
+
const featureRequireDescription = await select4({
|
|
1340
|
+
message: "Feature \u5206\u652F\u662F\u5426\u8981\u6C42\u5FC5\u586B\u63CF\u8FF0?",
|
|
1341
|
+
choices: [
|
|
1342
|
+
{ name: "\u5426", value: false },
|
|
1343
|
+
{ name: "\u662F", value: true }
|
|
1344
|
+
],
|
|
1345
|
+
theme
|
|
1346
|
+
});
|
|
1347
|
+
config2.featureRequireDescription = featureRequireDescription;
|
|
1348
|
+
const hotfixRequireDescription = await select4({
|
|
1349
|
+
message: "Hotfix \u5206\u652F\u662F\u5426\u8981\u6C42\u5FC5\u586B\u63CF\u8FF0?",
|
|
1350
|
+
choices: [
|
|
1351
|
+
{ name: "\u5426", value: false },
|
|
1352
|
+
{ name: "\u662F", value: true }
|
|
1353
|
+
],
|
|
1354
|
+
theme
|
|
1355
|
+
});
|
|
1356
|
+
config2.hotfixRequireDescription = hotfixRequireDescription;
|
|
1357
|
+
divider();
|
|
1330
1358
|
const defaultTagPrefix = await input3({
|
|
1331
1359
|
message: "\u9ED8\u8BA4 Tag \u524D\u7F00 (\u7559\u7A7A\u5219\u6BCF\u6B21\u9009\u62E9):",
|
|
1332
1360
|
theme
|
|
@@ -1791,6 +1819,9 @@ async function dropStash(index) {
|
|
|
1791
1819
|
// src/commands/commit.ts
|
|
1792
1820
|
init_utils();
|
|
1793
1821
|
import { execSync as execSync5 } from "child_process";
|
|
1822
|
+
import { writeFileSync as writeFileSync3, unlinkSync } from "fs";
|
|
1823
|
+
import { tmpdir } from "os";
|
|
1824
|
+
import { join as join3 } from "path";
|
|
1794
1825
|
import { select as select6, input as input5, checkbox } from "@inquirer/prompts";
|
|
1795
1826
|
import ora4 from "ora";
|
|
1796
1827
|
|
|
@@ -2198,45 +2229,39 @@ function formatFileStatus(status) {
|
|
|
2198
2229
|
}
|
|
2199
2230
|
async function commit() {
|
|
2200
2231
|
const config2 = getConfig();
|
|
2232
|
+
const autoStage = config2.autoStage ?? true;
|
|
2233
|
+
if (autoStage) {
|
|
2234
|
+
execSync5("git add -A", { stdio: "pipe" });
|
|
2235
|
+
}
|
|
2201
2236
|
let { staged, unstaged } = parseGitStatus();
|
|
2202
|
-
if (unstaged.length > 0) {
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
})),
|
|
2226
|
-
theme
|
|
2227
|
-
});
|
|
2228
|
-
if (filesToStage.length === 0) {
|
|
2229
|
-
console.log(colors.yellow("\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6587\u4EF6\uFF0C\u5DF2\u53D6\u6D88"));
|
|
2230
|
-
return;
|
|
2231
|
-
}
|
|
2232
|
-
for (const file of filesToStage) {
|
|
2233
|
-
execSync5(`git add "${file}"`, { stdio: "pipe" });
|
|
2234
|
-
}
|
|
2235
|
-
console.log(colors.green(`\u2714 \u5DF2\u6682\u5B58 ${filesToStage.length} \u4E2A\u6587\u4EF6`));
|
|
2236
|
-
divider();
|
|
2237
|
-
const newStatus = parseGitStatus();
|
|
2238
|
-
staged = newStatus.staged;
|
|
2237
|
+
if (staged.length === 0 && unstaged.length > 0 && !autoStage) {
|
|
2238
|
+
console.log(colors.yellow("\u6CA1\u6709\u6682\u5B58\u7684\u66F4\u6539"));
|
|
2239
|
+
divider();
|
|
2240
|
+
console.log("\u672A\u6682\u5B58\u7684\u6587\u4EF6:");
|
|
2241
|
+
for (const { status, file } of unstaged) {
|
|
2242
|
+
console.log(` ${formatFileStatus(status)} ${file}`);
|
|
2243
|
+
}
|
|
2244
|
+
divider();
|
|
2245
|
+
const filesToStage = await checkbox({
|
|
2246
|
+
message: "\u9009\u62E9\u8981\u6682\u5B58\u7684\u6587\u4EF6:",
|
|
2247
|
+
choices: unstaged.map(({ status, file }) => ({
|
|
2248
|
+
name: `${formatFileStatus(status)} ${file}`,
|
|
2249
|
+
value: file,
|
|
2250
|
+
checked: true
|
|
2251
|
+
})),
|
|
2252
|
+
theme
|
|
2253
|
+
});
|
|
2254
|
+
if (filesToStage.length === 0) {
|
|
2255
|
+
console.log(colors.yellow("\u6CA1\u6709\u9009\u62E9\u4EFB\u4F55\u6587\u4EF6\uFF0C\u5DF2\u53D6\u6D88"));
|
|
2256
|
+
return;
|
|
2257
|
+
}
|
|
2258
|
+
for (const file of filesToStage) {
|
|
2259
|
+
execSync5(`git add "${file}"`, { stdio: "pipe" });
|
|
2239
2260
|
}
|
|
2261
|
+
console.log(colors.green(`\u2714 \u5DF2\u6682\u5B58 ${filesToStage.length} \u4E2A\u6587\u4EF6`));
|
|
2262
|
+
divider();
|
|
2263
|
+
const newStatus = parseGitStatus();
|
|
2264
|
+
staged = newStatus.staged;
|
|
2240
2265
|
}
|
|
2241
2266
|
if (staged.length === 0) {
|
|
2242
2267
|
console.log(colors.yellow("\u5DE5\u4F5C\u533A\u5E72\u51C0\uFF0C\u6CA1\u6709\u9700\u8981\u63D0\u4EA4\u7684\u66F4\u6539"));
|
|
@@ -2322,14 +2347,10 @@ async function commit() {
|
|
|
2322
2347
|
}
|
|
2323
2348
|
const spinner = ora4("\u6B63\u5728\u63D0\u4EA4...").start();
|
|
2324
2349
|
try {
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
const autoStage = config2.autoStage ?? true;
|
|
2328
|
-
if (autoStage) {
|
|
2329
|
-
execSync5("git add -A", { stdio: "pipe" });
|
|
2330
|
-
finalStatus = parseGitStatus();
|
|
2331
|
-
}
|
|
2350
|
+
if (autoStage) {
|
|
2351
|
+
execSync5("git add -A", { stdio: "pipe" });
|
|
2332
2352
|
}
|
|
2353
|
+
const finalStatus = parseGitStatus();
|
|
2333
2354
|
if (finalStatus.staged.length === 0) {
|
|
2334
2355
|
spinner.fail("\u6CA1\u6709\u6682\u5B58\u7684\u6587\u4EF6\u53EF\u4EE5\u63D0\u4EA4");
|
|
2335
2356
|
console.log("");
|
|
@@ -2340,9 +2361,18 @@ async function commit() {
|
|
|
2340
2361
|
console.log("");
|
|
2341
2362
|
return;
|
|
2342
2363
|
}
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2364
|
+
const tmpFile = join3(tmpdir(), `.gw-commit-msg-${Date.now()}`);
|
|
2365
|
+
try {
|
|
2366
|
+
writeFileSync3(tmpFile, message, "utf-8");
|
|
2367
|
+
execSync5(`git commit -F "${tmpFile}"`, {
|
|
2368
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
2369
|
+
});
|
|
2370
|
+
} finally {
|
|
2371
|
+
try {
|
|
2372
|
+
unlinkSync(tmpFile);
|
|
2373
|
+
} catch {
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2346
2376
|
spinner.succeed("\u63D0\u4EA4\u6210\u529F");
|
|
2347
2377
|
const commitHash = execOutput("git rev-parse --short HEAD");
|
|
2348
2378
|
console.log(colors.dim(`commit: ${commitHash}`));
|
|
@@ -2449,15 +2479,15 @@ import { execSync as execSync7 } from "child_process";
|
|
|
2449
2479
|
import ora6 from "ora";
|
|
2450
2480
|
import boxen2 from "boxen";
|
|
2451
2481
|
import semver2 from "semver";
|
|
2452
|
-
import { existsSync as existsSync4, unlinkSync as
|
|
2482
|
+
import { existsSync as existsSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
2453
2483
|
import { homedir as homedir4 } from "os";
|
|
2454
|
-
import { join as
|
|
2484
|
+
import { join as join5 } from "path";
|
|
2455
2485
|
var CACHE_FILE2 = ".gw-update-check";
|
|
2456
2486
|
function clearUpdateCache2() {
|
|
2457
2487
|
try {
|
|
2458
|
-
const cacheFile =
|
|
2488
|
+
const cacheFile = join5(homedir4(), CACHE_FILE2);
|
|
2459
2489
|
if (existsSync4(cacheFile)) {
|
|
2460
|
-
|
|
2490
|
+
unlinkSync3(cacheFile);
|
|
2461
2491
|
}
|
|
2462
2492
|
} catch {
|
|
2463
2493
|
}
|
|
@@ -2914,7 +2944,7 @@ process.on("SIGTERM", () => {
|
|
|
2914
2944
|
console.log("");
|
|
2915
2945
|
process.exit(0);
|
|
2916
2946
|
});
|
|
2917
|
-
var version = true ? "0.3.
|
|
2947
|
+
var version = true ? "0.3.9" : "0.0.0-dev";
|
|
2918
2948
|
async function mainMenu() {
|
|
2919
2949
|
console.log(
|
|
2920
2950
|
colors.green(`
|
package/package.json
CHANGED
package/src/commands/branch.ts
CHANGED
|
@@ -32,15 +32,32 @@ export async function getBranchName(type: BranchType): Promise<string | null> {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
// 描述是否必填,默认非必填
|
|
36
|
+
const requireDescription =
|
|
37
|
+
type === "feature"
|
|
38
|
+
? config.featureRequireDescription ?? false
|
|
39
|
+
: config.hotfixRequireDescription ?? false;
|
|
40
|
+
const descMessage = requireDescription
|
|
41
|
+
? "请输入描述:"
|
|
42
|
+
: "请输入描述 (可跳过):";
|
|
43
|
+
|
|
44
|
+
const description = await input({ message: descMessage, theme });
|
|
45
|
+
|
|
46
|
+
if (requireDescription && !description) {
|
|
37
47
|
console.log(colors.red("描述不能为空"));
|
|
38
48
|
return null;
|
|
39
49
|
}
|
|
40
50
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
// 构建分支名
|
|
52
|
+
if (id && description) {
|
|
53
|
+
return `${branchPrefix}/${TODAY}-${id}-${description}`;
|
|
54
|
+
} else if (id) {
|
|
55
|
+
return `${branchPrefix}/${TODAY}-${id}`;
|
|
56
|
+
} else if (description) {
|
|
57
|
+
return `${branchPrefix}/${TODAY}-${description}`;
|
|
58
|
+
} else {
|
|
59
|
+
return `${branchPrefix}/${TODAY}`;
|
|
60
|
+
}
|
|
44
61
|
}
|
|
45
62
|
|
|
46
63
|
export async function createBranch(
|
package/src/commands/commit.ts
CHANGED
|
@@ -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
|
-
|
|
106
|
-
|
|
107
|
-
// ========== 步骤 1: 处理未暂存的文件 ==========
|
|
108
|
-
if (unstaged.length > 0) {
|
|
109
|
-
const autoStage = config.autoStage ?? true;
|
|
108
|
+
const autoStage = config.autoStage ?? true;
|
|
110
109
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
148
|
-
|
|
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
|
-
|
|
155
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
// 显示提交信息
|
package/src/commands/init.ts
CHANGED
|
@@ -118,6 +118,29 @@ export async function init(): Promise<void> {
|
|
|
118
118
|
|
|
119
119
|
divider();
|
|
120
120
|
|
|
121
|
+
// 描述必填配置
|
|
122
|
+
const featureRequireDescription = await select({
|
|
123
|
+
message: "Feature 分支是否要求必填描述?",
|
|
124
|
+
choices: [
|
|
125
|
+
{ name: "否", value: false },
|
|
126
|
+
{ name: "是", value: true },
|
|
127
|
+
],
|
|
128
|
+
theme,
|
|
129
|
+
});
|
|
130
|
+
config.featureRequireDescription = featureRequireDescription;
|
|
131
|
+
|
|
132
|
+
const hotfixRequireDescription = await select({
|
|
133
|
+
message: "Hotfix 分支是否要求必填描述?",
|
|
134
|
+
choices: [
|
|
135
|
+
{ name: "否", value: false },
|
|
136
|
+
{ name: "是", value: true },
|
|
137
|
+
],
|
|
138
|
+
theme,
|
|
139
|
+
});
|
|
140
|
+
config.hotfixRequireDescription = hotfixRequireDescription;
|
|
141
|
+
|
|
142
|
+
divider();
|
|
143
|
+
|
|
121
144
|
// Tag 配置
|
|
122
145
|
const defaultTagPrefix = await input({
|
|
123
146
|
message: "默认 Tag 前缀 (留空则每次选择):",
|
|
@@ -269,15 +292,16 @@ export async function init(): Promise<void> {
|
|
|
269
292
|
const detailedDescription = await select({
|
|
270
293
|
message: "是否生成详细的修改点描述?",
|
|
271
294
|
choices: [
|
|
272
|
-
{
|
|
273
|
-
name: "是(包含修改点列表,推荐)",
|
|
295
|
+
{
|
|
296
|
+
name: "是(包含修改点列表,推荐)",
|
|
274
297
|
value: true,
|
|
275
|
-
description:
|
|
298
|
+
description:
|
|
299
|
+
"如:feat(auth): 添加用户登录功能\n\n- 实现用户名密码登录接口\n- 添加登录状态验证中间件",
|
|
276
300
|
},
|
|
277
|
-
{
|
|
278
|
-
name: "否(仅生成标题)",
|
|
301
|
+
{
|
|
302
|
+
name: "否(仅生成标题)",
|
|
279
303
|
value: false,
|
|
280
|
-
description: "如:feat(auth): 添加用户登录功能"
|
|
304
|
+
description: "如:feat(auth): 添加用户登录功能",
|
|
281
305
|
},
|
|
282
306
|
],
|
|
283
307
|
theme,
|
|
@@ -286,20 +310,20 @@ export async function init(): Promise<void> {
|
|
|
286
310
|
const aiUseEmoji = await select({
|
|
287
311
|
message: "AI 生成的 commit message 是否包含 emoji?",
|
|
288
312
|
choices: [
|
|
289
|
-
{
|
|
290
|
-
name: "是(推荐)",
|
|
313
|
+
{
|
|
314
|
+
name: "是(推荐)",
|
|
291
315
|
value: true,
|
|
292
|
-
description: "如:✨ feat(auth): 添加用户登录功能"
|
|
316
|
+
description: "如:✨ feat(auth): 添加用户登录功能",
|
|
293
317
|
},
|
|
294
|
-
{
|
|
295
|
-
name: "否",
|
|
318
|
+
{
|
|
319
|
+
name: "否",
|
|
296
320
|
value: false,
|
|
297
|
-
description: "如:feat(auth): 添加用户登录功能"
|
|
321
|
+
description: "如:feat(auth): 添加用户登录功能",
|
|
298
322
|
},
|
|
299
|
-
{
|
|
300
|
-
name: "跟随全局设置",
|
|
323
|
+
{
|
|
324
|
+
name: "跟随全局设置",
|
|
301
325
|
value: undefined,
|
|
302
|
-
description: `当前全局设置:${useEmoji ?
|
|
326
|
+
description: `当前全局设置:${useEmoji ? "启用" : "禁用"} emoji`,
|
|
303
327
|
},
|
|
304
328
|
],
|
|
305
329
|
theme,
|
package/src/config.ts
CHANGED
|
@@ -12,6 +12,10 @@ export interface GwConfig {
|
|
|
12
12
|
hotfixPrefix: string;
|
|
13
13
|
// 是否要求必填 ID,默认 false
|
|
14
14
|
requireId: boolean;
|
|
15
|
+
// feature 分支是否要求必填描述,默认 false
|
|
16
|
+
featureRequireDescription?: boolean;
|
|
17
|
+
// hotfix 分支是否要求必填描述,默认 false
|
|
18
|
+
hotfixRequireDescription?: boolean;
|
|
15
19
|
// ID 标签名称
|
|
16
20
|
featureIdLabel: string;
|
|
17
21
|
hotfixIdLabel: string;
|
package/src/update-notifier.ts
CHANGED
|
@@ -151,11 +151,10 @@ function showSimpleNotification(current: string, latest: string): void {
|
|
|
151
151
|
"gw update"
|
|
152
152
|
)} ${colors.dim("更新")}`;
|
|
153
153
|
|
|
154
|
-
console.log("");
|
|
155
154
|
console.log(
|
|
156
155
|
boxen(message, {
|
|
157
156
|
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
158
|
-
margin: { top: 0, bottom:
|
|
157
|
+
margin: { top: 0, bottom: 0, left: 0, right: 0 },
|
|
159
158
|
borderStyle: "round",
|
|
160
159
|
borderColor: "yellow",
|
|
161
160
|
align: "center",
|