git history 命令借鉴 jj 特性集成于 Git未来改进值得期待在 Git 里并行处理大量变更可能会很痛苦你得不断在不同分支和提交之间切换还得运行令人提心吊胆的 rebase -i 命令有时候一个小失误就可能让你的代码库陷入半损坏状态。如今git 的替代工具 jj 备受讨论还常被视为解决之道。虽然认同 jj 试图解决的问题但它的解决方式却不太合我意。过去一年半里我每三个月就会试用它几天真心想把它融入我的工作流程可最终还是放弃回到了 git 的怀抱。这时git history 登场了。它是一个 **实验性** 命令分两个版本推出2.544 月推出 reword 和 split 子命令和 2.556 月推出 fixup 子命令。每次发布时它都会引发一阵关注但之后社区里就没什么讨论了。这挺可惜的因为在我看来它已经具备了很多人们夸赞 jj 的优点而且无需你彻底改变工作流程。更棒的是它是 Git 核心发行版的一部分你无需额外安装就能试用。它有三个子命令fixup、reword 和 split。fixup[git history fixup](https://git-scm.com/docs/git-history#Documentation/git-history.txt-fixupcommit) 可以修复有问题的旧提交然后自动对所有分支进行变基操作以保持一致。你像往常一样用 git add 暂存修复内容然后运行 git history fixup 将暂存的更改合并到目标提交中。这有点像 git commit --fixup 加上自动压缩变基但更厉害的是它还会更新包含该提交的其他分支。这一点比 git rebase --update-refs 更强大后者只移动你正在变基范围内的引用。而 git history 会找到并重写从该提交派生的 **每个** 本地分支也可以选择只限制在当前分支。不过它在有合并提交的情况下无法工作这对于某些使用场景来说可能是个致命问题。下面是它的实际工作方式在为 B 暂存修复之前运行 git history fixup B 之后B* 是合并了修复内容的 B。重写提交会生成新的哈希值所以 C 和 D 会自动在其基础上重新创建为 C* 和 D*feat-1 和 feat-2 分支指针也会随之移动。这三个命令的一个重要特性是原子性它们绝不会让你的代码库处于半损坏状态。这是通过拒绝任何可能导致冲突的操作来实现的。需要说明的是它的功能严格来说比 jj 弱。jj [将冲突视为头等大事](https://docs.jj-vcs.dev/latest/conflicts/)所以它可以在变基过程中保留冲突状态让你稍后再处理。git history 目前还做不到这一点但 [文档](https://git-scm.com/docs/git-history#_limitations) 留有余地“此限制是设计使然因为历史重写并非旨在成为有状态的操作。一旦如果Git 支持原生冲突处理此限制可能会被解除。”所以这个限制未来可能会改变很期待看到会有怎样的变化reword[git history reword](https://git-scm.com/docs/git-history#Documentation/git-history.txt-rewordcommit) 可以更新旧提交的提交信息并自动对其上的所有内容进行变基操作。当你在迭代过程中设计发生变化时用它回去修改提交信息非常有用。运行 git history reword 会打开编辑器显示该提交的现有信息。你编辑并保存后后续的提交会在其基础上重新构建分支指针也会随之移动。这和 fixup 类似只不过它处理的是提交信息而非代码内容。由于它只修改信息reword和后面的 split 一样完全不会触及你的索引或工作树它只在提交图上操作。所以你可以在不切换分支的情况下重写提交而不会干扰你正在进行的工作。之前运行 git history reword B 之后只有 B 的信息改变了但这仍会生成新的哈希值所以 C 会在其基础上重新构建为 C*feat-1 分支指针也会随之移动。split[git history split](https://git-scm.com/docs/git-history#Documentation/git-history.txt-splitcommit--pathspec) 可以将一个提交拆分为两个让你交互式地选择每个提交包含的内容。这相当于 git add -p但无需在 git rebase 上折腾。我发现这是三个命令中最具专业性的一个但在需要时却非常有用。具体来说运行 git history split 会让你逐块处理该提交的差异。你保留的块组成第一个提交其余的组成第二个提交。之前B 包含两个不相关的更改运行 git history split B 之后B 变成了 B1 和 B2C 会在它们的基础上重新构建为 C*。总结从使用 jj 的人数来看我觉得自己可能还没完成某种关键的思维转变。需要明确的是git history 并不能完全弥补差距jj 仍然提供了 [易于撤销的操作日志](https://docs.jj-vcs.dev/latest/operation-log/)、[将工作副本建模为提交](https://docs.jj-vcs.dev/latest/working-copy/)还能 [在变基过程中处理冲突](https://docs.jj-vcs.dev/latest/conflicts/)而这些都不是 git history 目前要做的。但就目前而言git history 在借鉴吸引人们使用 jj 的诸多特性方面迈出了 **一大步**而且它已经集成在我每天都用的工具里了。从文档的表述来看我有理由期待未来的版本会有更多改进如果你喜欢这篇文章可以订阅我的时事通讯或者通过 [RSS](/index.xml) 关注我。你也可以在 [Hacker News](https://news.ycombinator.com/submitlink?uhttps%3a%2f%2flalitm.com%2fpost%2fgit-history%2ftThe%20git%20history%20command%20deserves%20more%20attention) 或 [Lobsters](https://lobste.rs/stories/new?urlhttps%3a%2f%2flalitm.com%2fpost%2fgit-history%2ftitleThe%20git%20history%20command%20deserves%20more%20attention) 上分享它。