2026年8月2日 1 分钟阅读

Git 用户上手 Jujutsu(jj):没有暂存区、工作区即提交,版本控制工作流会怎样变化?

tinyash 0 条评论

Git 的能力并不缺,缺的是把一次尚未想清楚的修改整理成可提交历史时,开发者往往要在工作区、暂存区、临时提交、rebase 和撤销之间来回切换。Jujutsu(命令名 jj)不是给 Git 加一层别名,而是一个以 Git 作为当前可用存储后端、重新设计日常操作模型的版本控制系统。它仍能与已有 Git 仓库协作,却把「修改中的工作副本」也看成一个提交,并把每次仓库操作记录下来。

这篇文章不把 jj 描述成 Git 的无条件替代品。官方 README 仍明确标注它是 experimental;更合适的切入方式是:在个人项目、实验分支或团队已同意的试点仓库中,看看它如何减少整理历史时的机械步骤。

先理解变化:你编辑的东西已经在一个提交里

Git 常见的最小循环是编辑、git add 选择暂存内容、git commit 固化历史。这个流程对精确拆分提交很有帮助,但在「先探索、再整理」的任务里,暂存区也会变成额外状态:文件改了,却不一定已经 add;部分 hunk 已 add,部分还在工作区;临时提交后又要 amend 或 rebase。

jj 去掉显式的暂存区。当前工作副本本身就是一个可变的 commit:新建、删除和修改文件会被自动纳入这个工作副本;继续编辑相当于持续 amend 它。你不需要先把文件放进 index,仍然可以随时查看差异、描述当前工作,或在完成一段工作后创建下一个空工作副本。

另一个关键对象是 change ID。在 jj 中,一次逻辑改动可以因为修改、变基而产生新的 commit ID,但其 change ID 保持稳定。对于频繁整理提交顺序的分支,这比盯着每次都会变化的 SHA 更接近「我正在修改哪件事」的心智模型。

这不代表历史从此不用整理。工作副本自动收集变更,恰恰要求你在准备分享前明确检查差异、补充描述、决定是否拆分改动。jj 减少的是中间状态,不是替你做工程判断。

10 分钟在现有 Git 仓库试用

官方提供多种安装方式。macOS 可用 Homebrew;已安装 Rust/Cargo 的环境可安装最新发行版:

brew install jj

cargo install --locked --bin jj jj-cli

首次使用先配置身份。这里的值会写入 jj 配置,请使用自己的真实提交身份:

jj config set --user user.name "你的名字"
jj config set --user user.email "you@example.com"

为了避免把第一次试验直接带进重要仓库,可以从一个公开 Git 仓库开始。jj git clone 创建的是可被 jj 操作、同时保留 Git 兼容性的仓库:

jj git clone https://github.com/octocat/Hello-World
cd Hello-World
jj st

jj st 会显示当前工作副本及其变更。接着编辑一个文件,不需要执行 git add;用下面的命令查看 Git 风格 diff,并为正在进行的改动写描述:

jj diff --git
jj describe

jj describe 会为当前工作副本填写说明。完成这一小段任务后执行 jj new:它会在当前工作副本之上创建一个新的空 commit,作为下一项工作的工作副本。

一个实用习惯是把 jj diff --git 放在每次 jj describe 前后各执行一次:前者确认准备写进说明的内容边界,后者确认新工作副本没有意外携带旧文件。这个检查并非 jj 强制要求,却能避免「无需暂存」被误解成「无需审阅」。特别是同时改了配置、测试和实现时,仍应主动决定它们是否属于同一项变更;不确定就保留当前 change 继续整理,或在分享前再做拆分,而不是让自动记录替代提交质量。

jj new
jj st

这里最值得亲自体会的不是命令数量,而是状态的变化:旧改动已经成为一个有说明的 commit,你立即站在新的空工作副本上继续工作。若发现上一段改动还缺一行修正,jj 的工作流允许你回到相应 change 继续整理,而不是被「是否已经提交」这个二元边界卡住。

与 Git 分支、暂存区的差异:先把术语对齐

初次使用时,最容易误会的是把 jj 的每个概念机械翻译成 Git 命令。它们有交集,但不应强行一一对应。Git 的 branch 是指向提交的可移动引用;Jujutsu 使用 bookmark 表示接近「需要对外发布或协作的命名指针」的概念。Git 的 commit SHA 标识某个具体提交对象;jj 的 change ID 则用来追踪一项逻辑变更在多次改写后的连续身份。工作副本 commit 也不是「每敲一个字就制造一条公开历史」:它是本地整理过程中的当前对象,是否推送、何时建立 bookmark,仍需要开发者决定。

因此,试用阶段不要用 git status 的预期去解读每个屏幕输出。先问三个问题:现在的 working copy 包含哪些差异?这项工作准备以哪个描述对外呈现?哪些变更应当进入同一个逻辑 change?这三个问题比「下一步该不该 add」更能帮助你判断新模型是否适合自己的工作。

为什么自动变基会改变整理分支的成本

传统 Git 中,修改一个较早的提交往往意味着 interactive rebase,并且要谨慎维护后续提交与分支引用。Jujutsu 的 README 把它的行为概括为自动 rebase:当某个 commit 改变时,它的后代会自动重新建立在新版本之上;已经解决过的冲突还能向后续变基传播。

可以把它理解为把手动 git rebase --update-refs 与 Git 的 rerere 式冲突复用体验尽量前置到日常模型中——这是功能上的类比,不是说两者实现完全相同。实际收益出现在下面的场景:你先写了三个小改动,后来发现第一个提交的函数签名不合理。Git 工作流通常要显式发起交互式变基;jj 则把「后续改动依赖前面版本」作为持续维护的关系。

但自动化不等于没有冲突。依赖确实无法兼容时,仍要由开发者理解语义并解决;共享分支、受保护分支、CI、代码审查和团队的推送约定也不会因为换了客户端而消失。试用时应先在个人分支上验证与现有 hooks、GUI、IDE 和托管平台的协作方式。

操作日志比“撤销命令”更像安全网

版本控制出错并不只发生在文件内容上:误操作可能来自 rebase、pull、push,或者一次错误的历史整理。jj 会记录仓库操作,并提供 operation log 与撤销能力。它把「仓库刚才处于什么状态」作为一等信息,而不是要求你凭记忆拼出恢复命令。

这对尝试新工作流尤其有价值。你可以大胆做一次历史重排或描述修改,然后通过操作记录核对结果;若方向错误,再沿着操作历史回退。它不能替代远端备份,也不能绕过已经推送后对他人造成的影响,但能降低本地试验时的不可逆感。

从 Git 迁移时,先保留边界而不是追求全量替换

Jujutsu 当前的生产可用后端是 Git。README 说明,文件和 commit 可以存放在 Git 中,而 bookmark 等高层元数据并不完全等同于 Git 分支元数据。这正是它能逐步试用的原因,也是需要审慎验证的原因:不要先承诺整个团队切换,再去确认 GUI、自动化脚本和分支策略是否适配。

建议按以下顺序试点:

  1. 用一次性克隆或低风险个人仓库熟悉 jj stjj diff --gitjj describejj new
  2. 选一条会反复 amend、调整提交顺序的个人功能分支,观察 change ID 和自动变基是否真的减轻整理成本;
  3. 在要推送、开 PR 前,按团队原有流程检查 diff、提交说明和分支目标;
  4. 只有在 hooks、CI、代码审查和同事的 Git 使用方式都验证过后,才扩大使用范围。

如果你的痛点主要是「探索过程中不想反复 add/commit/rebase,却又希望随时可检查、可回退」,jj 的模型值得体验。若团队依赖固定的 Git 教程、脚本或图形界面,并且没有空间试错,保持 Git 工作流可能更合适。工具的价值不在于命令是否更短,而在于它是否让历史整理、恢复和协作的边界更清晰。

相关链接

发表评论

你的邮箱地址不会被公开,带 * 的为必填项。