# 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 更强大: ```bash jj op log # 查看操作历史 jj undo # 撤销上一步操作(任何操作) jj redo # 重做 jj op restore # 恢复到指定操作状态 ``` ### 冲突是一等公民 冲突不会阻塞操作(变基/合并不需要 `--continue`),可以提交带冲突的内容,稍后解决。 ### 版本集查询(Revset)语法 类似 SQL 的提交查询语言: ```bash 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 ` | `jj git clone ` | | | 添加远程 | `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 ` | `jj show ` | | 日志 | `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 ` | `jj bookmark create ` / `jj b c ` | | 移动 | `git branch -f ` | `jj bookmark move --to ` / `jj b m -t @` | | 删除 | `git branch -d ` | `jj bookmark delete ` | > jj 中分支叫**书签**(bookmark),不会随提交自动移动,需手动更新。 ### 切换与编辑 | 场景 | Git | jj | |------|-----|-----| | 切换分支 | `git switch ` | `jj new ` | | 编辑旧提交 | `git rebase -i` (选 edit) | `jj edit `(直接编辑) | | 完成编辑 | `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 ` | `jj duplicate ` | | 拆分提交 | `git reset HEAD~ && git add -p ...` | `jj split` | | 排序提交 | `git rebase -i` | `jj arrange`(TUI) | | 放弃提交 | 需变基 | `jj abandon` | | 还原提交 | `git revert ` | `jj revert -r ` | ### 远程操作 | 场景 | Git | jj | |------|-----|-----| | 拉取 | `git pull` | `jj git fetch` + `jj rebase -r @ -d trunk` | | 推送 | `git push` | `jj git push` | | 推送指定分支 | `git push origin ` | `jj git push -b ` | | 强制推送 | `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 @-`(创建兄弟提交) | ## 典型工作流 ### 日常开发 ```bash jj new main # 在 main 上新建工作副本 # ... 编写代码 ... jj diff # 查看变更 jj describe -m "功能: xxx" # 设置提交消息 jj new # 创建下一个提交(当前提交固化) ``` ### 修改上一个提交 ```bash jj edit @- # 切换到父提交编辑 # ... 修改 ... jj new # 回到新的工作副本 ``` 或用合并命令: ```bash # 在工作副本修改后 jj squash # 合并到父提交 ``` ### 功能分支推送 ```bash jj new main # ... 开发 ... jj describe -m "功能: xxx" jj b c feature-xxx # 创建书签 jj git push -b feature-xxx # 推送 ``` ### 同步远程更新 ```bash jj git fetch jj rebase -d main # 变基到最新 main jj git push # 推送 ``` ## 远程同步与故障排除 ### 同步远程更新(推荐流程) ```bash jj git fetch # 拉取远程变更 jj rebase -r @ -d trunk # 将工作区变基到最新 trunk ``` > 已配置 `remotes.origin.auto-track-bookmarks = '*'`,fetch 后本地书签自动跟随远程。 ### 书签跟踪状态诊断 ```bash jj bookmark list --all # 查看所有书签及远程跟踪状态 # 输出示例: # trunk: (commit_id) # @origin: (commit_id) ← 远程跟踪版本 # @git: (commit_id) ← git 层面版本 # 交叉验证(用 git 层面确认是否真的同步) git log --oneline HEAD..origin/trunk ``` ### Divergent Change ID 当同一个 Change ID 对应多个提交时,`jj log` 会显示 `(divergent)` 标记。 **常见原因**: - 另一个会话/工具(如 VS Code jj 扩展、直接 git 操作)对同一变更进行了重写 - 多个 jj 操作并发执行导致操作日志分歧 **排查步骤**: ```bash # 1. 查看操作日志,找到分歧产生的时间点 jj op log --limit 10 # 2. 确认书签指向是否正确 jj bookmark list --tracked # 3. 如果书签指向正确(与远程一致),只需清理分歧 # 方法 A: 回退到分歧前的操作状态(推荐) jj op restore <操作ID> # 方法 B: 放弃不需要的 divergent 版本(注意:如果它是另一个版本的祖先会引发冲突) jj abandon --ignore-immutable ``` **注意事项**: - 不要轻易 `jj abandon` 一个 divergent 版本,如果它是另一个版本的祖先(parent),会产生大量冲突 - 优先使用 `jj op restore` 回退到分歧前的干净状态 - divergent 标记只影响 jj 内部的 Change ID 映射,不影响实际提交数据 ### fetch 返回 "Nothing changed" 但远程有新提交 **排查清单**: 1. 检查 `@origin` 跟踪是否已更新(可能其他进程/扩展已 fetch 过) 2. 用 `git fetch origin && git log HEAD..origin/trunk` 交叉验证 3. 检查是否是 divergent 状态导致工作区 parent 不在最新 trunk 上 ### 关键配置 ```bash # 用户级配置文件 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 ` | `jj workspace add [-r ] ` | | 列出工作副本 | `git worktree list` | `jj workspace list` | | 删除工作副本 | `git worktree remove ` | `jj workspace forget ` | ### 日常使用 ```bash # 为功能分支创建独立工作目录 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 中完成工作后,主目录同步: ```bash jj git fetch jj git import # 将 Agent 的 git worktree 提交导入 jj 视图 ``` > **注意**:git worktree 创建的目录没有 `.jj`,jj 不主动跟踪这些 worktree。通过 `jj git import` 可将 git 层面的变更同步到 jj 视图。 ## 注意事项 ### 与 Git 共存 并存模式下 `.jj` 和 `.git` 并存,两边可互操作: ```bash jj git import # 将 git 变更导入 jj jj git export # 将 jj 变更导出到 git ``` ### IDE 集成 - VS Code 有 jj 扩展但功能不如 GitLens 成熟 - 部分工具只识别 git,可能需要 `jj git export` - JetBrains 插件开发中 ### 性能 - 大仓库可能因 Git 导入/导出开销稍慢 - 普通仓库无感知差异 ## 安装 ```bash # 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