新一代的版本管理工具 Jujutsu 使用实践

在当今软件开发领域,Git 已成为事实上的版本控制标准。虽然 Git 功能强大,但在日常使用中,我们常常会遇到一些令人头疼的情况:比如正在专注开发功能 feature 时,突然需要处理紧急 bug,不得不手忙脚乱地执行 git stash
保存工作;合并远端分支大量代码时,在复杂的 git rebase -i
操作中一步走错,整个提交历史变得混乱不堪;或者提交了一个庞大的 PR 后,被要求拆分成多个小 PR 再提交。如果这些痛点让你感同身受,那么 Jujutsu 值得你关注。这个基于 Rust 开发的新一代版本控制工具,正在改变开发者对版本管理的思维方式。本文将深入探讨 Jujutsu 在日常的开发中如何优雅地实现 Git 的操作,以及它如何通过创新的设计理念提升我们的开发效率和体验。
Jujutsu 简介
Jujutsu( 以下简称 JJ) 是由前 Google 工程师 Martin von Zweigbergk 于 2019 年作为个人兴趣而发起的项目,是一个兼容 Git 的新一代分布式版本控制系统,采用 Rust 开发,目标是简化用户体验并提供更高性能。随着项目的发展,目前 JJ 已成为功能成熟、社区活跃、潜力十足的下一代版本控制系统。
JJ 的核心理念是每次文件修改都会自动创建一个新的 revision(修订版本),你无需手动执行 git add
和 git commit
,所有的修改都会被自动记录和保存。
这种设计带来了几个重要优势:首先,即使忘记提交,你也不会丢失任何工作;其次,你可以在任何时候自由地重组、拆分或合并你的修改历史;最后,冲突不会阻塞你的工作流程,你可以先完成其他任务,稍后再处理冲突。
更重要的是,JJ 与 Git 仓库完全兼容,你可以在现有的 Git 项目中使用 JJ,也可以随时切换回纯 Git 模式。这意味着你可以在个人工作中享受 JJ 带来的便利,同时与使用 Git 的团队成员无缝协作。
下面我们就来一探 JJ 的究竟吧!
基本操作
接下来,让我们通过对比 Git 的常见工作流程,来深入了解 JJ 的使用方法。每个环节我都会先展示 Git 的操作方式,再介绍 JJ 的做法,让大家对 JJ 的用法有更直观的感受。
JJ 的安装和配置非常简单,可以参考官方文档,JJ 的配置存储在 ~/.config/jj/config.toml
文件中,使用 TOML 格式。你可以通过命令行修改配置,也可以直接编辑这个文件,配置示例如下:
1 | [user] |
仓库创建和克隆
通常使用 JJ 集成 Git 仓库,最好的做法是使用 JJ 的 jj git init --colocate
命令,操作命令如下:
1 | # create new git repository |
- 第一种是新建一个 Git 仓库,然后使用
jj git init --colocate
命令将 JJ 集成到 Git 仓库中 - 第二种是克隆一个远程 Git 仓库,然后使用
jj git init --colocate
命令将 JJ 集成到 Git 仓库中
当你运行该命令时,JJ 会在 项目根目录下创建一个 .jj
目录,它和 .git
目录是可以和谐共存的,你可以随时在 Git 和 JJ 之间切换。这种设计让你可以渐进式地采用 JJ,而不用担心影响现有的 Git 工作流程。
如果在使用 JJ 的过程中,由于误操作导致工作区混乱,可以随时删除 .jj
目录,这样项目又会回复成 Git 项目,不会影响现有的 Git 工作流程。
拉取远程代码和合并
在团队协作中,同步远程代码是最常见的操作之一,让我们看看两种工具如何处理这个场景。
Git 操作:
1 | # fetch remote updates |
关于 Merge 和 Rebase 的区别: Merge 策略会保留完整的分支历史,通过创建一个新的合并提交来连接两个分支,这样历史图谱会呈现分叉结构,能够清晰地看到分支的开发过程。而 Rebase 策略则是将你的提交重新应用到目标分支之上,形成一条线性的历史记录,虽然看起来更加整洁清晰,但需要注意的是,这个过程会改写原有的提交历史。

在合并代码过程中,如果是 merge
方式遇到冲突,需要手动解决冲突,然后执行 git add
和 git commit
完成合并。 如果是 rebase
方式遇到冲突,需要手动解决冲突,然后执行 git rebase --continue
完成合并。
JJ 操作:
1 | # fetch remote updates |
其中 current-revision
是当前的 revision,可以通过 jj log
查看,也可以使用 @
符号表示当前的 revision。
什么是 Revision? 在 JJ 中,revision(修订版本)是核心概念,类似于 Git 的 commit 但更智能:你的工作副本本身就是一个 revision,文件修改会自动保存,无需手动 add/commit,每个 revision 有稳定的 Change ID(如
qzsvtxpv
)和可变的 Commit Hash(如e18f7532
);当前工作位置用@
符号标识,支持随时编辑、拆分、合并而不破坏历史完整性。这种设计让你专注代码开发,无需担心提交时机或丢失进度。
在合并代码过程中,如果遇到冲突,JJ 会自动创建冲突标记,但与 Git 不同的是,JJ 不会阻塞你的工作,你可以继续工作,稍后再解决冲突。也可以通过 jj st
查看冲突文件,然后手动解决冲突。
代码修改工作流
这是日常开发中最核心的部分,两种工具的差异在这里体现得最为明显,常见的代码修改工作流如下:
- 首先修改文件
- 然后检查状态
- 然后检查差异
- 然后添加文件
- 然后提交
- 然后创建并切换到新分支
- 最后推送远程仓库
Git 操作:
1 | # 1. change file |
JJ 操作:
1 | # 1. create new working revision |
这里体现了 revision 的好处,你无需再像 Git 那样手动执行 git add
,所有修改都会自动保存到当前的工作 revision 中。另外你也无需像 Git 那样使用 git stash
来缓存工作,因为 JJ 的 revision 是自动保存的,你可以在任何时候回退到之前的 revision。
什么是 Bookmark? JJ 的 bookmark 与 Git 的 branch 功能上类似,但它们不会自动随工作改变移动,Git 的 branch 好比一条自动向前延伸的道路,当你 commit 时,它就自己跟着走。JJ 的 bookmark 更像是你在地图上的一个旗帜标记,当你走远了旗帜还留在原地,只有你自己去把它挪到新地点。这样做的好处是你可以随时创建和删除 bookmark,也可以随时将 bookmark 映射到不同的 revision,而不会影响工作流程。
比如在前面提到的场景,当你提交了庞大的 PR 后(这个 PR 包含了多个 commit),被要求拆分成多个小的 PR 再提交。如果是使用 Git 的话,你需要使用 git cherry-pick
等复杂命令来选择不同的 commit 创建分支,然后再提交 PR。而使用 JJ 的话,你可以使用 jj bookmark
命令来对不同的 revision 创建不同的分支,然后分别对这些分支进行提交 PR 即可,操作相对简单很多。

针对这个场景的 JJ 操作如下:
1 | jj edit <revision-id1> |
- 假设要为某个 commit 创建分支,先使用
jj edit
命令选中该 commit - 然后使用
jj bookmark create
命令创建分支 - 最后使用
jj git push
命令将分支推送到远程仓库 - 重复以上步骤,为其他 commit 创建分支
高级操作
熟悉了基本的工作流程后,让我们深入了解 JJ 的一些高级特性,这些功能将帮助你更高效地管理代码历史。
JJ 最强大的功能之一是灵活的 revision 操作,让你可以随时重组、拆分或合并你的修改历史。
合并 Revision(Squash)
Git 操作:
在 Git 中,合并多个提交通常需要使用交互式 rebase:
1 | # Git: Interactive rebase to squash commits |
JJ 操作:
而在 JJ 中,这个操作更加直观:
1 | # Squash current revision into parent |
jj squash
命令可以将当前的 revision 合并到父 revision 中,也可以将指定的 revision 合并到另一个 revision 中。
假设你已经完成了 feature 的开发,这个 feature 包含了 3 个小的 revision,现在需要将多个小的 revision 合并成一个大的 revision,这种情况下你就可以使用 jj squash
命令进行操作,下面是操作示例:
1 | jj log |
- 首先查看当前的 revision 历史,可以看到有 3 个 revision,当前的 revision 是
@
符号标识的 revision,也就是 revision 1 - 执行
jj squash
命令,会将 revision 1 合并到 revision 2 中 - 再次查看 revision 历史,可以看到 revision 1 已经被合并到 revision 2 中,这时当前的 revision 变成了 revision 2
- 然后再次执行
jj squash
命令,会将 revision 2 合并到 revision 3 中
- 再次查看 revision 历史,可以看到 revision 2 已经被合并到 revision 3 中,最终我们将这 3 个 revision 合并成了 1 个 revision

jj squash
如果在合并过程中遇到冲突,JJ 会自动创建冲突标记,你可以立即处理冲突,也可以稍后再处理冲突。
jj squash
命令不仅适用于合并 revision,也适用于合并 bookmark,因为 bookmark 名称可以作为 revision 的简洁别名出现在任何接受 revision 参数的命令里,这也就意味着你可以快速地使用该命令进行分支的合并。
jj squash
命令还可以将某个 revision 移动到另一个 revision 的前面或后面,只需在目标 revision 前后 new 一个新的 revision,然后使用 jj squash
命令将当前的 revision 合并到新的 revision 中即可。
拆分 Revision(Split)
Git 操作:
Git 中拆分提交相对复杂:
1 | # Git: Split a commit |
JJ 操作:
JJ 的拆分操作更加优雅:
1 | # Interactive split current revision |
使用 jj split
命令需要指定交互式编辑器,JJ 官方推荐使用 Meld 作为交互式编辑器,工具的安装方法可以参考官方文档,安装完成后在 JJ 的配置文件里面添加如下配置:
1 | [ui] |
假设你在一个 revision 中修改了 3 个文件,现在需要将这 3 个文件拆分成 3 个新的 revision,可以使用 jj split
命令进行操作,下面是操作示例:
1 | jj log |
- 首先查看当前的 revision 历史,可以看到只有 1 个 revision
- 再查看当前的工作区状态,可以看到有 3 个文件被修改
- 执行
jj split
命令,会出现 Meld 的 GUI 界面,你可以选择将哪个文件拆分到新的 revision 中

这里我们选中 b.txt
和 c.txt
文件并点击 >
按钮将它们移动到新的 revision 中,然后点击右上角的关闭按钮,这样就会自动创建一个新的 revision,并切换到新的 revision 中。操作完成后,我们再次查看 revision 历史,可以看到已经创建了 2 个新的 revision。
1 | jj log |
重复以上的操作再将 xuwrtktq
这个 revision 拆分成 2 个 revision,这样我们的操作就完成了。
实用技巧
操作历史和回滚(Operation Log)
在日常开发中,我们有时会因为误操作而需要撤销之前的命令,比如错误地删除了分支、执行了错误的 rebase 等。这时候查看操作历史和回滚操作就显得非常重要。
Git 操作:
Git 提供了 reflog
命令来查看引用的历史记录,但它只能追踪特定引用(如分支)的变化:
1 | # show reflog of current branch |
Git 的 reflog 有一些限制:只能追踪引用的变化,不能看到完整的操作历史;分支删除后 reflog 也会丢失;无法查看全局的操作记录。
JJ 操作:
JJ 的操作日志(operation log)功能更加强大,它记录了你在仓库中执行的每一个操作:
1 | # show operation log |
JJ 的操作日志命令有查看、撤销和恢复等操作,让我们通过一个实际例子来看看 JJ 的操作历史功能:
1 | # do some operations |
- 首先执行了两个操作,分别是创建了 2 个新的 revision,一个是
Feature A
,一个是Feature B
,分别在这 2 个 revision 中修改了文件file1.txt
和file2.txt
- 操作完成后当前的 revision 是
Feature B
- 然后使用
jj op restore
命令将操作历史恢复到Feature A
的状态 - 最后可以用
jj log
命令查看 revision 记录,可以看到Feature B
的 revision 的已经被撤销了,只有Feature A
的 revision 被保留了
注意:
jj log
和jj op log
是两个不同的命令,前者是查看 revision 历史,后者是查看操作历史。
JJ 提供了全面的操作记录追踪,不仅记录分支引用的变化,还会完整保存每一个执行的操作。这种设计让你可以精确地撤销任何特定操作而不影响其他工作,同时保证操作历史永不丢失,即使删除了分支也能完整保留。每个操作都会显示完整的命令行参数,让你清楚地了解当时执行了什么操作,极大地提升了操作的可追溯性和安全性。
Cherry-pick 和复制修改
当你有一个大型功能分支,但只想将其中部分提交合并到主分支时,可以使用 Git 的 cherry-pick
命令来选择特定的 commit 并合并到主分支。
Git 操作:
Git 的 cherry-pick 操作如下:
1 | # Git: Apply specific commits |
JJ 操作:
与 git cherry-pick
类似,JJ 也提供了 duplicate
命令来复制特定的 revision 到另一个 revision 中:
1 | # Duplicate a revision |
忽略文件跟踪
JJ 直接使用 .gitignore
文件,与 Git 完全兼容,但对于已经被跟踪的文件,JJ 提供了更便捷的操作:
1 | # Stop tracking a file (but keep it locally) |
jj file untrack
这个命令相当于 Git 的 git rm --cached
命令。
JJ 的不足
前面我们看到了 JJ 的诸多优势,但任何新技术都不是完美的。在实际使用 JJ 的过程中也会经常遇到了一些问题,这些问题可能会影响你的使用体验。
- VS Code、SourceTree 等开发者常用的工具对 JJ 的支持还比较有限,而且 CI/CD 集成也需要额外配置,生态系统不够成熟
- 从 Git 迁移到 JJ 需要重新理解 revision、bookmark 等概念,学习曲线比较陡峭
- 相比 Git 成熟的功能体系,JJ 在 hooks、复杂工作流支持等方面还有不足
尽管有这些问题,我觉得 JJ 仍然值得尝试,但建议采取渐进的方式:
- 首先可以在个人项目中试用,熟悉 JJ 的工作方式
- 如果觉得确实能提升效率,再考虑在小团队中推广
- 对于大型项目或企业环境,可以考虑混合使用的方式——个人开发时用 JJ,团队协作时仍然用 Git
总结
本文通过对比 Git 和 JJ 的日常工作流程,从仓库初始化、配置管理、代码修改到高级操作,全面介绍了 JJ 的核心特性和实际使用方法。我们可以看到,JJ 通过自动保存 revision、灵活的历史管理和非阻塞的冲突处理等创新设计,有效解决了 Git 在日常使用中的诸多痛点。
虽然 Git 目前仍是版本控制的事实标准,但 JJ 展示了版本控制工具的另一种可能性。对于追求高效开发体验的个人开发者,或者需要处理复杂代码审查流程的团队来说,JJ 都值得投入时间去学习和尝试。毕竟,好的工具应该让我们更专注于创造,而不是被繁琐的操作所束缚。
虽然 JJ 还有这样那样的一些问题, 但是 JJ 还在快速发展中,很多现在的问题可能在未来的版本中会得到解决。如果你对版本控制工具的未来发展感兴趣,关注 JJ 的进展是个不错的选择。
参考
关注我,一起学习各种最新的 AI 和编程开发技术,欢迎交流,如果你有什么想问想说的,欢迎在评论区留言。