大型功能分支最累的地方,经常不是把代码写出来,而是把它讲成一个干净故事。

理想提交应该像目录:类型、数据库、服务端 CRUD、客户端 API、UI,一层层递进。Reviewer 看得轻松,维护者也能追踪设计变化。

现实不是这样。WIP、返工、修 bug、重构、跨层改动,会把早期提交反复覆盖。代码最后能跑,提交历史却碎了。这篇文章里的 Jujutsu 工作流,解决的就是这种 Git 式严谨疲劳。

它不是 Jujutsu 官方发布,也不是“Git 已经过时”的证据。它更像一个承认现场混乱的整理术:开发时先让代码长出来,收尾时再把历史剪成可读版本。

好提交有用,但大型功能天然会弄脏历史

Good Commits 的价值很清楚。它让 review 从“看一坨 diff”变成“按层理解变化”。

理想提交主要内容对 review 的价值
define types定义类型先看数据形状
add DB functions数据库函数单独检查存储逻辑
server CRUD服务端增删改查聚焦后端行为
client API客户端接口检查前后端契约
client UI界面实现最后看交互呈现

问题是,功能开发很少按这个顺序发生。

你可能先写 UI 才发现类型不够用。写完 CRUD 又回头改数据库函数。调试时塞了临时代码,后来修 bug 又覆盖早期提交。顺手重构一个工具函数,还牵动了好几层。

这不是工程师不专业。很多时候,探索本来就是来回走。把探索过程保持成线性叙事,本身就是额外劳动。

对经常做大型功能分支的人,这意味着一件具体事:不要只盯着“每一步都提交得漂亮”。更现实的策略,是先保证最终代码正确,再预留一段专门整理提交历史的时间。

对 reviewer 也是一样。看到一个 PR 历史干净,不代表作者一路优雅推进。更可能是作者已经替你做过一遍编辑。好历史不是自然产物,是维护成本。

这套 Jujutsu 流程:先合成一坨,再按主题分拣

作者的做法可以压缩成三步。

先创建一组理想的空提交,比如 red、blue。它们是目标篮子,代表你希望最终呈现的主题提交。

再把真实开发中的混乱提交区间,比如 messy-first..messy-last,压进一个 everything commit。这个提交装下所有实际改动。

然后用类似 jj squash -i --from messy-first --into red 的交互式操作,把属于 red 的 hunk 挑出来,放回 red。接着处理 blue。直到 everything commit 被掏空。

它和几个常见工具的差别在边界处理上。

方法适合什么主要限制
jj absorb把新改动吸收到旧提交依赖最近触碰文件的归属判断,可能分错主人
jj squash -i手动选择 hunk 合并边界不干净时,容易陷入冲突
jj split把一个提交切开漏掉 hunk 后,还要继续切、合、调整
everything commit 流程先有目标提交,再从大堆里分拣单个整理后提交不保证能编译

这个流程的好处是顺序反过来了。

不是边开发边维护完美历史,而是在最终状态稳定后,再把改动按主题归位。你可以先挑最容易判断归属的 hunk,不必从一开始就把提交切得像手术刀。

它也降低了一类烦人的冲突。everything commit 代表最终状态,自身不会在内部打架。你从这个最终状态里往 red、blue 分拣,很多时候比反复把“修 red 和 green”的中间提交塞回旧历史更顺。

但别把它神化。最大代价很硬:整理后的每个提交未必都能单独编译。

如果团队要求每个 commit 都通过 CI,或者强依赖 git bisect 定位问题,这套方法可能不合适。至少不能原样照搬。它更适合那些把 PR review 可读性放在更高位置、但不强制每个中间提交都可运行的团队。

真正要观察的变量也在这里:你的团队到底把“每个提交可构建”看得多重?CI 是跑整条 PR,还是逐提交跑?reviewer 是按 commit 看,还是只看最终 diff?答案不同,工具选择就不同。

这不是懒人技巧,是把开发和叙事分开

我更在意的不是 Jujutsu 多顺手,而是它戳破了一个行业习惯:我们常把“会写代码”和“会编辑提交历史”混在一起考核。

开发现场需要试错。代码评审需要秩序。两件事都重要,但不是同一种劳动。

很多团队嘴上说“提交要干净”,听起来像工程素养。落到大型功能里,它常常变成另一份工作:删掉弯路,重排顺序,把返工伪装成线性推进。

这不算虚伪。Reviewer 不该被迫读你的思维草稿。维护者也需要一条能追溯的线。

问题在于,工具和流程不该假装开发过程天然整洁。古人说“修辞立其诚”。放到代码里也成立:提交历史当然可以被修辞,但前提是别把修辞成本藏起来。

Jujutsu 这套工作流的价值,就在于把成本摊开。开发阶段付探索成本,收尾阶段付叙事成本。该乱的时候别假装不乱,该交付的时候别把乱交给 reviewer。

对大型功能分支作者,动作很具体:可以在动手前先想好最终提交目录,但不必每一步都维持它。等功能稳定,再用 jj 把 everything commit 分拣回主题提交。

对想试 Jujutsu 的开发者,也别急着迁移整个团队。更稳的方式,是先在个人分支、小型 PR、非强制逐提交 CI 的仓库里试。看三件事:冲突是否减少,review 是否更顺,整理后的提交能否满足团队规则。

这事和早期电影剪辑有点像。不完全一样,但结构相似。拍摄现场不按观众观看顺序发生,剪辑台负责把素材组织成叙事。代码开发也是这样。现场有现场的乱,交付有交付的秩序。

Jujutsu 没有取消这份劳动。它只是让这份劳动更像剪片,而不是让摄影师边拍边剪。

所以结论并不复杂:如果你的团队重视逐提交可构建,这套方法要谨慎;如果你的痛点是大型 PR 难 review,它值得试。真正的分水岭不是 Git 和 jj 谁赢,而是你愿不愿意承认,干净历史本来就是事后编辑出来的。