@zjex/git-workflow 0.4.2 → 0.4.3

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.
@@ -0,0 +1,380 @@
1
+ # 修改提交信息
2
+
3
+ 修改指定 commit 的提交信息(commit message)。
4
+
5
+ ## 命令
6
+
7
+ ```bash
8
+ # 交互式选择 commit
9
+ gw amend
10
+
11
+ # 直接指定 commit hash
12
+ gw amend a1b2c3d
13
+ gw amend HEAD
14
+ gw amend HEAD~2
15
+ ```
16
+
17
+ ## 使用方式
18
+
19
+ ### 方式 1: 交互式选择(推荐)
20
+
21
+ 不带参数运行,从最近 20 个 commit 中选择:
22
+
23
+ ```bash
24
+ gw amend
25
+ ```
26
+
27
+ ### 方式 2: 指定 commit hash
28
+
29
+ 直接指定要修改的 commit:
30
+
31
+ ```bash
32
+ # 使用短 hash
33
+ gw amend a1b2c3d
34
+
35
+ # 使用完整 hash
36
+ gw amend a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
37
+
38
+ # 使用相对引用
39
+ gw amend HEAD # 最新 commit
40
+ gw amend HEAD~1 # 上一个 commit
41
+ gw amend HEAD~2 # 上上个 commit
42
+ ```
43
+
44
+ ## 交互流程
45
+
46
+ ### 步骤 1: 选择 commit(如果未指定)
47
+
48
+ ```
49
+ ? 选择要修改的 commit:
50
+ ❯ a1b2c3d ✨ feat(auth): 添加用户登录功能 2026-01-19 14:30:00
51
+ d4e5f6g 🐛 fix(auth): 修复登录bug 2026-01-18 10:20:00
52
+ h7i8j9k 📝 docs: 更新文档 2026-01-17 16:45:00
53
+ ...
54
+ ```
55
+
56
+ ### 步骤 2: 显示当前信息
57
+
58
+ ```
59
+ 当前 commit 信息:
60
+ Hash: a1b2c3d
61
+ Message: ✨ feat(auth): 添加用户登录功能
62
+ Date: 2026-01-19 14:30:00 +0800
63
+ ────────────────────────────────────────
64
+ ```
65
+
66
+ ### 步骤 3: 输入新的 commit message
67
+
68
+ ```
69
+ ? 输入新的 commit message: ✨ feat(auth): 实现用户登录和注册功能
70
+ ```
71
+
72
+ **提示**: 默认会显示原来的 message,可以直接编辑
73
+
74
+ ### 步骤 4: 确认修改
75
+
76
+ ```
77
+ 修改预览:
78
+ Commit: a1b2c3d
79
+ 旧 Message: ✨ feat(auth): 添加用户登录功能
80
+ 新 Message: ✨ feat(auth): 实现用户登录和注册功能
81
+ ────────────────────────────────────────
82
+
83
+ ? 确认修改? (y/N)
84
+ ```
85
+
86
+ ## 使用场景
87
+
88
+ ### 场景 1: 修改最新 commit
89
+
90
+ ```bash
91
+ gw amend HEAD
92
+ # 或
93
+ gw amend
94
+
95
+ # 输入新的 message
96
+ # 确认
97
+
98
+ ✔ 修改成功
99
+ ```
100
+
101
+ **特点**: 使用 `git commit --amend`,不会改变 commit hash
102
+
103
+ ### 场景 2: 修改历史 commit
104
+
105
+ ```bash
106
+ gw amend HEAD~3
107
+
108
+ # 输入新的 message
109
+ # 确认
110
+
111
+ ⚠️ 警告: 修改非最新 commit 需要使用 rebase,可能会改变 commit hash
112
+ 这会影响已推送到远程的 commit,请谨慎操作
113
+
114
+ ? 确认修改? (y/N) y
115
+
116
+ 正在执行 rebase...
117
+ ✔ 修改成功
118
+
119
+ ⚠️ 注意: commit hash 已改变
120
+ 如果已推送到远程,需要使用 force push:
121
+ git push --force-with-lease
122
+ ```
123
+
124
+ **特点**: 使用 `git filter-branch`,会改变 commit hash
125
+
126
+ ### 场景 3: 修正拼写错误
127
+
128
+ ```bash
129
+ # 发现最新 commit 的 message 有拼写错误
130
+ gw amend HEAD
131
+
132
+ # 旧: feat: add logn feature
133
+ # 新: feat: add login feature
134
+
135
+ ✔ 修改成功
136
+ ```
137
+
138
+ ### 场景 4: 补充详细说明
139
+
140
+ ```bash
141
+ gw amend HEAD
142
+
143
+ # 旧: fix: bug
144
+ # 新: fix(auth): 修复登录页面在 Safari 浏览器下的兼容性问题
145
+
146
+ ✔ 修改成功
147
+ ```
148
+
149
+ ## 注意事项
150
+
151
+ ### ⚠️ 修改历史 commit
152
+
153
+ - **会改变 commit hash** - 所有后续 commit 的 hash 都会改变
154
+ - **影响团队协作** - 如果已推送到远程,其他人需要重新拉取
155
+ - **需要 force push** - 推送时必须使用 `--force-with-lease`
156
+
157
+ ### 🔒 Force Push
158
+
159
+ 修改已推送的 commit 后,需要强制推送:
160
+
161
+ ```bash
162
+ # 推荐:更安全的 force push
163
+ git push --force-with-lease
164
+
165
+ # 或:强制推送(不推荐)
166
+ git push --force
167
+ ```
168
+
169
+ ### 📝 Commit Message 规范
170
+
171
+ 建议遵循 [Conventional Commits](https://www.conventionalcommits.org/) 规范:
172
+
173
+ ```
174
+ <type>(<scope>): <subject>
175
+
176
+ <body>
177
+
178
+ <footer>
179
+ ```
180
+
181
+ **示例**:
182
+
183
+ ```
184
+ feat(auth): 添加用户登录功能
185
+
186
+ 实现了基于 JWT 的用户认证系统,包括:
187
+ - 登录接口
188
+ - 注册接口
189
+ - Token 刷新机制
190
+
191
+ Closes #123
192
+ ```
193
+
194
+ ## 示例
195
+
196
+ ### 示例 1: 修改最新 commit 的 message
197
+
198
+ ```bash
199
+ $ gw amend HEAD
200
+
201
+ 当前 commit 信息:
202
+ Hash: a1b2c3d
203
+ Message: feat: add feature
204
+ Date: 2026-01-19 14:30:00
205
+
206
+ ? 输入新的 commit message: feat(auth): 添加用户登录功能
207
+
208
+ 修改预览:
209
+ Commit: a1b2c3d
210
+ 旧 Message: feat: add feature
211
+ 新 Message: feat(auth): 添加用户登录功能
212
+
213
+ ? 确认修改? y
214
+
215
+ ✔ 修改成功
216
+ ```
217
+
218
+ ### 示例 2: 修改指定 hash 的 commit
219
+
220
+ ```bash
221
+ $ gw amend d4e5f6g
222
+
223
+ 当前 commit 信息:
224
+ Hash: d4e5f6g
225
+ Message: fix bug
226
+ Date: 2026-01-18 10:20:00
227
+
228
+ ? 输入新的 commit message: fix(auth): 修复登录页面崩溃问题
229
+
230
+ ⚠️ 警告: 修改非最新 commit 需要使用 rebase,可能会改变 commit hash
231
+
232
+ ? 确认修改? y
233
+
234
+ 正在执行 rebase...
235
+ ✔ 修改成功
236
+
237
+ ⚠️ 注意: commit hash 已改变
238
+ 如果已推送到远程,需要使用 force push:
239
+ git push --force-with-lease
240
+ ```
241
+
242
+ ### 示例 3: 交互式选择并修改
243
+
244
+ ```bash
245
+ $ gw amend
246
+
247
+ ? 选择要修改的 commit:
248
+ ❯ a1b2c3d feat: add feature 2026-01-19 14:30:00
249
+ d4e5f6g fix: bug 2026-01-18 10:20:00
250
+
251
+ ? 输入新的 commit message: feat(auth): 添加用户登录和注册功能
252
+
253
+ ? 确认修改? y
254
+
255
+ ✔ 修改成功
256
+ ```
257
+
258
+ ## 相关命令
259
+
260
+ - `gw c` - 提交代码
261
+ - `gw ad` - 修改提交时间
262
+ - `gw log` - 查看提交日志
263
+ - `git commit --amend` - Git 原生修改命令
264
+
265
+ ## 技术细节
266
+
267
+ ### 实现方式
268
+
269
+ **最新 commit**:
270
+
271
+ ```bash
272
+ git commit --amend -m "新的 commit message"
273
+ ```
274
+
275
+ **历史 commit**:
276
+
277
+ ```bash
278
+ git filter-branch -f --msg-filter '
279
+ if [ "$GIT_COMMIT" = "commit-hash" ]; then
280
+ echo "新的 commit message"
281
+ else
282
+ cat
283
+ fi
284
+ ' parent-hash..HEAD
285
+ ```
286
+
287
+ ### 与 git commit --amend 的区别
288
+
289
+ | 特性 | gw amend | git commit --amend |
290
+ | --------------- | -------- | ------------------ |
291
+ | 修改最新 commit | ✅ | ✅ |
292
+ | 修改历史 commit | ✅ | ❌ |
293
+ | 交互式选择 | ✅ | ❌ |
294
+ | 指定 hash | ✅ | ❌ |
295
+ | 预览修改 | ✅ | ❌ |
296
+ | 安全提示 | ✅ | ❌ |
297
+
298
+ ## 常见问题
299
+
300
+ ### Q: 修改 commit message 会改变 commit hash 吗?
301
+
302
+ A:
303
+
304
+ - **最新 commit**: 会改变 hash(因为 message 是 hash 计算的一部分)
305
+ - **历史 commit**: 会改变该 commit 及所有后续 commit 的 hash
306
+
307
+ ### Q: 可以修改多行 commit message 吗?
308
+
309
+ A: 可以,直接在输入时换行即可。但建议使用简洁的单行 message,详细说明放在 body 部分。
310
+
311
+ ### Q: 修改后如何验证?
312
+
313
+ A: 使用 `gw log` 或 `git log` 查看提交历史,确认 message 已更新。
314
+
315
+ ```bash
316
+ gw log
317
+ # 或
318
+ git log --oneline
319
+ ```
320
+
321
+ ### Q: 修改失败怎么办?
322
+
323
+ A: 如果修改历史 commit 失败,可以使用 `git reflog` 找回之前的状态:
324
+
325
+ ```bash
326
+ git reflog
327
+ git reset --hard HEAD@{1}
328
+ ```
329
+
330
+ ### Q: 可以批量修改多个 commit 吗?
331
+
332
+ A: 当前版本不支持批量修改。如需批量修改,建议使用 `git rebase -i` 命令。
333
+
334
+ ### Q: 与 gw c 有什么区别?
335
+
336
+ A:
337
+
338
+ - `gw c` - 创建新的 commit
339
+ - `gw amend` - 修改已有的 commit message
340
+
341
+ ## 最佳实践
342
+
343
+ ### 1. 修改前先检查状态
344
+
345
+ ```bash
346
+ # 查看当前分支和提交历史
347
+ gw log
348
+
349
+ # 确认要修改的 commit
350
+ git log --oneline -5
351
+ ```
352
+
353
+ ### 2. 遵循 commit message 规范
354
+
355
+ 使用 Conventional Commits 格式:
356
+
357
+ ```
358
+ feat: 新功能
359
+ fix: 修复 bug
360
+ docs: 文档更新
361
+ style: 代码格式
362
+ refactor: 重构
363
+ perf: 性能优化
364
+ test: 测试相关
365
+ chore: 其他杂项
366
+ ```
367
+
368
+ ### 3. 修改后及时推送
369
+
370
+ ```bash
371
+ # 修改后立即推送,避免与他人冲突
372
+ git push --force-with-lease
373
+ ```
374
+
375
+ ### 4. 团队协作时谨慎使用
376
+
377
+ 如果 commit 已经被其他人拉取,修改会导致冲突。建议:
378
+
379
+ - 只修改本地未推送的 commit
380
+ - 或在团队内提前沟通
@@ -6,16 +6,18 @@ Git Workflow 提供了一套简洁而强大的命令,涵盖了 Git 工作流
6
6
 
7
7
  ### 核心命令
8
8
 
9
- | 命令 | 别名 | 功能 | 示例 |
10
- | -------- | ----------------- | ------------------- | --------------------- |
11
- | `gw` | - | 显示交互式菜单 | `gw` |
12
- | `gw f` | `feat`, `feature` | 创建 feature 分支 | `gw f --base develop` |
13
- | `gw h` | `fix`, `hotfix` | 创建 hotfix 分支 | `gw h` |
14
- | `gw c` | `cm`, `commit` | 提交代码(AI 模式) | `gw c` |
15
- | `gw log` | `ls`, `l` | 查看提交历史 | `gw log` |
16
- | `gw t` | `tag` | 创建 tag | `gw t v` |
17
- | `gw brd` | `br:del` | 删除分支 | `gw brd feature/old` |
18
- | `gw s` | `st`, `stash` | 管理 stash | `gw s` |
9
+ | 命令 | 别名 | 功能 | 示例 |
10
+ | ---------- | ----------------- | ------------------- | --------------------- |
11
+ | `gw` | - | 显示交互式菜单 | `gw` |
12
+ | `gw f` | `feat`, `feature` | 创建 feature 分支 | `gw f --base develop` |
13
+ | `gw h` | `fix`, `hotfix` | 创建 hotfix 分支 | `gw h` |
14
+ | `gw c` | `cm`, `commit` | 提交代码(AI 模式) | `gw c` |
15
+ | `gw log` | `ls`, `l` | 查看提交历史 | `gw log` |
16
+ | `gw amend` | - | 修改提交信息 | `gw amend a1b2c3d` |
17
+ | `gw ad` | `amend:date` | 修改提交时间 | `gw ad` |
18
+ | `gw t` | `tag` | 创建 tag | `gw t v` |
19
+ | `gw brd` | `br:del` | 删除分支 | `gw brd feature/old` |
20
+ | `gw s` | `st`, `stash` | 管理 stash | `gw s` |
19
21
 
20
22
  ### 辅助命令
21
23
 
@@ -186,6 +188,8 @@ DEBUG=gw:* gw c
186
188
 
187
189
  - [**gw c**](/commands/commit) - 交互式提交(支持 AI 生成)
188
190
  - [**gw log**](/commands/log) - 查看 Git 提交历史(GitHub 风格)
191
+ - [**gw amend**](/commands/amend) - 修改指定 commit 的提交信息
192
+ - [**gw ad**](/commands/amend-date) - 修改指定 commit 的提交时间
189
193
 
190
194
  ### 版本管理
191
195
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zjex/git-workflow",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "🚀 极简的 Git 工作流 CLI 工具,让分支管理和版本发布变得轻松愉快",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,228 @@
1
+ import { execSync } from "child_process";
2
+ import { select, input, confirm } from "@inquirer/prompts";
3
+ import { colors, theme, execOutput, divider } from "../utils.js";
4
+
5
+ /**
6
+ * 格式化日期为 Git 可接受的格式
7
+ * @param date Date 对象
8
+ * @returns Git 日期格式字符串 (YYYY-MM-DD HH:mm:ss)
9
+ */
10
+ function formatGitDate(date: Date): string {
11
+ const year = date.getFullYear();
12
+ const month = String(date.getMonth() + 1).padStart(2, "0");
13
+ const day = String(date.getDate()).padStart(2, "0");
14
+ const hours = String(date.getHours()).padStart(2, "0");
15
+ const minutes = String(date.getMinutes()).padStart(2, "0");
16
+ const seconds = String(date.getSeconds()).padStart(2, "0");
17
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
18
+ }
19
+
20
+ /**
21
+ * 解析用户输入的日期
22
+ * 支持格式:YYYY-MM-DD (默认 00:00:00)
23
+ * @param input 用户输入
24
+ * @returns Date 对象或 null
25
+ */
26
+ function parseDate(input: string): Date | null {
27
+ const trimmed = input.trim();
28
+ const dateMatch = trimmed.match(/^(\d{4})-(\d{2})-(\d{2})$/);
29
+
30
+ if (dateMatch) {
31
+ const [, year, month, day] = dateMatch;
32
+ return new Date(
33
+ parseInt(year),
34
+ parseInt(month) - 1,
35
+ parseInt(day),
36
+ 0,
37
+ 0,
38
+ 0,
39
+ );
40
+ }
41
+
42
+ return null;
43
+ }
44
+
45
+ /**
46
+ * 获取最近的 commits
47
+ * @param limit 数量限制
48
+ * @returns commit 列表
49
+ */
50
+ function getRecentCommits(limit: number = 20): Array<{
51
+ hash: string;
52
+ message: string;
53
+ date: string;
54
+ }> {
55
+ const output = execOutput(`git log -${limit} --pretty=format:"%H|%s|%ai"`);
56
+
57
+ if (!output) return [];
58
+
59
+ return output.split("\n").map((line) => {
60
+ const [hash, message, date] = line.split("|");
61
+ return { hash, message, date };
62
+ });
63
+ }
64
+
65
+ /**
66
+ * 根据 hash 获取 commit 信息
67
+ * @param hash commit hash (可以是短 hash)
68
+ * @returns commit 信息或 null
69
+ */
70
+ function getCommitByHash(hash: string): {
71
+ hash: string;
72
+ message: string;
73
+ date: string;
74
+ } | null {
75
+ try {
76
+ const output = execOutput(`git log -1 ${hash} --pretty=format:"%H|%s|%ai"`);
77
+
78
+ if (!output) return null;
79
+
80
+ const [fullHash, message, date] = output.split("|");
81
+ return { hash: fullHash, message, date };
82
+ } catch {
83
+ return null;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * 修改指定 commit 的提交时间
89
+ * 支持修改 author date 和 committer date
90
+ * @param commitHash 可选的 commit hash
91
+ */
92
+ export async function amendDate(commitHash?: string): Promise<void> {
93
+ console.log(colors.cyan("修改 Commit 提交时间"));
94
+ divider();
95
+
96
+ let selectedCommit: { hash: string; message: string; date: string };
97
+
98
+ // ========== 步骤 1: 确定要修改的 commit ==========
99
+ if (commitHash) {
100
+ // 如果指定了 hash,直接获取该 commit
101
+ const commit = getCommitByHash(commitHash);
102
+ if (!commit) {
103
+ console.log(colors.red(`✖ 找不到 commit: ${commitHash}`));
104
+ return;
105
+ }
106
+ selectedCommit = commit;
107
+ } else {
108
+ // 否则让用户选择
109
+ const commits = getRecentCommits(20);
110
+
111
+ if (commits.length === 0) {
112
+ console.log(colors.yellow("没有找到任何 commit"));
113
+ return;
114
+ }
115
+
116
+ selectedCommit = await select({
117
+ message: "选择要修改时间的 commit:",
118
+ choices: commits.map((c) => ({
119
+ name: `${colors.yellow(c.hash.slice(0, 7))} ${c.message.slice(0, 60)} ${colors.dim(c.date)}`,
120
+ value: c,
121
+ description: c.date,
122
+ })),
123
+ pageSize: 15,
124
+ theme,
125
+ });
126
+ }
127
+
128
+ console.log("");
129
+ console.log("当前 commit 信息:");
130
+ console.log(` Hash: ${colors.yellow(selectedCommit.hash.slice(0, 7))}`);
131
+ console.log(` Message: ${selectedCommit.message}`);
132
+ console.log(` Date: ${colors.cyan(selectedCommit.date)}`);
133
+ divider();
134
+
135
+ // ========== 步骤 2: 输入新日期 ==========
136
+ console.log(colors.dim("输入日期格式: YYYY-MM-DD (如: 2026-01-19)"));
137
+ console.log("");
138
+
139
+ const dateInput = await input({
140
+ message: "输入新的日期:",
141
+ validate: (value) => {
142
+ const parsed = parseDate(value);
143
+ if (!parsed) {
144
+ return "日期格式不正确,请使用 YYYY-MM-DD 格式";
145
+ }
146
+ return true;
147
+ },
148
+ theme,
149
+ });
150
+
151
+ const newDate = parseDate(dateInput)!;
152
+ const formattedDate = formatGitDate(newDate);
153
+
154
+ // ========== 步骤 3: 预览并确认 ==========
155
+ divider();
156
+ console.log("修改预览:");
157
+ console.log(` Commit: ${colors.yellow(selectedCommit.hash.slice(0, 7))}`);
158
+ console.log(` 旧时间: ${colors.dim(selectedCommit.date)}`);
159
+ console.log(` 新时间: ${colors.green(formattedDate)}`);
160
+ console.log(` 修改类型: Author + Committer (两者都修改)`);
161
+ divider();
162
+
163
+ // 检查是否是最新的 commit
164
+ const latestHash = execOutput("git rev-parse HEAD");
165
+ const isLatestCommit = selectedCommit.hash === latestHash;
166
+
167
+ if (!isLatestCommit) {
168
+ console.log(
169
+ colors.yellow(
170
+ "⚠️ 警告: 修改非最新 commit 需要使用 rebase,可能会改变 commit hash",
171
+ ),
172
+ );
173
+ console.log(colors.dim(" 这会影响已推送到远程的 commit,请谨慎操作"));
174
+ console.log("");
175
+ }
176
+
177
+ const shouldProceed = await confirm({
178
+ message: "确认修改?",
179
+ default: false,
180
+ theme,
181
+ });
182
+
183
+ if (!shouldProceed) {
184
+ console.log(colors.yellow("已取消"));
185
+ return;
186
+ }
187
+
188
+ // ========== 步骤 4: 执行修改 ==========
189
+ try {
190
+ if (isLatestCommit) {
191
+ // 最新 commit,使用 amend
192
+ // 使用 --reset-author 确保 Author Date 也被修改
193
+ execSync(
194
+ `GIT_AUTHOR_DATE="${formattedDate}" GIT_COMMITTER_DATE="${formattedDate}" git commit --amend --no-edit --reset-author`,
195
+ { stdio: "pipe", shell: "/bin/bash" },
196
+ );
197
+
198
+ console.log("");
199
+ console.log(colors.green("✔ 修改成功"));
200
+ } else {
201
+ // 非最新 commit,使用 filter-branch
202
+ console.log("");
203
+ console.log(colors.cyan("正在执行 rebase..."));
204
+
205
+ const parentHash = execOutput(`git rev-parse ${selectedCommit.hash}^`);
206
+
207
+ const filterCmd = `git filter-branch -f --env-filter 'if [ "$GIT_COMMIT" = "${selectedCommit.hash}" ]; then export GIT_AUTHOR_DATE="${formattedDate}"; export GIT_COMMITTER_DATE="${formattedDate}"; fi' ${parentHash}..HEAD`;
208
+
209
+ execSync(filterCmd, { stdio: "pipe" });
210
+
211
+ console.log(colors.green("✔ 修改成功"));
212
+ console.log("");
213
+ console.log(colors.yellow("⚠️ 注意: commit hash 已改变"));
214
+ console.log(colors.dim(" 如果已推送到远程,需要使用 force push:"));
215
+ console.log(colors.cyan(" git push --force-with-lease"));
216
+ }
217
+
218
+ console.log("");
219
+ } catch (error) {
220
+ console.log("");
221
+ console.log(colors.red("✖ 修改失败"));
222
+ if (error instanceof Error) {
223
+ console.log(colors.dim(error.message));
224
+ }
225
+ console.log("");
226
+ throw error;
227
+ }
228
+ }