@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.
- package/CHANGELOG.md +11 -0
- package/README.md +1 -1
- package/dist/index.js +323 -11
- 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/index.ts +39 -4
- package/tests/amend-date.test.ts +364 -0
- package/tests/amend.test.ts +441 -0
|
@@ -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
|
+
- 或在团队内提前沟通
|
package/docs/commands/index.md
CHANGED
|
@@ -6,16 +6,18 @@ Git Workflow 提供了一套简洁而强大的命令,涵盖了 Git 工作流
|
|
|
6
6
|
|
|
7
7
|
### 核心命令
|
|
8
8
|
|
|
9
|
-
| 命令
|
|
10
|
-
|
|
|
11
|
-
| `gw`
|
|
12
|
-
| `gw f`
|
|
13
|
-
| `gw h`
|
|
14
|
-
| `gw c`
|
|
15
|
-
| `gw log`
|
|
16
|
-
| `gw
|
|
17
|
-
| `gw
|
|
18
|
-
| `gw
|
|
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
|
@@ -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
|
+
}
|