mcpField
J
jj-workflow

jj-workflow

github.com/trevors开发93 次安装
代码仓库

关于 jj-workflow

面向使用 jj 的开发者,介绍变更描述、分支、变基、冲突处理和恢复等工作流。指南还为 AI 辅助操作规定非交互式命令,并说明共置模式可让团队成员继续只看到 Git。

主要功能

  1. 01用非交互式命令描述变更
  2. 02改写前后使用稳定的变更 ID
  3. 03将冲突作为提交状态管理
  4. 04创建并比较并行实验
  5. 05用 jj absorb 分配修复内容
  6. 06通过操作日志恢复旧状态

适用场景

  1. 01维护较浅的变更栈
  2. 02修复旧变更并自动变基后续变更
  3. 03从同一基点尝试不同实现
  4. 04撤销操作或恢复仓库旧状态

安装

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

$npx skills add https://github.com/trevors/dot-claude --skill jj-workflow

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

附带文件REFERENCE.md

详细介绍

jj 工作流

理念

  1. 提交很轻松,描述必不可少。 工作副本始终是一个提交。绝不要让它处于“(no description set)”状态。
  2. 尽情尝试,操作日志会为你兜底。 每次变更都会被记录。jj undo 和 jj op restore 可以撤销任何操作。
  3. 冲突是一种状态,不是紧急情况。 jj 会将冲突作为结构化数据存储在提交中。即使存在冲突,变基也能成功。准备好后再解决。
  4. 变更 ID 是你跟踪工作的凭据。 重写时提交哈希会变化,变更 ID 不会。变基和压缩提交期间,用变更 ID 引用工作。
  5. 书签是为 GitHub 准备的,不是为你准备的。 使用匿名变更进行工作。只有在需要推送时才添加书签。
  6. 保持提交栈浅。 尽早压缩提交。别让历史记录在整理前增长到 10 个提交那么深。
  7. 用 absorb 代替手动路由压缩提交。 跨提交栈修复时,让 jj 判断每个代码块应归属何处。
  8. 共置仓库 = 团队成员无从察觉。 队友看到的是标准 git。他们不知道你在用 jj。

重要:AI 专属规则

始终使用 -m 标志,避免 jj 打开编辑器:

# WRONG - opens editor, blocks AI
jj new
jj describe
jj commit
jj squash

# CORRECT - non-interactive
jj new -m "message"
jj describe -m "message"
jj commit -m "message"
jj squash -m "message"

绝不要使用这些交互式命令(没有非交互模式):

  • jj split / jj split -i
  • jj squash -i
  • jj diffedit

核心概念

工作副本 = 提交

这里没有暂存区。每次文件编辑都会自动记录在 @(当前变更)中。不需要执行 git add。

  • @ = 当前变更(工作副本提交)
  • @- = 当前变更的父提交
  • @-- = 祖父提交
变更 ID 与提交 ID

每个变更都有两个标识符:

  • 变更 ID(例如,kpqxywon)——重写后保持不变。用它来引用工作。
  • 提交 ID(例如,a1b2c3d4)——内容重写时会变化。

压缩、变基或修订时,变更 ID 会保持不变。这意味着你可以在心里记住一个变更 ID,它始终都能解析到对应变更;git 提交哈希则不同。

访问先前版本(xyz/n 语法)

每次重写变更都会被记录。使用 <change-id>/n 访问先前版本:

  • xyz/0 — 最新(当前)版本(与 xyz 相同)
  • xyz/1 — 上一个版本
  • xyz/2 — 两个版本之前

这在需要将变更恢复到较早状态时很有用:

jj restore --from xyz/1 --to xyz    # Revert xyz to its previous contents
jj diff --from xyz/1 --to xyz      # See what changed between versions
冲突只是一种状态

变基产生冲突时,jj 会将冲突记录在提交中并成功完成操作。不会出现阻塞状态“rebase in progress”,也不需要执行 --continue。

  • 冲突提交的后代可以正常工作
  • 方便时再解决冲突——检出该提交、修复文件即可
  • jj log 会标记冲突提交,方便你发现它们

工作流

压缩工作流(推荐)
jj describe -m "feat: what I'm building"   # State intent on current change
jj new -m "wip"                             # New empty change on top
# ... make changes ...
jj squash -m "feat: done"                   # Squash into parent
提交工作流(更简单)
# ... make changes ...
jj commit -m "feat: what I did"             # Describe + create new change in one step
# ... keep working ...

jj commit 等同于 jj describe -m "..." && jj new。

编辑工作流(修复提交栈中间的内容)

需要修复较早变更中的内容?不必经历 stash/rebase-i 的繁琐操作:

jj edit <change-id>        # Switch working copy to that change
# ... make your fix ...
jj new -m "back to work"   # Return to tip (descendants auto-rebased)

编辑后的变更的所有后代都会自动变基。

并行试验
jj new main -m "approach A"           # Branch from main
jj new main -m "approach B"           # Another branch from main (not from A)
jj diff --from <A-id> --to <B-id>    # Compare approaches
jj edit <winner-id>                   # Continue with the winner
jj abandon <loser-id>                 # Discard the loser

Absorb:智能路由压缩

当你有一组提交,并在 @ 中进行修复时,jj absorb 会自动将每个代码块分发到最后修改过对应行的祖先提交中。

# You're at the top of a 3-commit stack, fixing bugs across all of them
jj absorb    # Each fix goes to the right commit automatically

跨提交栈修复时使用 jj absorb。如果明确知道变更应该放到哪里,则使用 jj squash。

书签与推送

书签相当于 jj 中的 git 分支,但不会自动前移。你必须显式移动它们。

推送到 main
jj bookmark set master -r @-        # Point bookmark at your commit (not empty @)
jj git push
功能分支
# Create and push
jj bookmark create feature-x -r @-
jj git push

# Update after more work
jj bookmark set feature-x -r @-
jj git push
处理 PR 反馈
jj new feature-x- -m "address review feedback"
# ... make changes ...
jj squash -m "feat: updated per review"
jj bookmark set feature-x -r @-
jj git push

Revsets

Revsets 是一种用于选择提交的函数式语言。除了 @ 和 @- 之外:

表达式含义
@当前工作副本
@-父提交
@--祖父提交
x+x 的子提交
x::x 的所有后代
::xx 的所有祖先
trunk()trunk/main 提交
bookmarks()所有带书签的提交
empty()空提交
divergent()分叉的更改
remote_tags()远程标签
diff_lines("text")差异匹配的提交
description("text")按描述筛选
author("name")按作者筛选

实用示例:

jj log -r 'trunk()..@'              # Everything between main and here
jj log -r '::@ & ~::trunk()'         # My branch only
jj log -r 'author("trevor")'         # My commits

与远程同步

jj git fetch                         # Pull from remote
jj rebase -d master@origin           # Rebase onto updated main

临时禁用不可变提交

当你需要重写受 immutable_heads() 保护的提交时(例如,将提交压入远程书签):

# Disable protection (quote the key — parentheses are invalid TOML bare keys)
jj config set --repo 'revset-aliases."immutable_heads()"' 'none()'

# Do your rewrite
jj squash -m "updated message"

# ALWAYS restore protection immediately after
jj config set --repo 'revset-aliases."immutable_heads()"' 'builtin_immutable_heads() | remote_bookmarks()'

重要: NAME 参数需要对 TOML 键使用 shell 引号,因为 immutable_heads() 包含括号。对完整的点分隔键使用单引号,内部使用双引号:'revset-aliases."immutable_heads()"'。

关键:禁用不可变保护前务必征得用户同意。 重写远程书签意味着强制推送,这会改写共享历史。继续操作前请向用户确认——绝不能在未告知的情况下禁用不可变保护。

恢复

操作日志会记录每次变更。任何内容都不会真正丢失。

jj op log                            # See all operations
jj undo                              # Undo last operation
jj op restore <id>                   # Jump to any past state
jj evolog                            # See how current change evolved
jj evolog -r <change-id>             # See how any change evolved

推荐配置

用户配置位于 ~/.config/jj/config.toml:

[remotes.origin]
auto-track-bookmarks = "*"

[revset-aliases]
# Prevent rewriting pushed commits
'immutable_heads()' = 'builtin_immutable_heads() | remote_bookmarks()'
# Shorthand for trunk
'trunk()' = 'master@origin'

退出

rm -rf .jj    # Delete jj state, keep git unchanged

评论

支持 Markdown