@zjex/git-workflow 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/README.md +1 -1
- package/dist/index.js +580 -178
- package/docs/.vitepress/config.ts +2 -0
- package/docs/commands/amend-date.md +425 -0
- package/docs/commands/amend.md +380 -0
- package/docs/commands/index.md +14 -10
- package/package.json +1 -1
- package/src/commands/amend-date.ts +228 -0
- package/src/commands/amend.ts +189 -0
- package/src/commands/branch.ts +47 -38
- package/src/commands/stash.ts +40 -33
- package/src/commands/tag.ts +113 -73
- package/src/commands/update.ts +77 -44
- package/src/index.ts +39 -4
- package/src/utils.ts +59 -1
- package/tests/amend-date.test.ts +364 -0
- package/tests/amend.test.ts +441 -0
- package/tests/stash.test.ts +70 -75
- package/tests/update.test.ts +125 -112
package/src/commands/tag.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import { execSync } from "child_process";
|
|
2
1
|
import { select, input } from "@inquirer/prompts";
|
|
3
2
|
import ora from "ora";
|
|
4
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
colors,
|
|
5
|
+
theme,
|
|
6
|
+
exec,
|
|
7
|
+
execOutput,
|
|
8
|
+
execAsync,
|
|
9
|
+
execWithSpinner,
|
|
10
|
+
divider,
|
|
11
|
+
} from "../utils.js";
|
|
5
12
|
import { getConfig } from "../config.js";
|
|
6
13
|
|
|
7
14
|
/**
|
|
@@ -27,7 +34,7 @@ export async function listTags(prefix?: string): Promise<void> {
|
|
|
27
34
|
// 4. 如果没有 tags,提示并返回
|
|
28
35
|
if (tags.length === 0) {
|
|
29
36
|
console.log(
|
|
30
|
-
colors.yellow(prefix ? `没有 '${prefix}' 开头的 tag` : "没有 tag")
|
|
37
|
+
colors.yellow(prefix ? `没有 '${prefix}' 开头的 tag` : "没有 tag"),
|
|
31
38
|
);
|
|
32
39
|
return;
|
|
33
40
|
}
|
|
@@ -35,7 +42,7 @@ export async function listTags(prefix?: string): Promise<void> {
|
|
|
35
42
|
// 5. 如果指定了前缀,直接显示单列(最多 5 个)
|
|
36
43
|
if (prefix) {
|
|
37
44
|
console.log(
|
|
38
|
-
colors.green(`以 '${prefix}' 开头的 tags (共 ${tags.length} 个):`)
|
|
45
|
+
colors.green(`以 '${prefix}' 开头的 tags (共 ${tags.length} 个):`),
|
|
39
46
|
);
|
|
40
47
|
if (tags.length > 5) {
|
|
41
48
|
console.log(colors.dim(" ..."));
|
|
@@ -84,7 +91,7 @@ export async function listTags(prefix?: string): Promise<void> {
|
|
|
84
91
|
|
|
85
92
|
// 9. 计算每列的宽度(取所有 tag 中最长的,至少 20 字符)
|
|
86
93
|
const maxTagLength = Math.max(
|
|
87
|
-
...columns.flatMap((col) => col.tags.map((t) => t.length))
|
|
94
|
+
...columns.flatMap((col) => col.tags.map((t) => t.length)),
|
|
88
95
|
);
|
|
89
96
|
const columnWidth = Math.max(maxTagLength + 4, 20);
|
|
90
97
|
|
|
@@ -99,8 +106,9 @@ export async function listTags(prefix?: string): Promise<void> {
|
|
|
99
106
|
// 11. 打印分隔线
|
|
100
107
|
console.log(
|
|
101
108
|
colors.dim(
|
|
102
|
-
" " +
|
|
103
|
-
|
|
109
|
+
" " +
|
|
110
|
+
"─".repeat(columnWidth * columns.length + (columns.length - 1) * 2),
|
|
111
|
+
),
|
|
104
112
|
);
|
|
105
113
|
|
|
106
114
|
// 12. 打印省略号(如果某列有超过 5 个 tag)
|
|
@@ -248,7 +256,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
248
256
|
const choices: TagChoice[] = prefixWithDate.map(
|
|
249
257
|
({ prefix: p, latest }) => {
|
|
250
258
|
return { name: `${p} (最新: ${latest})`, value: p };
|
|
251
|
-
}
|
|
259
|
+
},
|
|
252
260
|
);
|
|
253
261
|
choices.push({ name: "输入新前缀...", value: "__new__" });
|
|
254
262
|
|
|
@@ -273,7 +281,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
273
281
|
if (!latestTag) {
|
|
274
282
|
const newTag = `${prefix}1.0.0`;
|
|
275
283
|
console.log(
|
|
276
|
-
colors.yellow(`未找到 '${prefix}' 开头的 tag,将创建 ${newTag}`)
|
|
284
|
+
colors.yellow(`未找到 '${prefix}' 开头的 tag,将创建 ${newTag}`),
|
|
277
285
|
);
|
|
278
286
|
const ok = await select({
|
|
279
287
|
message: `确认创建 ${newTag}?`,
|
|
@@ -297,7 +305,7 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
297
305
|
|
|
298
306
|
// 解析版本号,支持预发布版本如 1.0.0-beta.1
|
|
299
307
|
const preReleaseMatch = version.match(
|
|
300
|
-
/^(\d+)\.(\d+)\.(\d+)-([a-zA-Z]+)\.(\d+)
|
|
308
|
+
/^(\d+)\.(\d+)\.(\d+)-([a-zA-Z]+)\.(\d+)$/,
|
|
301
309
|
);
|
|
302
310
|
const match3 = version.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
303
311
|
const match2 = version.match(/^(\d+)\.(\d+)$/);
|
|
@@ -404,33 +412,34 @@ export async function createTag(inputPrefix?: string): Promise<void> {
|
|
|
404
412
|
return;
|
|
405
413
|
}
|
|
406
414
|
|
|
407
|
-
doCreateTag(nextTag);
|
|
415
|
+
await doCreateTag(nextTag);
|
|
408
416
|
}
|
|
409
417
|
|
|
410
|
-
function doCreateTag(tagName: string): void {
|
|
418
|
+
async function doCreateTag(tagName: string): Promise<void> {
|
|
411
419
|
divider();
|
|
412
420
|
|
|
413
421
|
const spinner = ora(`正在创建 tag: ${tagName}`).start();
|
|
422
|
+
const success = await execWithSpinner(
|
|
423
|
+
`git tag -a "${tagName}" -m "Release ${tagName}"`,
|
|
424
|
+
spinner,
|
|
425
|
+
`Tag 创建成功: ${tagName}`,
|
|
426
|
+
"tag 创建失败",
|
|
427
|
+
);
|
|
414
428
|
|
|
415
|
-
|
|
416
|
-
execSync(`git tag -a "${tagName}" -m "Release ${tagName}"`, {
|
|
417
|
-
stdio: "pipe",
|
|
418
|
-
});
|
|
419
|
-
spinner.succeed(`Tag 创建成功: ${tagName}`);
|
|
420
|
-
} catch {
|
|
421
|
-
spinner.fail("tag 创建失败");
|
|
429
|
+
if (!success) {
|
|
422
430
|
return;
|
|
423
431
|
}
|
|
424
432
|
|
|
425
433
|
const pushSpinner = ora("正在推送到远程...").start();
|
|
434
|
+
const pushSuccess = await execWithSpinner(
|
|
435
|
+
`git push origin "${tagName}"`,
|
|
436
|
+
pushSpinner,
|
|
437
|
+
`Tag 已推送: ${tagName}`,
|
|
438
|
+
"远程推送失败",
|
|
439
|
+
);
|
|
426
440
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
pushSpinner.succeed(`Tag 已推送: ${tagName}`);
|
|
430
|
-
} catch {
|
|
431
|
-
pushSpinner.warn(
|
|
432
|
-
`远程推送失败,可稍后手动执行: git push origin ${tagName}`
|
|
433
|
-
);
|
|
441
|
+
if (!pushSuccess) {
|
|
442
|
+
console.log(colors.dim(` 可稍后手动执行: git push origin ${tagName}`));
|
|
434
443
|
}
|
|
435
444
|
}
|
|
436
445
|
|
|
@@ -484,12 +493,14 @@ export async function deleteTag(): Promise<void> {
|
|
|
484
493
|
divider();
|
|
485
494
|
|
|
486
495
|
const spinner = ora(`正在删除本地 tag: ${tagToDelete}`).start();
|
|
496
|
+
const localSuccess = await execWithSpinner(
|
|
497
|
+
`git tag -d "${tagToDelete}"`,
|
|
498
|
+
spinner,
|
|
499
|
+
`本地 tag 已删除: ${tagToDelete}`,
|
|
500
|
+
"本地 tag 删除失败",
|
|
501
|
+
);
|
|
487
502
|
|
|
488
|
-
|
|
489
|
-
execSync(`git tag -d "${tagToDelete}"`, { stdio: "pipe" });
|
|
490
|
-
spinner.succeed(`本地 tag 已删除: ${tagToDelete}`);
|
|
491
|
-
} catch {
|
|
492
|
-
spinner.fail("本地 tag 删除失败");
|
|
503
|
+
if (!localSuccess) {
|
|
493
504
|
return;
|
|
494
505
|
}
|
|
495
506
|
|
|
@@ -504,12 +515,15 @@ export async function deleteTag(): Promise<void> {
|
|
|
504
515
|
|
|
505
516
|
if (deleteRemote) {
|
|
506
517
|
const pushSpinner = ora("正在删除远程 tag...").start();
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
pushSpinner
|
|
510
|
-
|
|
518
|
+
const remoteSuccess = await execWithSpinner(
|
|
519
|
+
`git push origin --delete "${tagToDelete}"`,
|
|
520
|
+
pushSpinner,
|
|
521
|
+
`远程 tag 已删除: ${tagToDelete}`,
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
if (!remoteSuccess) {
|
|
511
525
|
pushSpinner.warn(
|
|
512
|
-
`远程删除失败,可稍后手动执行: git push origin --delete ${tagToDelete}
|
|
526
|
+
`远程删除失败,可稍后手动执行: git push origin --delete ${tagToDelete}`,
|
|
513
527
|
);
|
|
514
528
|
}
|
|
515
529
|
}
|
|
@@ -574,32 +588,40 @@ export async function updateTag(): Promise<void> {
|
|
|
574
588
|
|
|
575
589
|
const spinner = ora(`正在重命名 tag: ${oldTag} → ${newTag}`).start();
|
|
576
590
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
591
|
+
// 获取旧 tag 的 commit 和消息
|
|
592
|
+
const commit = execOutput(`git rev-list -n 1 "${oldTag}"`).trim();
|
|
593
|
+
const message = execOutput(
|
|
594
|
+
`git tag -l --format='%(contents)' "${oldTag}"`,
|
|
595
|
+
).trim();
|
|
596
|
+
|
|
597
|
+
// 创建新 tag(指向同一个 commit)
|
|
598
|
+
let createSuccess: boolean;
|
|
599
|
+
if (message) {
|
|
600
|
+
createSuccess = await execWithSpinner(
|
|
601
|
+
`git tag -a "${newTag}" "${commit}" -m "${message}"`,
|
|
602
|
+
spinner,
|
|
603
|
+
);
|
|
604
|
+
} else {
|
|
605
|
+
createSuccess = await execWithSpinner(
|
|
606
|
+
`git tag "${newTag}" "${commit}"`,
|
|
607
|
+
spinner,
|
|
608
|
+
);
|
|
609
|
+
}
|
|
595
610
|
|
|
596
|
-
|
|
597
|
-
} catch (error) {
|
|
611
|
+
if (!createSuccess) {
|
|
598
612
|
spinner.fail("tag 重命名失败");
|
|
599
|
-
console.log(colors.red(String(error)));
|
|
600
613
|
return;
|
|
601
614
|
}
|
|
602
615
|
|
|
616
|
+
// 删除旧 tag
|
|
617
|
+
const deleteSuccess = await execAsync(`git tag -d "${oldTag}"`, spinner);
|
|
618
|
+
if (!deleteSuccess) {
|
|
619
|
+
spinner.fail("删除旧 tag 失败");
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
spinner.succeed(`Tag 已重命名: ${oldTag} → ${newTag}`);
|
|
624
|
+
|
|
603
625
|
const pushRemote = await select({
|
|
604
626
|
message: "是否同步到远程?",
|
|
605
627
|
choices: [
|
|
@@ -611,17 +633,32 @@ export async function updateTag(): Promise<void> {
|
|
|
611
633
|
|
|
612
634
|
if (pushRemote) {
|
|
613
635
|
const pushSpinner = ora("正在同步到远程...").start();
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
636
|
+
|
|
637
|
+
// 推送新 tag
|
|
638
|
+
const pushNewSuccess = await execAsync(
|
|
639
|
+
`git push origin "${newTag}"`,
|
|
640
|
+
pushSpinner,
|
|
641
|
+
);
|
|
642
|
+
if (!pushNewSuccess) {
|
|
621
643
|
pushSpinner.warn(
|
|
622
|
-
`远程同步失败,可稍后手动执行:\n git push origin ${newTag}\n git push origin --delete ${oldTag}
|
|
644
|
+
`远程同步失败,可稍后手动执行:\n git push origin ${newTag}\n git push origin --delete ${oldTag}`,
|
|
623
645
|
);
|
|
646
|
+
return;
|
|
624
647
|
}
|
|
648
|
+
|
|
649
|
+
// 删除远程旧 tag
|
|
650
|
+
const deleteOldSuccess = await execAsync(
|
|
651
|
+
`git push origin --delete "${oldTag}"`,
|
|
652
|
+
pushSpinner,
|
|
653
|
+
);
|
|
654
|
+
if (!deleteOldSuccess) {
|
|
655
|
+
pushSpinner.warn(
|
|
656
|
+
`远程旧 tag 删除失败,可稍后手动执行: git push origin --delete ${oldTag}`,
|
|
657
|
+
);
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
pushSpinner.succeed(`远程 tag 已同步: ${oldTag} → ${newTag}`);
|
|
625
662
|
}
|
|
626
663
|
}
|
|
627
664
|
|
|
@@ -692,10 +729,10 @@ export async function cleanInvalidTags(): Promise<void> {
|
|
|
692
729
|
let localFailed = 0;
|
|
693
730
|
|
|
694
731
|
for (const tag of invalidTags) {
|
|
695
|
-
|
|
696
|
-
|
|
732
|
+
const success = await execAsync(`git tag -d "${tag}"`, localSpinner);
|
|
733
|
+
if (success) {
|
|
697
734
|
localSuccess++;
|
|
698
|
-
}
|
|
735
|
+
} else {
|
|
699
736
|
localFailed++;
|
|
700
737
|
}
|
|
701
738
|
}
|
|
@@ -704,7 +741,7 @@ export async function cleanInvalidTags(): Promise<void> {
|
|
|
704
741
|
localSpinner.succeed(`本地标签已删除: ${localSuccess} 个`);
|
|
705
742
|
} else {
|
|
706
743
|
localSpinner.warn(
|
|
707
|
-
`本地标签删除: 成功 ${localSuccess} 个,失败 ${localFailed}
|
|
744
|
+
`本地标签删除: 成功 ${localSuccess} 个,失败 ${localFailed} 个`,
|
|
708
745
|
);
|
|
709
746
|
}
|
|
710
747
|
|
|
@@ -724,10 +761,13 @@ export async function cleanInvalidTags(): Promise<void> {
|
|
|
724
761
|
let remoteFailed = 0;
|
|
725
762
|
|
|
726
763
|
for (const tag of invalidTags) {
|
|
727
|
-
|
|
728
|
-
|
|
764
|
+
const success = await execAsync(
|
|
765
|
+
`git push origin --delete "${tag}"`,
|
|
766
|
+
remoteSpinner,
|
|
767
|
+
);
|
|
768
|
+
if (success) {
|
|
729
769
|
remoteSuccess++;
|
|
730
|
-
}
|
|
770
|
+
} else {
|
|
731
771
|
remoteFailed++;
|
|
732
772
|
}
|
|
733
773
|
}
|
|
@@ -736,7 +776,7 @@ export async function cleanInvalidTags(): Promise<void> {
|
|
|
736
776
|
remoteSpinner.succeed(`远程标签已删除: ${remoteSuccess} 个`);
|
|
737
777
|
} else {
|
|
738
778
|
remoteSpinner.warn(
|
|
739
|
-
`远程标签删除: 成功 ${remoteSuccess} 个,失败 ${remoteFailed}
|
|
779
|
+
`远程标签删除: 成功 ${remoteSuccess} 个,失败 ${remoteFailed} 个`,
|
|
740
780
|
);
|
|
741
781
|
}
|
|
742
782
|
}
|
package/src/commands/update.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { execSync } from "child_process";
|
|
2
|
-
import ora from "ora";
|
|
1
|
+
import { execSync, spawn } from "child_process";
|
|
2
|
+
import ora, { Ora } from "ora";
|
|
3
3
|
import boxen from "boxen";
|
|
4
4
|
import semver from "semver";
|
|
5
5
|
import { existsSync, unlinkSync } from "fs";
|
|
@@ -90,8 +90,8 @@ export async function update(currentVersion: string): Promise<void> {
|
|
|
90
90
|
borderStyle: "round",
|
|
91
91
|
borderColor: "green",
|
|
92
92
|
align: "left",
|
|
93
|
-
}
|
|
94
|
-
)
|
|
93
|
+
},
|
|
94
|
+
),
|
|
95
95
|
);
|
|
96
96
|
return;
|
|
97
97
|
}
|
|
@@ -105,7 +105,7 @@ export async function update(currentVersion: string): Promise<void> {
|
|
|
105
105
|
colors.yellow(colors.bold("🎉 发现新版本!")),
|
|
106
106
|
"",
|
|
107
107
|
`${colors.dim(currentVersion)} → ${colors.green(
|
|
108
|
-
colors.bold(latestVersion)
|
|
108
|
+
colors.bold(latestVersion),
|
|
109
109
|
)}`,
|
|
110
110
|
].join("\n"),
|
|
111
111
|
{
|
|
@@ -115,62 +115,95 @@ export async function update(currentVersion: string): Promise<void> {
|
|
|
115
115
|
borderColor: "yellow",
|
|
116
116
|
align: "center",
|
|
117
117
|
width: 40,
|
|
118
|
-
}
|
|
119
|
-
)
|
|
118
|
+
},
|
|
119
|
+
),
|
|
120
120
|
);
|
|
121
121
|
|
|
122
122
|
// 开始更新
|
|
123
|
-
|
|
123
|
+
console.log("");
|
|
124
|
+
console.log(colors.cyan("📦 开始安装新版本..."));
|
|
125
|
+
console.log("");
|
|
124
126
|
|
|
125
127
|
// 根据包管理器选择更新命令
|
|
126
128
|
const updateCommand = usingVolta
|
|
127
129
|
? `volta install ${packageName}@latest`
|
|
128
130
|
: `npm install -g ${packageName}@latest`;
|
|
129
131
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
133
|
-
});
|
|
132
|
+
// 使用 spawn 异步执行,这样可以显示实时输出
|
|
133
|
+
const [command, ...args] = updateCommand.split(" ");
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
const updateProcess = spawn(command, args, {
|
|
136
|
+
stdio: "inherit", // 继承父进程的 stdio,显示实时输出
|
|
137
|
+
});
|
|
136
138
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
updateProcess.on("close", (code) => {
|
|
140
|
+
console.log("");
|
|
141
|
+
|
|
142
|
+
if (code === 0) {
|
|
143
|
+
console.log(colors.green("✔ 更新成功!"));
|
|
144
|
+
|
|
145
|
+
// 清理缓存文件
|
|
146
|
+
clearUpdateCache();
|
|
147
|
+
|
|
148
|
+
console.log("");
|
|
149
|
+
console.log(
|
|
150
|
+
boxen(
|
|
151
|
+
[
|
|
152
|
+
colors.green(colors.bold("✨ 更新完成!")),
|
|
153
|
+
"",
|
|
154
|
+
`新版本: ${colors.green(colors.bold(latestVersion))}`,
|
|
155
|
+
"",
|
|
156
|
+
colors.dim("请执行以下命令验证:"),
|
|
157
|
+
colors.cyan(" hash -r && gw --version"),
|
|
158
|
+
"",
|
|
159
|
+
colors.dim("或重新打开终端"),
|
|
160
|
+
].join("\n"),
|
|
161
|
+
{
|
|
162
|
+
padding: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
163
|
+
margin: { top: 0, bottom: 1, left: 2, right: 2 },
|
|
164
|
+
borderStyle: "round",
|
|
165
|
+
borderColor: "green",
|
|
166
|
+
align: "left",
|
|
167
|
+
width: 40,
|
|
168
|
+
},
|
|
169
|
+
),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
// 更新成功后退出
|
|
173
|
+
process.exit(0);
|
|
174
|
+
} else {
|
|
175
|
+
console.log(colors.red("✖ 更新失败"));
|
|
176
|
+
console.log("");
|
|
177
|
+
console.log(colors.dim(" 你可以手动运行以下命令更新:"));
|
|
178
|
+
console.log(colors.cyan(` ${updateCommand}`));
|
|
179
|
+
console.log("");
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
139
183
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
colors.dim("或重新打开终端"),
|
|
152
|
-
].join("\n"),
|
|
153
|
-
{
|
|
154
|
-
padding: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
155
|
-
margin: { top: 0, bottom: 1, left: 2, right: 2 },
|
|
156
|
-
borderStyle: "round",
|
|
157
|
-
borderColor: "green",
|
|
158
|
-
align: "left",
|
|
159
|
-
width: 40,
|
|
160
|
-
}
|
|
161
|
-
)
|
|
162
|
-
);
|
|
184
|
+
updateProcess.on("error", (error) => {
|
|
185
|
+
console.log("");
|
|
186
|
+
console.log(colors.red("✖ 更新失败"));
|
|
187
|
+
console.log("");
|
|
188
|
+
console.log(colors.dim(" 你可以手动运行以下命令更新:"));
|
|
189
|
+
console.log(colors.cyan(` ${updateCommand}`));
|
|
190
|
+
console.log("");
|
|
191
|
+
console.log(colors.dim(` 错误信息: ${error.message}`));
|
|
192
|
+
console.log("");
|
|
193
|
+
process.exit(1);
|
|
194
|
+
});
|
|
163
195
|
|
|
164
196
|
// 更新成功后退出
|
|
165
197
|
process.exit(0);
|
|
166
198
|
} catch (error) {
|
|
167
|
-
spinner.fail(colors.red("
|
|
199
|
+
spinner.fail(colors.red("获取版本信息失败"));
|
|
168
200
|
console.log("");
|
|
169
|
-
console.log(colors.dim("
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
201
|
+
console.log(colors.dim(" 请检查网络连接后重试"));
|
|
202
|
+
console.log("");
|
|
203
|
+
|
|
204
|
+
if (error instanceof Error) {
|
|
205
|
+
console.log(colors.dim(` 错误信息: ${error.message}`));
|
|
206
|
+
}
|
|
174
207
|
console.log("");
|
|
175
208
|
process.exit(1);
|
|
176
209
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,8 @@ import { commit } from "./commands/commit.js";
|
|
|
29
29
|
import { checkForUpdates } from "./update-notifier.js";
|
|
30
30
|
import { update } from "./commands/update.js";
|
|
31
31
|
import { log, quickLog } from "./commands/log.js";
|
|
32
|
+
import { amendDate } from "./commands/amend-date.js";
|
|
33
|
+
import { amend } from "./commands/amend.js";
|
|
32
34
|
|
|
33
35
|
// ========== 全局错误处理 ==========
|
|
34
36
|
|
|
@@ -109,7 +111,7 @@ async function mainMenu(): Promise<void> {
|
|
|
109
111
|
███╔╝ ██ ██║██╔══╝ ██╔██╗
|
|
110
112
|
███████╗╚█████╔╝███████╗██╔╝ ██╗
|
|
111
113
|
╚══════╝ ╚════╝ ╚══════╝╚═╝ ╚═╝
|
|
112
|
-
`)
|
|
114
|
+
`),
|
|
113
115
|
);
|
|
114
116
|
console.log(colors.dim(` git-workflow v${colors.yellow(version)}\n`));
|
|
115
117
|
|
|
@@ -157,11 +159,19 @@ async function mainMenu(): Promise<void> {
|
|
|
157
159
|
value: "stash",
|
|
158
160
|
},
|
|
159
161
|
{
|
|
160
|
-
name: `[b]
|
|
162
|
+
name: `[b] 📜 查看日志 ${colors.dim("gw log")}`,
|
|
161
163
|
value: "log",
|
|
162
164
|
},
|
|
163
165
|
{
|
|
164
|
-
name: `[c]
|
|
166
|
+
name: `[c] 🕐 修改提交时间 ${colors.dim("gw ad")}`,
|
|
167
|
+
value: "amend-date",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: `[d] ✏️ 修改提交信息 ${colors.dim("gw amend")}`,
|
|
171
|
+
value: "amend",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: `[e] ⚙️ 初始化配置 ${colors.dim("gw init")}`,
|
|
165
175
|
value: "init",
|
|
166
176
|
},
|
|
167
177
|
{ name: "[0] ❓ 帮助", value: "help" },
|
|
@@ -215,6 +225,14 @@ async function mainMenu(): Promise<void> {
|
|
|
215
225
|
checkGitRepo();
|
|
216
226
|
await log();
|
|
217
227
|
break;
|
|
228
|
+
case "amend-date":
|
|
229
|
+
checkGitRepo();
|
|
230
|
+
await amendDate();
|
|
231
|
+
break;
|
|
232
|
+
case "amend":
|
|
233
|
+
checkGitRepo();
|
|
234
|
+
await amend();
|
|
235
|
+
break;
|
|
218
236
|
case "init":
|
|
219
237
|
await init();
|
|
220
238
|
break;
|
|
@@ -378,6 +396,23 @@ cli
|
|
|
378
396
|
return log(logOptions);
|
|
379
397
|
});
|
|
380
398
|
|
|
399
|
+
cli
|
|
400
|
+
.command("amend:date [hash]", "修改指定 commit 的提交时间")
|
|
401
|
+
.alias("ad")
|
|
402
|
+
.action(async (hash?: string) => {
|
|
403
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
404
|
+
checkGitRepo();
|
|
405
|
+
return amendDate(hash);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
cli
|
|
409
|
+
.command("amend [hash]", "修改指定 commit 的提交信息")
|
|
410
|
+
.action(async (hash?: string) => {
|
|
411
|
+
await checkForUpdates(version, "@zjex/git-workflow");
|
|
412
|
+
checkGitRepo();
|
|
413
|
+
return amend(hash);
|
|
414
|
+
});
|
|
415
|
+
|
|
381
416
|
cli
|
|
382
417
|
.command("clean", "清理缓存和临时文件")
|
|
383
418
|
.alias("cc")
|
|
@@ -446,7 +481,7 @@ cli
|
|
|
446
481
|
console.log("");
|
|
447
482
|
console.log(colors.yellow("⚠️ 全局配置文件已删除"));
|
|
448
483
|
console.log(
|
|
449
|
-
colors.dim(` 如需重新配置,请运行: ${colors.cyan("gw init")}`)
|
|
484
|
+
colors.dim(` 如需重新配置,请运行: ${colors.cyan("gw init")}`),
|
|
450
485
|
);
|
|
451
486
|
}
|
|
452
487
|
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { execSync, type ExecSyncOptions } from "child_process";
|
|
1
|
+
import { execSync, spawn, type ExecSyncOptions } from "child_process";
|
|
2
|
+
import type { Ora } from "ora";
|
|
2
3
|
|
|
3
4
|
export interface Colors {
|
|
4
5
|
red: (s: string) => string;
|
|
@@ -98,3 +99,60 @@ export type BranchType = "feature" | "hotfix";
|
|
|
98
99
|
export function divider(): void {
|
|
99
100
|
console.log(colors.dim("─".repeat(40)));
|
|
100
101
|
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 使用 spawn 异步执行命令,避免阻塞 spinner
|
|
105
|
+
* @param command 命令字符串
|
|
106
|
+
* @param spinner 可选的 ora spinner 实例
|
|
107
|
+
* @returns Promise<boolean> 成功返回 true,失败返回 false
|
|
108
|
+
*/
|
|
109
|
+
export function execAsync(command: string, spinner?: Ora): Promise<boolean> {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
const [cmd, ...args] = command.split(" ");
|
|
112
|
+
|
|
113
|
+
const process = spawn(cmd, args, {
|
|
114
|
+
stdio: spinner ? "pipe" : "inherit",
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
process.on("close", (code) => {
|
|
118
|
+
resolve(code === 0);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
process.on("error", () => {
|
|
122
|
+
resolve(false);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 执行命令并在 spinner 中显示进度
|
|
129
|
+
* @param command 命令字符串
|
|
130
|
+
* @param spinner ora spinner 实例
|
|
131
|
+
* @param successMessage 成功消息
|
|
132
|
+
* @param errorMessage 错误消息
|
|
133
|
+
* @returns Promise<boolean> 成功返回 true,失败返回 false
|
|
134
|
+
*/
|
|
135
|
+
export async function execWithSpinner(
|
|
136
|
+
command: string,
|
|
137
|
+
spinner: Ora,
|
|
138
|
+
successMessage?: string,
|
|
139
|
+
errorMessage?: string,
|
|
140
|
+
): Promise<boolean> {
|
|
141
|
+
const success = await execAsync(command, spinner);
|
|
142
|
+
|
|
143
|
+
if (success) {
|
|
144
|
+
if (successMessage) {
|
|
145
|
+
spinner.succeed(successMessage);
|
|
146
|
+
} else {
|
|
147
|
+
spinner.succeed();
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
if (errorMessage) {
|
|
151
|
+
spinner.fail(errorMessage);
|
|
152
|
+
} else {
|
|
153
|
+
spinner.fail();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return success;
|
|
158
|
+
}
|