10 KiB
Jujutsu (jj) 操作指南
Git 兼容的下一代版本控制系统,Rust 编写,Google 内部使用。
核心概念差异
工作副本即提交
Git 中:工作目录 → 暂存区 → 提交,jj 中:工作目录就是提交。
- 无
git add,所有变更自动跟踪 @表示当前工作副本,@-表示父提交- 无"暂存区"概念,用
jj split拆分、jj squash -i部分合并
变更标识 vs 提交标识
| 标识 | 说明 |
|---|---|
| 变更标识(Change ID) | 逻辑变更标识,修改/变基后不变 |
| 提交标识(Commit ID) | 具体提交哈希,重写后改变 |
jj log 同时显示两者:qpvuntsm 23036a08 功能: 添加认证
操作日志(万能撤销)
记录所有操作,比 Git 的 reflog 更强大:
jj op log # 查看操作历史
jj undo # 撤销上一步操作(任何操作)
jj redo # 重做
jj op restore <id> # 恢复到指定操作状态
冲突是一等公民
冲突不会阻塞操作(变基/合并不需要 --continue),可以提交带冲突的内容,稍后解决。
版本集查询(Revset)语法
类似 SQL 的提交查询语言:
jj log -r ::@ # @ 的所有祖先
jj log -r 'remote_bookmarks()..' # 尚未推送的提交
jj log -r 'main..' # main 之后的提交
jj log -r 'mine() ~ ::immutable()' # 我的非不可变提交
命令对照表
仓库操作
| 场景 | Git | jj | 备注 |
|---|---|---|---|
| 初始化 | git init |
jj git init |
默认并存模式 |
| 克隆 | git clone <url> |
jj git clone <url> |
|
| 添加远程 | git remote add |
jj git remote add |
查看状态
| 场景 | Git | jj |
|---|---|---|
| 状态 | git status |
jj st |
| 差异 | git diff |
jj diff |
| 某提交差异 | git diff A^ A |
jj diff -r A |
| 查看提交 | git show <rev> |
jj show <rev> |
| 日志 | git log --graph |
jj log |
| 所有日志 | git log --all --graph |
jj log -r 'all()' |
提交
| 场景 | Git | jj |
|---|---|---|
| 暂存全部 | git add . |
不需要(自动跟踪) |
| 提交 | git commit -m "msg" |
jj describe -m "msg" && jj new 或 jj commit -m "msg" |
| 修改消息 | git commit --amend |
jj describe |
| 修改内容 | git add . && git commit --amend |
jj squash |
| 交互式修改 | git add -p && git commit --amend |
jj squash -i |
书签(Bookmark)
| 场景 | Git | jj |
|---|---|---|
| 列出 | git branch |
jj bookmark list / jj b l |
| 创建 | git branch <name> |
jj bookmark create <name> / jj b c <name> |
| 移动 | git branch -f <name> <rev> |
jj bookmark move <name> --to <rev> / jj b m <name> -t @ |
| 删除 | git branch -d <name> |
jj bookmark delete <name> |
jj 中分支叫书签(bookmark),不会随提交自动移动,需手动更新。
切换与编辑
| 场景 | Git | jj |
|---|---|---|
| 切换分支 | git switch <name> |
jj new <name> |
| 编辑旧提交 | git rebase -i (选 edit) |
jj edit <rev>(直接编辑) |
| 完成编辑 | git rebase --continue |
jj new(新建工作副本) |
历史改写
| 场景 | Git | jj |
|---|---|---|
| 变基 | git rebase main |
jj rebase -d main |
| 变基指定提交 | git rebase --onto B A |
jj rebase -s A -d B |
| 摘选 | git cherry-pick <rev> |
jj duplicate <rev> |
| 拆分提交 | git reset HEAD~ && git add -p ... |
jj split |
| 排序提交 | git rebase -i |
jj arrange(TUI) |
| 放弃提交 | 需变基 | jj abandon |
| 还原提交 | git revert <rev> |
jj revert -r <rev> |
远程操作
| 场景 | Git | jj |
|---|---|---|
| 拉取 | git pull |
jj git fetch + jj rebase -r @ -d trunk |
| 推送 | git push |
jj git push |
| 推送指定分支 | git push origin <name> |
jj git push -b <name> |
| 强制推送 | git push --force |
jj git push(默认 force-with-lease) |
撤销与恢复
| 场景 | Git | jj |
|---|---|---|
| 撤销操作 | 无通用方法 | jj undo |
| 操作历史 | git reflog |
jj op log |
| 恢复工作副本 | git checkout . |
jj restore |
| 暂存 | git stash |
jj new @-(创建兄弟提交) |
典型工作流
日常开发
jj new main # 在 main 上新建工作副本
# ... 编写代码 ...
jj diff # 查看变更
jj describe -m "功能: xxx" # 设置提交消息
jj new # 创建下一个提交(当前提交固化)
修改上一个提交
jj edit @- # 切换到父提交编辑
# ... 修改 ...
jj new # 回到新的工作副本
或用合并命令:
# 在工作副本修改后
jj squash # 合并到父提交
功能分支推送
jj new main
# ... 开发 ...
jj describe -m "功能: xxx"
jj b c feature-xxx # 创建书签
jj git push -b feature-xxx # 推送
同步远程更新
jj git fetch
jj rebase -d main # 变基到最新 main
jj git push # 推送
远程同步与故障排除
同步远程更新(推荐流程)
jj git fetch # 拉取远程变更
jj rebase -r @ -d trunk # 将工作区变基到最新 trunk
已配置
remotes.origin.auto-track-bookmarks = '*',fetch 后本地书签自动跟随远程。
书签跟踪状态诊断
jj bookmark list --all # 查看所有书签及远程跟踪状态
# 输出示例:
# trunk: <local> (commit_id)
# @origin: <remote> (commit_id) ← 远程跟踪版本
# @git: <git> (commit_id) ← git 层面版本
# 交叉验证(用 git 层面确认是否真的同步)
git log --oneline HEAD..origin/trunk
Divergent Change ID
当同一个 Change ID 对应多个提交时,jj log 会显示 (divergent) 标记。
常见原因:
- 另一个会话/工具(如 VS Code jj 扩展、直接 git 操作)对同一变更进行了重写
- 多个 jj 操作并发执行导致操作日志分歧
排查步骤:
# 1. 查看操作日志,找到分歧产生的时间点
jj op log --limit 10
# 2. 确认书签指向是否正确
jj bookmark list --tracked
# 3. 如果书签指向正确(与远程一致),只需清理分歧
# 方法 A: 回退到分歧前的操作状态(推荐)
jj op restore <操作ID>
# 方法 B: 放弃不需要的 divergent 版本(注意:如果它是另一个版本的祖先会引发冲突)
jj abandon <divergent版本> --ignore-immutable
注意事项:
- 不要轻易
jj abandon一个 divergent 版本,如果它是另一个版本的祖先(parent),会产生大量冲突 - 优先使用
jj op restore回退到分歧前的干净状态 - divergent 标记只影响 jj 内部的 Change ID 映射,不影响实际提交数据
fetch 返回 "Nothing changed" 但远程有新提交
排查清单:
- 检查
@origin跟踪是否已更新(可能其他进程/扩展已 fetch 过) - 用
git fetch origin && git log HEAD..origin/trunk交叉验证 - 检查是否是 divergent 状态导致工作区 parent 不在最新 trunk 上
关键配置
# 用户级配置文件
jj config path --user # → ~/.config/jj/config.toml
# 自动跟踪远程书签(替代已废弃的 git.auto-local-bookmark)
jj config set --user 'remotes.origin.auto-track-bookmarks' "'*'"
# 查看所有配置(含默认值)
jj config list git --include-defaults
| 配置项 | 推荐值 | 说明 |
|---|---|---|
remotes.origin.auto-track-bookmarks |
'*' |
fetch 后本地书签自动跟随远程 |
git.push-new-bookmarks |
false |
防止意外推送新书签 |
Workspace(多工作副本)
jj 原生支持多工作副本,概念上等同于 git worktree,但融入 jj 的变更模型。
命令对照
| 场景 | Git | jj |
|---|---|---|
| 创建工作副本 | git worktree add <path> <branch> |
jj workspace add [-r <rev>] <path> |
| 列出工作副本 | git worktree list |
jj workspace list |
| 删除工作副本 | git worktree remove <path> |
jj workspace forget <name> |
日常使用
# 为功能分支创建独立工作目录
jj workspace add -r trunk ../feature-a
jj workspace add -r trunk ../feature-b
# 在各目录独立工作
cd ../feature-a
# ... 编辑代码 ...
jj describe -m "功能: 实现认证"
jj new
# 回到主目录查看
cd ../my-project
jj workspace list
每个 workspace 拥有独立的 .jj/ 目录和自己的 @ 工作副本,共享同一个底层仓库。
Claude Code Agent 并行开发
Claude Code 的 EnterWorktree 工具内部调用 git worktree add(不是 jj workspace),在 jj 并存模式下两种路径对比:
| 路径 | 方式 | 兼容性 | 说明 |
|---|---|---|---|
Claude Code EnterWorktree |
git worktree | 可用 | git 层面隔离,jj 需 jj git import 同步 |
手动 jj workspace add |
jj workspace | jj 原生 | jj 完全感知,但 Agent 无法自动调用 |
推荐方案:Agent 并行开发使用 Claude Code 内置 EnterWorktree。
Agent-1: EnterWorktree → .claude/worktrees/xxx/ (git worktree)
Agent-2: EnterWorktree → .claude/worktrees/yyy/ (git worktree)
主目录: jj 管理 trunk
Agent 在 worktree 中完成工作后,主目录同步:
jj git fetch
jj git import # 将 Agent 的 git worktree 提交导入 jj 视图
注意:git worktree 创建的目录没有
.jj,jj 不主动跟踪这些 worktree。通过jj git import可将 git 层面的变更同步到 jj 视图。
注意事项
与 Git 共存
并存模式下 .jj 和 .git 并存,两边可互操作:
jj git import # 将 git 变更导入 jj
jj git export # 将 jj 变更导出到 git
IDE 集成
- VS Code 有 jj 扩展但功能不如 GitLens 成熟
- 部分工具只识别 git,可能需要
jj git export - JetBrains 插件开发中
性能
- 大仓库可能因 Git 导入/导出开销稍慢
- 普通仓库无感知差异
安装
# macOS
brew install jj
# Linux (Cargo)
cargo install --locked --bin jj jj-cli
# Linux (Arch)
pacman -S jujutsu
# Windows
winget install jj-vcs.jj
参考资源
- 官方文档: https://docs.jj-vcs.dev/latest/
- GitHub: https://github.com/jj-vcs/jj
- 教程: https://docs.jj-vcs.dev/latest/tutorial/
- Git 对比: https://docs.jj-vcs.dev/latest/git-comparison/
最后更新: 2026-04-14