The git history command
TL;DR · AI 摘要
git history命令通过fixup、reword和split子命令,提供更安全的提交修改方式,无需切换工具。
核心要点
- git history fixup可自动更新所有包含目标提交的分支
- 与jj相比,git history保持原子操作避免半成品状态
- 54和2.55版本新增reword和split子命令
结构提纲
按章节快速跳转。
思维导图
用一张图看清主题之间的关系。
查看大纲文本(无障碍 / 无 JS 友好)
- git history命令
- fixup子命令
- 自动分支更新
- 原子操作
- reword子命令
- 提交信息修改
- 与jj对比
- 无需切换工具
- 功能局限性
金句 / Highlights
值得收藏与分享的关键句。
git history fixup会自动更新所有包含目标提交的本地分支
原子操作特性确保不会产生半成品提交状态
与jj相比缺少对冲突状态的持久化处理能力
git history 命令值得更多关注 - Lalit Maganti
git history 命令值得更多关注
2026年7月13日
·
随笔
在 git 上并行处理大量更改可能会令人痛苦。你最终会在多个分支和提交之间来回切换,运行令人胆战心惊的 rebase -i 命令,稍有不慎(比如打个喷嚏)就可能让你的代码树处于半损坏状态。
最近 jj(git 的替代工具)经常被讨论(1, 2, 3, 4),并常被宣传为解决方案。虽然我对 jj 所要解决的问题非常认同,但它的实现方式并没有真正打动我。过去1.5年里,每3个月我都会尝试使用它几天,努力将其融入工作流程,但最终还是放弃并回到 git。1
这就是 git history 命令的用武之地。这是一个实验性命令,分两个版本发布:2.54(4月,重写和拆分子命令)和 2.55(6月,fixup 子命令)。每次发布时都曾引发大量关注,但据我所知,此后社区讨论几乎停滞。这很遗憾,因为在我看来,它已经提供了许多人推崇 jj 的诸多优势,而无需彻底改变工作流程。更棒的是,它是 git 核心发行版的一部分,无需安装任何东西即可尝试。
该命令包含三个子命令:fixup、reword 和 split。
fixup
git history fixup 用于修复包含错误的旧提交,然后自动将所有分支变基为匹配状态。
你像平常一样使用 git add 阶段化修改,然后运行 git history fixup <commit> 将这些已暂存的更改合并到目标提交中。这类似于 git commit --fixup 加上自动变基,但额外的魔法在于它还会更新包含该提交的任何其他分支。
这部分功能比 git rebase --update-refs 更强大,后者仅移动你正在变基的范围内的引用。git history 则会查找并重写所有源自该提交的本地分支(同时提供限制仅当前分支的选项)。但另一方面,在存在合并提交的情况下它无法工作,这可能对某些 git 使用场景来说是个致命缺陷。
实际操作示例如下:
在修复 B 提交前:
执行 git history fixup B 后:
B* 是已合并修复的 B。重写提交会生成新哈希,因此 C 和 D 会自动重建为 C* 和 D*,feat-1 和 feat-2 分支尖端也会随之移动。
这三个命令最重要的特性是原子性:它永远不会让代码树处于半损坏状态。它通过拒绝任何可能导致冲突的操作来实现这一点。
需要明确的是,这比 jj 的能力要弱。jj 将冲突视为一等公民,可以在变基过程中携带冲突状态并让你稍后解决。git history 目前尚未实现此功能,但文档留有余地:
“此限制是设计使然,因为历史重写不打算作为有状态的操作。如果 Git 学会了如何处理一等冲突,这一限制可以解除。”
基本上,这一限制未来可能会改变,期待看到是否真的实现!
reword
git history reword 用于更新旧提交的提交信息,并自动将所有后续提交变基。当你在迭代过程中设计发生变更需要回溯修改提交信息时,这个功能非常有用。
git history reword <commit> 会用该提交现有的提交信息打开你的编辑器。你编辑后保存,其余提交栈会重新构建在顶部,分支也会随之更新。这与 fixup 的作用类似,区别在于 reword 用于修改提交信息而非提交内容。
由于它仅修改信息,reword(与之后的 split 类似)完全不会触碰索引或工作区;它纯粹基于提交图进行操作。因此两者都能在不干扰当前工作内容的情况下,重写未被检出分支上的提交。
执行前:
执行 git history reword B 后:
仅 B 的提交信息发生变化,但这也导致它获得新的哈希值,因此 C 会被重新构建为 C*,feat-1 分支也会随之更新。
拆分
git history split 会将一个提交拆分为两个,交互式地选择每个提交中需要保留的内容。这相当于 git add -p,但无需进行复杂的 git rebase 操作。我发现这是三个功能中最专用的,但在需要时却非常有价值。
具体来说,git history split <commit> 会进入该提交的 diff 的逐块提示界面。你保留的块组成第一个提交,其余内容会形成第二个提交。
执行前,B 提交包含两个无关更改:
执行 git history split B 后:
B 拆分为 B1 和 B2,C 会基于这两个提交重新构建为 C*。
结论
根据使用 jj 的人数来看,我认为仍存在一些关键的思维转变尚未实现。需要明确的是,git history 并未完全弥补 jj 的所有功能:jj 仍然提供易于撤销的操作日志,将工作副本建模为提交,并能在变基过程中处理冲突,这些都不是 git history 的目标。
但目前来看,git history 在采纳吸引人们使用 jj 的诸多特性方面迈出了重要一步,它已经集成在我每天使用的工具中。文档的编写方式也让我对后续版本的改进充满期待!
如果你喜欢这篇文章,可以订阅我的通讯或通过 RSS 关注。你也可以在 Hacker News 或 Lobsters 上分享这篇文章。
#
19:40
/
#git
- 如果有兴趣,我很乐意写一篇关于我使用体验的文章。 ↩︎