与 JJ 的对话
摘要
作者分享了使用 jj 版本控制工具与 Git 一起改进 PR 清理工作流程的经验,突出了其历史编辑和撤销等功能,同时指出了一些可用性问题。
<p><a href="https://lobste.rs/s/nbmbxj/conversations_with_jj">评论</a></p>
查看缓存全文
缓存时间: 2026/09/10 18:16
# 与JJ的对话 | 劳伦兹的博客
来源:https://laurmaedje.github.io/posts/jj/
2026年9月1日
到目前为止,我的博客一直只写关于Typst的内容,但今天我想聊聊别的。(这种事本就难得,我觉得最好抓紧写下来。)
我断断续续地使用`jj`(https://www.jj-vcs.dev/latest/)有一阵子了,想分享一些经验。我的学习路径可能不算最佳,但至少我是从阅读Steve的教程(https://steveklabnik.github.io/jujutsu-tutorial/)开始的,而不是盲目上手。不过之后,我就只是在需要时摸索着前进。
也许我应该先说说*为什么*想学它。除了周围和团队里的人总在念叨,我也确实喜欢干净的PR历史。虽然我很擅长交互式变基,但我的PR清理流程依然不够理想——介于交互式变基、在GitHub Desktop里拖动提交,以及在Tower(一款Mac的Git客户端)里来回调整之间。我很喜欢图形界面,尤其是用来查看差异[\[1\]](https://laurmaedje.github.io/posts/jj/#fn1),因此经常在图形工具和终端之间切换(当GUI功能有限时)。所以,我学习jj的目标就是获得一个更好的工具来整理提交历史。
过去六个月左右,我一直让jj与Git并肩工作。它表现得非常好,首先要感谢背后的开发者们!特别是,jj与Git如此互操作非常务实;如果是我,可能会更倾向于“从头开始,抛开所有遗留问题”(参考Typst……)
但有一件事没有发生:我并未完全迁移到它。它仍然只是我的PR清理工具。这没问题,而且可能更多地反映了我自身的习惯而非jj的局限。不过我想,这篇博客依然可以记录我的历程。在此先声明一点:我在工作和工作之外的时间都非常有限(不像大学时),且很少想处理编码相关的事。因此,当我在jj中遇到障碍时,阻力最小的路径就是回到Git。
## 我非常喜欢的方面
**编辑历史。**来回跳跃并编辑提交感觉非常流畅!这让清理工作简单多了,尤其是配合`jjui`(https://github.com/idursun/jjui)(也是个很棒的工具)。我还没完全记住CLI命令,因为我主要使用`jjui`,记住了快捷键,这在大多数情况下就足够了。
**更丰富的操作集。**我欣赏那些为不同概念设计的独立命令。Git的工作方式如今已深入我的脑海,我基本能接受它的逻辑,但我认为将这些高层概念设为独立命令(例如`jj split`)更好。
**撤销功能。**撤销功能也非常酷。当然,在你搞砸需要恢复丢失数据时很有帮助(我在jj上遇到过几次;能力越大,责任越大 :P)。但我发现它也很有用:可以尝试做些修改,看看冲突如何处理,然后反复撤销几次。当然,在Git中我也可以通过记住要返回的SHA或使用reflog来做到,但没这么顺畅。
**工具本身。**CLI感觉……很友好?我说不清。但我就是喜欢它!
## 让我困惑的地方
**日志。**`jj log`的输出有时仍让我有些困惑。在我主要工作的仓库(`typst/typst`)中,默认显示很多其他人的分支(我最近检出它们来审查他们的工作),这使得(a)找到我自己的工作,以及(b)仅查看当前分支的历史(特别是当它截断不可变提交时)变得困难。我尝试调整默认的revset,情况有所改善,但这个问题对我来说是新的;在Git这边我一直没问题。
我了解到很多人在jj中较少使用分支名(我可能说错了)。但我之所以喜欢命名分支,正是因为我可以轻松在不同内容间跳转,而无需通过关联提交来查找。这也引出了一个相关问题:每当我通过Git和JJ来回切换编辑分支时,即使当前编辑的变更是空的,而前一个已加书签,我仍会留在分离头指针状态。这总是在通过Git提交新东西前多了一个要记住的步骤。
而且,正是因为我喜欢分支,*当*我通过JJ添加新提交时(这不常发生,因为我只用它做清理),分支不会自动前进这一点让我有点烦。
**自动快照。**JJ在运行任何命令时都会自动快照磁盘状态。这使得工作成果几乎不可能永久丢失。这很好!然而,实际上,我对此有两个问题。
首先,快照是一个副作用(它可能触发自动变基并影响撤销历史)。这有时会出人意料(特别是通过`jjui`自动触发时),或在结合相同变更合并(见下文)时。对我来说,这意味着在编辑代码之间,我点击一次终端(聚焦`jjui`)或点击两次,结果可能不同。
其次,很容易意外快照像`node_modules`这样的东西。假设你在仓库中添加了一个新的JS项目(将`node_modules`加入了gitignore),然后你回去编辑一个旧提交。接着,哎呀,`node_modules`突然出现在了那个提交里。虽然有一些启发式方法来检测大文件,但并非所有你不想要的文件都是大文件。在我其中一个项目中,我猜`node_modules`仍在jj历史中。我懒得去确认,但这让我有点不爽。
**相同变更合并。**JJ中有一个有趣的合并行为,我认为Git默认也有。然而,我在Git中从未遇到过,可能因为我从未尝试过足够疯狂的变基。
假设你有两个提交A和B。最初,B做了一些修改。现在,你尝试改为在A中做同样的修改,保存并触发jj(例如通过`jjui`或`jj log`)。这会触发自动变基,B中的变更被吸收了。但接着,如果你在A中按Ctrl+Z撤销并再次保存,B中的变更仍然消失了。
相同变更合并行为在许多情况下确实方便,但这让我困惑。幸运的是,你可以配置一个设置来禁用它。就个人而言,我宁愿解决一些额外的冲突,以求心安。
```
[merge]
same-change = "keep"
```
## 我的期望
老实说,我没有全面投入JJ的主要原因可能是,我就是需要好的GUI工具来查看和拆分差异。用`jj`,我喜欢跳跃和修改的能力,但这让我牺牲了阅读差异和选择部分更改的便捷性。虽然`jj split`能完成任务,但如果有好的GUI,我会快得多。当我没有深入历史时,我仍然更喜欢通过GitHub Desktop来拆分最新的提交。总结一下:我希望看到一个真正精致、具有原生感的GUI来支持JJ。我可能会为此付费。
---
1. 终端上可能有很多优秀的差异查看器,但仅仅滚动查看`git diff`的输出对我来说不太行。它无法吸引人细读差异,而我认为这对于写出好代码非常重要。在创建PR的过程中,我会反复阅读自己的差异,既激发重构的想法,也发现潜在的错误。↩︎ (https://laurmaedje.github.io/posts/jj/#fnref1)
相似文章
超越jj:配置与工具生态系统
来自JJ Con 2026的这次演讲总结了jj版本控制系统的配置和工具生态系统,涵盖了内置功能和外部脚本。
Git history 命令值得更多关注
文章重点介绍了新的 `git history` 命令及其 `fixup`、`reword` 和 `split` 子命令,这些命令提供了原子化且感知分支的提交历史编辑功能,带来了类似 jj 的益处,而无需切换版本控制系统。
Jujutsu 如何重新思考 Git 的工作副本和冲突模型
Jujutsu (jj) 是一个新的版本控制系统,它重新思考了 Git 的工作副本和冲突模型,在保持完全 Git 兼容性的同时,提供了更一致的工作流程。
我们最终没有使用jj的(微不足道的)原因
Gradle解释了为何决定不采用Jujutsu (jj)作为其主要版本控制系统:jj不遵循.gitattributes,导致在Windows上像gradlew.bat这样的文件因行尾不匹配而持续出现虚假修改。他们继续使用git worktree替代。
jj v0.43.0 发布
Jujutsu v0.43.0,一个与 Git 兼容的版本控制系统,旨在易用性和强大的历史重写功能,现已发布更新。