超越jj:配置与工具生态系统
摘要
来自JJ Con 2026的这次演讲总结了jj版本控制系统的配置和工具生态系统,涵盖了内置功能和外部脚本。
<p><a href="https://lobste.rs/s/xgdt4u/beyond_jj_config_tools_ecosystem">评论</a></p>
查看缓存全文
缓存时间: 2026/09/20 12:12
# 超越 jj:配置与工具生态系统
来源:https://andre.arko.net/2026/09/16/beyond-jj-config-and-tools-ecosystem/
2026年9月16日
本文最初是在JJ Con 2026 (https://git-merge.com/) 上进行的演讲。演讲幻灯片 (https://speakerdeck.com/indirect/beyond-jj-config-and-tools-ecosystem) 也可在此获取。
你好!欢迎来到“超越 jj”,在这里我们将一起探索 jj 生态系统:那些专为与 jj 协同工作而设计的命令、配置和工具。这在一定程度上是我去年在 JJCon 演讲 (https://andre.arko.net/2025/09/28/stupid-jj-tricks/) 的续篇,去年我调研了社区中的 jj 配置情况,不过我们稍后会再深入。
我来讲解 jj 的实际资格基本可以归结为“我喜欢尝试新事物,并且对 jj 充满热情”。而不切实际的资格则在于“Steve Klabnik 曾经当过我的室友,所以我可以请他把我的想法写进 jj 的官方文档”。提前感谢了,Steve!
如前所述,这个演讲部分延续了去年的内容,当时我们深入探讨了 jj 配置的方方面面,从设置姓名,到模板、修订集、命令和别名。我们甚至简要提及了那些包裹 shell 脚本、再包裹 Python 脚本以自动化工作流的复杂别名。
今年,我将较少谈论 jj 配置本身,而是更多探讨一个更大的问题:当你有了 jj 之后,你可以用它做什么?我们将首先看看纯 jj 能做的事情,然后是通过配置 jj 能做的事情,最后探讨一些完全在 jj CLI 之外完成的事情。
其中一些内容在 jj 文档中有所提及,一些在 jj wiki 上,另一些在 awesome jj 仓库里,但此前没有任何来源将它们全部汇集在一起。此外,我还有一大堆补充内容,这些在上述任何地方都没有列出。
### jj 内部
即使你不添加任何额外工具、辅助程序或脚本,仅仅使用 `jj` 你也能做到非常多事情。通过添加自定义模板、修订集和别名,你可以缩短或自动化相当多的操作。除了我们去年讨论过的 jj 配置外,我想重点介绍的是那些已经内置到 jj 核心功能中的、以前需要外部脚本或工具才能实现的功能。
首先,我们来谈谈从配置升级为内置的功能。
#### jj bookmark advance / jj b a
去年,我介绍过 `jj tug`,这是一个别名,用于查找最近的书签并将其移动到最近的可推送更改上。我很高兴地报告,今天我们不再需要 `tug` 了,因为 `jj bookmark advance`(以及快捷方式 `jj b a`)现在实现了与 tug 相同的功能。默认情况下,书签前进会将书签推进到工作副本。如果你更喜欢只推进到最近可提交更改的 tug 版本,可以配置 `revsets.bookmark-advance-to`,将其设置为相同的可推送修订集。
#### jj bisect run
jj bisect 工具引入了一项以前需要包装脚本的功能,你现在可以运行 bisect run 来自动找到你试图定位的更改。以我个人的观点,这曾经是 jj 与 git 之间一个重要的功能差距,所以我很高兴看到它现在被集成进来,无需费力去寻找脚本。
#### jj run
如果你不需要二分查找,但仍然想运行脚本来修改修订集中的每个更改呢?这就是 `jj run` 的用途:给它一个脚本和一个修订集,脚本就会运行,并且如果任何文件被修改,每个更改都会被更新。整个更改树将保持不变(或者从另一个角度看,随着运行过程在每个更改上进行,它会自动变基)。
#### jj fix
等等,你可能在想。`jj fix` 和 `jj run` 不是做同样的事情吗?接受一系列更改,并通过(可能)修改这些更改中的文件来更新它们?是,但又不完全是。
`jj fix` 专门用于只对已更改的文件进行修改。它不会检出每个更改,并且一次只向脚本提供一个文件,同时接受修改后的文件版本作为输出。
如果你需要一组检出到磁盘的文件来运行脚本,`jj fix` 无法做到。但如果你想要的是追溯性地对整个修订集中每个更改过的文件应用格式化工具或代码检查器,`jj fix` 将比 `jj run` 快得多。
#### jj tag
`jj tag` 是功能从外部(不是配置或脚本,而是直接从 git 本身)迁移到 jj 内部的一个例子。你现在不必再使用 `git tag` 来管理你的标签了,你可以使用 `jj tag set` 和 `jj git push --all` 来推送你所有的书签和标签。(你也可以用 `jj git push --tag NAME` 推送单个标签)。现在我们有了 `jj tag`,我日常工作中已经完全不需要运行 git 命令了。相比去年,这是一个巨大的进步!
#### jj arrange
`jj arrange` 就像是拥有一个交互式的 `git rebase -i`。你不需要编辑文本文件,可以直接在类似日志的图中选择或移动更改。如果你不想运行三条命令来查找名称并移动最近的更改,这可以节省大量时间。
[ jj arrange TUI 的截图 ]
#### jj converge
converge 命令是最新但可能也是最有用的。任何时候你在两个不同的地方更新一个更改,它都可能产生分歧。一年前我演讲时,处理分歧的更改非常痛苦——我们没有 /N 语法来轻松引用分歧的每一方,而且很容易因为同时从两个不同的终端窗口运行命令,或者拉取远程分支,而意外导致分歧。
如今,引用分歧更改变得容易多了,这有望使得变基或合并分支变得简单,你甚至可能不需要那样做!converge 命令尝试获取两个分歧的更改流,并将它们合并成一个单一的非分歧更改流。这可能会产生合并冲突,但合并冲突总比需要手动协调两个独立分支要好。我个人对拥有 converge 并能够在将来使用它感到非常兴奋。
### 围绕 jj
现在,让我们超越完全内置于 jj 的功能,来看看那些与 jj CLI 集成的工具。
#### 子命令别名
我想谈论的第一种集成是一种令人难以置信的技巧,它允许 jj 别名拥有子命令。这来自 @tjjfvi 在 issue #6611 (https://github.com/jj-vcs/jj/issues/6611#issuecomment-2960602347) 上的评论。核心思路是,jj 允许你定义一个包含空格的别名命令,这样你就可以构建一个 jj → jj → jj → bash → jj 的执行流程。
```
aliases.subcommand = ["util", "exec", "--", "bash", "-c", 'jj "$0 $1" "${@:2}"']
aliases.foo = ["subcommand", "foo"]
aliases."foo bar" = ["..."]
aliases."foo baz" = ["..."]
```
设置好这个配置后,你可以运行:
```
jj foo bar ARGS
```
这将运行名为 `foo` 的别名,但这也是一个别名,所以它会转化为:
```
jj subcommand foo bar ARGS
```
然后 `subcommand` 也是一个别名,因此扩展为:
```
jj util exec -- bash -c 'jj "foo bar" ARGS'
```
当然,在你剥离外层的 jj 和 bash 之后,最终执行的命令是:
```
jj "foo bar" ARGS
```
这正是我们定义的子命令别名,于是我们的子命令就运行起来了!这完全就是“骚操作”,但我喜欢它。
希望明年我能报告说,我们现在对某种子命令有了内置支持。但即使没有,我们目前也能成功配置自己的子命令了!
#### 类似 `git push` 的功能
另一类非常流行的别名(尽管它们并不总是完全相同)是手动重建 `git push`。这通常意味着:
1. 某种预检查(运行钩子?可推送?书签?)
2. 某种 `tug`,不对,等等,是 `bookmark advance`
3. 某种将书签推送到源的操作
4. 如果未跟踪,则跟踪该书签
```
publish = ["util", "exec", "bash", "--", "-c", '''
jj git-hooks-run-pre-push @ &&
jj bookmark advance -t 'closest_pushable(@)' &&
jj git push -b 'closest_bookmark(@)' &&
jj track-if-untracked 'closest_bookmark(@)'
''']
```
我目前认为,这现在是 jj 内置功能中最大的缺口。我意识到你可以用 `push -c` 创建并推送一个分支,但这不会给你一个便于讨论或评审的人类可读名称。
如果附近有位勇于贡献的英雄,希望在不久的将来为 jj 做贡献,那么如果您能就 pre-push 钩子达成共识,并推出一个将所有这些功能打包在一起的内置命令(将你客户端的最新更改发送到当前仓库的后端),我和许多人都会对你赞不绝口。我个人喜欢 `jj publish`,因为这样可以避免与 `git push` 混淆。
#### 别名目录
我将通过向大家推荐 jj 别名的网页目录 (https://www.lysator.liu.se/~axl/jj-aliases/) 来结束关于配置和别名的讨论。添加你自己的别名!为现有的别名投票!找到一些你无法离开的新别名,然后请愿将它们添加到 jj 核心功能中。可能性是无限的。
[ jj 别名网页目录的截图 ]
在这里查看:`jj` 别名的网页目录 (https://www.lysator.liu.se/~axl/jj-aliases/)
#### 平台支持
我想研究的最后一个与 jj 集成的领域是代码托管平台的支持。自去年以来,这方面有一些明显的进展,但很可能是未来增长空间最大的领域。目前,有三个平台明确地文档化或实现了对 jj 风格开发的支持。
在 GitHub 上,这指的是他们称为“堆叠 PR (stacked PRs) (https://docs.github.com/en/pull-requests/get-started/about-stacked-prs)”的功能。GitHub 构建了服务器端和 `gh` CLI 支持,用于推送一组相互依赖的 PR。这比过去创建相互指向的 PR 链稍微智能一些。也有一些独立的 jj 工具已经可以帮助将 jj 堆栈集成到常规 GitHub PR 中,我们稍后会介绍它们。
另一种 GitHub 集成风格是来自 Erisera 的 JJHub (https://jjhub.erisera.com/),它自称是“GitHub 上的一个覆盖层”。这个覆盖层提供了一致的更改 ID、技能以及供代理使用的 MCP 服务器。
[ jjhub 首页截图 ]
Radicle 是一个点对点的代码托管平台,他们撰文介绍了如何将其补丁请求(更接近 git 邮件流程,而非明确的堆叠方式)与 jj 一起使用。他们发布的流程 (https://radicle.dev/2025/08/14/jujutsu-with-radicle) 展示了如何使用 jj 在 Radicle 中创建和修订补丁,直到它们被接受并合并。
今天,我个人最兴奋的平台是 Tangled。Tangled 是一个托管在 tangled.org 的公共平台,基于 ATProto 身份构建。你控制你的身份和关于你行为的数据,并且你可以在所有 ATProto 应用程序中维护一个单一的身份,包括用于发帖的 Bluesky、用于代码仓库的 Tangled、用于博客的 Leaflet,以及围绕用户在 Web 上拥有自己数据而不断增长的在线工具生态系统。
Tangled 一直走在支持 jj 的最前沿,包括明确支持“堆叠 (stacking) (https://blog.tangled.org/stacking/)”,它使用 jj 更改 ID 来允许评审 PR 不同版本之间的差异。然而,不仅仅是支持更改 ID,Tangled 在 next.tangled.org 上有一个新的评审功能处于公开预览阶段,我认为它对 jj 有完全的支持。它允许评审单个修订或修订之间的差异,同时允许评审任何一组更改,并明确跟踪 jj 更改 ID。
[ tangled PR 评审界面截图 ]
在我看来,Tangled 是我们目前拥有的最接近“jj 原生”的平台。我认为你应该试试它。
最后,还有一类平台:即将推出但尚未对公众开放的平台。排在最前面的是 East River Source Control (https://ersc.io/),我预计我们很快都会尝试使用这个项目。
除了 ERSC,还有一些项目已经发布了网站,并表示你可以申请试用它们的产品。我知道的有 revset.dev (https://revset.dev/)、juju.bi (https://juju.bi/) 和 vex.sc (https://vex.sc/)。我还没有获得这些网站的早期访问权限,但我在这里提及它们,以防你有兴趣尝试闪亮的新技术(或即将推出的技术)。你可能确实有兴趣,毕竟你在 JJCon 会上。
如果你知道任何其他平台或开发项目能更好地支持 jj 与现有平台的集成,请告诉我!我稍后会将更新添加到这篇博客文章中。
### jj 之外
最后,我想带你领略 jj 本身之外存在的景观。这意味着那些旨在与 jj 仓库一起使用、但无需你运行 jj CLI 的应用程序、脚本和工具。
首先,我要指出,其中一些工具甚至在官方 jj discord 中拥有自己的专属频道。无论这些工具是否有这样的频道,我都试图将它们包含在我的列表中,但仅仅浏览一下 jj discord 就可能是开始使用 jj GUI 或 TUI 的一种简单方式,因为你可以与其他用户和维护者交流。
#### GUI
第一类工具是意料之中的 GUI。如果你使用 git 已经很长时间,你可能熟悉经典的 gitk (https://git-scm.com/docs/gitk)、macOS 上的 fork gitx (https://gitx.frim.nl/),甚至是较新的 GUI 如 GitTower (https://www.git-tower.com/)、Fork (https://fork.dev/) 或 Retcon (https://retcon.app/)。让我们来看看一些专门为 jj 仓库和 jj 命令构建的 GUI。
- **gg**,来自 https://github.com/gulbanana/gg
[ gg 的截图 ]
gg(据我所知)是最古老的 jj GUI,使用 Rust 编写,并使用 Tauri 为 Linux、Windows 和 macOS 构建应用程序,同时还提供网页选项。gg 对自身的定位是:“如果你总是处于交互式 rebase 的中间,但实际上这是一件好事,会怎样?”
- **jayjay**,来自 https://github.com/hewigovens/jayjay
[ jayjay 的截图 ]
JayJay 是一个较新的 GUI,自称是“快速、对键盘友好的 Jujutsu 客户端”。在 macOS 上,它基于 SwiftUI 框架构建;在 Linux 上,它基于 Zed 的 GPUI 框架构建。
- **lightjj**,来自 https://github.com/chronologos/lightjj
[ lightjj 的截图 ]
lightjj 自称为“快速强大的 jujutsu UI”。它创建一个 Web 服务器,允许你通过 SSH 端口转发从本地或远程仓库使用 GUI。lightjj 提供的最有趣功能包括三窗格差异视图、冲突解决、分歧解决以及用于差异的 Markdown 渲染。
我还要在这里特别介绍两个更新、较小的项目。荣誉提名:
- **jjewel**,来自 https://checksimsoftware.com/jjewel/
jjewel 是一个较新的 GUI,面向 macOS,提供原生 UI 以及对你的更改图进行拖放操作。
- **weiff**,来自 https://github.com/isgj/weiff
weiff 是一个较新的 Web GUI 创建项目,类似于 lightjj,但正在积极开发中。
虽然 jj 的 GUI 环境目前还没有 git 那么丰富(但未来会有!),但围绕 jj 有很多热情和活动,我预计随着 jj 的普及,这些 GUI 之外还会有更多出现。
#### TUI
这是 jj 时代真正的增长点,拥有大量出色的 TUI 库,如 Go 的 Charm 和 Rust 的 Ratatui,以及其他许多。这意味着 TUI 是多样性和工具更多的领域。
相似文章
与 JJ 的对话
作者分享了使用 jj 版本控制工具与 Git 一起改进 PR 清理工作流程的经验,突出了其历史编辑和撤销等功能,同时指出了一些可用性问题。
Jujutsu 如何重新思考 Git 的工作副本和冲突模型
Jujutsu (jj) 是一个新的版本控制系统,它重新思考了 Git 的工作副本和冲突模型,在保持完全 Git 兼容性的同时,提供了更一致的工作流程。
jj v0.41.0 发布
Jujutsu (jj) v0.41.0 已发布,这款实验性版本控制系统迎来了更新,旨在提升易用性和冲突处理能力。
jj jj jj jj jj
一篇博客文章演示了如何通过在 jj 配置文件中创建递归别名,并利用 'util exec --' 传递参数,来修复在 jj 版本控制系统中意外输入 'jj jj' 时出现的错误。
Evan的Jujutsu教程
面向熟悉Git的用户,关于版本控制系统Jujutsu (jj)的简明教程。