mcpField
J
jujutsu

jujutsu

github.com/danverbraganza开发81129 次安装20 周前更新
代码仓库

关于 jujutsu

jujutsu 是面向编码 Agent 的 Jujutsu(jj)版本控制工作指南,介绍可变更提交、自动变基及 Git 兼容工作流。内容还针对非交互式 Agent 环境提供命令使用和操作核验建议。

主要功能

  1. 01可变更提交与自动变基
  2. 02使用 revset 选择提交
  3. 03提交整理与变更吸收
  4. 04Bookmark 与 Workspace 管理
  5. 05Git 仓库集成
  6. 06Agent 环境操作规范

适用场景

  1. 01在 jj 仓库中查看和修改提交历史
  2. 02将工作拆分为原子提交并整理
  3. 03在多 Workspace 间并行工作
  4. 04获取并推送 Git 远程变更

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/danverbraganza/jujutsu-skill --skill jujutsu

安装后在 Claude Code 中输入/jujutsu确认已加载。

详细介绍

Jujutsu (jj) 版本控制系统

此技能可帮助你使用 Jujutsu,这是一种兼容 Git、支持可变提交和自动变基的 VCS。

已使用 jj v0.37.0 测试——其他版本中的命令可能有所不同。

重要:自动化/Agent 环境

作为 Agent 运行时:

  1. 始终使用 --no-pager,防止命令打开交互式分页器(例如 less),以免 Agent 卡住:
# Always use --no-pager on commands that produce output
jj --no-pager log          # NOT: jj log
jj --no-pager diff         # NOT: jj diff
jj --no-pager show <id>    # NOT: jj show <id>
  1. 始终使用 -m 标志内联提供消息,而不要依赖编辑器提示:
# Always use -m to avoid editor prompts
jj desc -m "message"      # NOT: jj desc
jj squash -m "message"    # NOT: jj squash (which opens editor)

基于编辑器的命令在非交互式环境中会失败。

  1. 在修改操作(squash、abandon、rebase、restore)后,使用 jj st 验证操作成功。

核心概念

工作副本就是一个提交

在 jj 中,你的工作目录始终是一个提交(引用为 @)。运行任何 jj 命令时,变更都会自动创建快照。jj 没有暂存区。

无需运行 jj commit。

提交是可变的

重要:与 git 不同,jj 的提交创建后仍可自由修改。你可以更新描述、压缩变更、变基和吸收变更,所有这些操作都无需创建新提交。推荐的工作模式请参见下文的“基本工作流”。

变更 ID 与提交 ID
  • 变更 ID:稳定标识符(例如 tqpwlqmp),即使提交被重写也会保留——引用提交时优先使用此 ID
  • 提交 ID:内容哈希(例如 3ccf7581),提交内容变更时也会改变
修订集表达式

jj 使用修订集表达式语言来选择命令所操作的提交。常见的修订集表达式:

  • @ — 工作副本提交
  • @- — 工作副本的父提交
  • ::@ — @ 的所有祖先
  • @:: — @ 的所有后代
  • trunk()..@ — trunk 与 @(你的分支)之间的提交
  • bookmarks() — 所有带书签的提交

使用 -r 标志指定修订集表达式:jj log -r 'trunk()..@'

基本工作流

开始工作:先写描述,再编写代码

始终先创建提交消息,再编写代码:

使用 jj st 验证当前位于一个空白修订上。如果不是,请输入:

jj new
# First, describe what you intend to do
jj desc -m "Add user authentication to login endpoint"

# Then make your changes - they automatically become part of this commit
# ... edit files ...

# Check status
jj st
创建原子提交

每个提交应代表一项逻辑变更。提交消息使用以下格式:

Examples:
- "Add validation to user input forms"
- "Fix null pointer in payment processor"
- "Remove deprecated API endpoints"
- "Update dependencies to latest versions"
查看历史记录
# View recent commits
jj --no-pager log

# View with patches
jj --no-pager log -p

# View specific commit
jj --no-pager show <change-id>

# View diff of working copy (use --git for familiar +/- format)
jj --no-pager diff --git

重要:jj diff 输出格式:默认的 jj diff 输出采用并排行号格式(例如 26 26:),与 git 的 +/- 前缀格式看起来大不相同。这是正常且正确的——并非内容损坏或显示了过期内容。不过,为避免混淆,始终使用 jj diff --git 获取带有 +/- 行的标准统一差异格式。

在提交之间切换
# Create a new empty commit on top of current
jj new

# Create new commit with message
jj new -m "Commit message"

# Edit an existing commit (working copy becomes that commit)
jj edit <change-id>

# Edit the previous commit
jj prev -e

# Edit the next commit
jj next -e

优化提交

压缩变更

将当前提交中的变更移入其父提交:

# Squash all changes into parent
jj squash

注意:jj squash -i 会打开交互式界面,在 Agent 环境中会卡住。请避免使用。

拆分提交

警告:jj split 是交互式命令,在 Agent 环境中会卡住。要拆分提交,请使用 jj restore 移出变更,然后手动创建多个单独的提交。

吸收变更

自动将变更分配到最后修改了相应行的提交中:

# Absorb working copy changes into appropriate ancestor commits
jj absorb
放弃提交

彻底移除一个提交(其后代会变基到该提交的父提交上):

jj abandon <change-id>
撤销操作

反转最近一次 jj 操作:

jj undo

这会将仓库恢复到上一个命令执行前的状态。可用于从误执行 abandon、squash 或 rebase 等操作中恢复。

对提交进行变基

将提交移至其他父提交:

# Rebase current branch onto a destination
jj rebase -d <destination>

# Rebase a specific revision (without descendants) onto a destination
jj rebase -r <change-id> -d <destination>

# Rebase a revision and all its descendants
jj rebase -s <change-id> -d <destination>

# Rebase onto trunk (common: update your branch to latest main)
jj rebase -d main
恢复文件

丢弃特定文件的变更,或从另一个修订中恢复文件:

# Discard all uncommitted changes in working copy (restore from parent)
jj restore

# Discard changes to specific files
jj restore path/to/file.txt

# Restore files from a specific revision
jj restore --from <change-id> path/to/file.txt

使用书签(分支)

书签相当于 jj 中的 git 分支:

# Create a bookmark at current commit
jj bookmark create my-feature -r@

# Move bookmark to a different commit
jj bookmark move my-feature --to <change-id>

# List bookmarks
jj --no-pager bookmark list

# Delete a bookmark
jj bookmark delete my-feature

工作区

工作区是一个工作副本及其关联的仓库。一个仓库可以有多个工作区——每个工作区都有自己的工作目录和工作副本提交(@)——但它们共享相同的提交、操作和书签。这相当于 jj 中的 git worktree。

适用于在一个工作区中运行耗时的构建或测试,同时在另一个工作区中编辑。工作区是很少需要的功能;如需了解以下基础内容之外的信息,请查阅官方文档。

常用命令
# Create a new workspace (defaults: name = basename of path, parent = current @'s parent)
jj workspace add ../my-tests
jj workspace add --name tests -r <change-id> ../my-tests   # explicit name and base

# Inspect
jj --no-pager workspace list
jj workspace root [--name <ws>]

# Remove (does NOT delete files on disk — rm the directory separately)
jj workspace forget [<ws>]

# Rename current workspace
jj workspace rename <new-name>

在 jj log 中,每个工作区的 @ 显示为 <workspace-name>@。

关键语义
  • 默认隔离。 jj workspace add 会为新工作区创建一个全新的空提交;工作区起初不会共享 @,磁盘上的文件也不会在工作区之间实时镜像。
  • 在命令边界传播。 每条 jj 命令都会为当前工作区的文件创建快照并读取操作日志,因此能看到其他工作区创建的提交和书签。这里没有文件系统监视器。
  • 过时的工作副本。 如果另一个工作区改写了此工作区的 @(例如通过 jj squash、rebase、abandon),jj 会拒绝在此处执行命令,直到你运行 jj workspace update-stale。如果命令在更新过程中被中断,也需要用相同的方式恢复。
  • 共享 @ 容易出问题。 jj edit <id> 允许两个工作区在不发出警告的情况下指向同一个变更。当其中一个工作区修改它时,另一个就会过时;如果过时的工作区中有尚未创建快照的编辑,update-stale 会将它们保留为分歧提交(变更 ID 相同,在 jj log 中显示为 xyz??),你必须解决这个问题。除非两个工作区都为只读,否则应避免共享 @。
Agent 使用指南
  • 始终为 jj workspace list 传入 --no-pager。
  • 不要对另一个工作区已作为其 @ 的变更运行 jj edit——这是意外产生分歧的主要原因。
  • 不要在未运行 jj workspace forget <name> 的情况下对工作区目录执行 rm -rf。

Git 集成

使用现有 Git 仓库
# Clone a git repository
jj git clone <url>

# Initialize jj in an existing git repo
jj git init --colocate
获取远程更改
# Fetch all branches from the default remote
jj git fetch

# Fetch from a specific remote
jj git fetch --remote <remote-name>

# Fetch specific branches
jj git fetch -b <branch-name>

获取后,将你的工作变基到更新后的主干上:jj rebase -d main

在 jj 和 git 之间切换(仅适用于共置仓库)

本节仅适用于共置仓库(同时存在 .jj/ 和 .git/ 的仓库)。对于非共置仓库,请勿使用 git 命令,否则会破坏 jj 状态。

在共置仓库中,你可以谨慎地同时使用 jj 和 git 命令:

切换到 git 模式(例如用于合并工作流):

# First, ensure your jj working copy is clean
jj st

# Then checkout a branch with git
git checkout <branch-name>

切回 jj 模式:

# Use jj edit to resume working with jj
jj edit <change-id>

重要说明:

  • 如果 jj 的工作副本与 git HEAD 不同,Git 可能会提示存在未提交的更改
  • 切换到 git 前,务必确保你的工作已在 jj 中提交
  • 执行 git 操作后,jj 会在下一条命令运行时检测并纳入这些更改
推送更改

当用户要求你推送更改时:

# Push a specific bookmark to the remote
jj git push -b <bookmark-name>

# Example: push the main bookmark
jj git push -b main

推送前,请确保:

  1. 你的书签指向正确的提交(书签不会像 git 分支那样自动前移)
  2. 提交经过了完善整理,且粒度清晰
  3. 用户已明确要求推送

重要:与 git 分支不同,创建新提交时 jj 书签不会自动移动。推送前必须手动更新书签:

# Move an existing bookmark to the current commit
jj bookmark move my-feature --to @

# Then push it
jj git push -b my-feature

如果你的更改没有对应的书签,请先创建一个:

# Create a bookmark at the current commit
jj bookmark create my-feature

# Then push it
jj git push -b my-feature

处理冲突

jj 允许提交包含冲突的内容——你可以稍后解决:

# View conflicts
jj st

Agent 冲突解决:不要使用 jj resolve(交互式)。应直接编辑冲突文件,移除冲突标记,然后运行 jj st 验证冲突已解决。

保持提交质量

重要:由于提交是可变的,请务必在完成工作前对其进行完善:

  1. 审查提交:jj --no-pager show @ 或 jj --no-pager diff --git
  2. 是否是原子提交? 每个提交只包含一项逻辑变更
  3. 提交说明是否清晰? 使用句首字母大写格式的祈使动词短语,末尾不加句号,例如:"Add login endpoint"、"Fix null pointer in payment processor"、"Remove deprecated API endpoints"
  4. 是否包含无关变更? 使用 jj restore 移出这些变更,然后创建单独的提交
  5. 这些变更是否应该放到其他位置? 使用 jj squash 或 jj absorb

快速参考

操作命令
描述提交jj desc -m "message"
查看状态jj st
查看日志jj --no-pager log
查看差异jj --no-pager diff --git
新建提交jj new -m "message"(先使用 jj st;若 @ 为空则跳过)
编辑提交jj edit <id>
压缩到父提交jj squash
自动分配jj absorb
变基jj rebase -d <destination>
放弃提交jj abandon <id>
撤销上一次操作jj undo
恢复文件jj restore [paths]
创建书签jj bookmark create <name>
获取远程内容jj git fetch
推送书签jj git push -b <name>
添加工作区jj workspace add <path>
列出工作区jj --no-pager workspace list
忘记工作区jj workspace forget [name]
修复过时的工作副本jj workspace update-stale

最佳实践总结

  1. 先写描述:编码前设置提交说明
  2. 每个提交只做一项变更:保持提交原子且聚焦
  3. 使用变更 ID:它们在重写后仍保持稳定
  4. 完善提交:利用可变性保持历史记录整洁
  5. 拥抱工作流:没有暂存区,也不需要暂存——只需提交

评论

支持 Markdown