jj 入门
过去几周里,我一直在用 jj!这不是我第一次尝试它,但却是第一次真正用顺手了。
多年来,我的 Git 工作流一直是在分支上“改完就执行 git commit --amend --no-edit && git push --force-with-lease”。这样,远程上的代码几乎能零延迟地保持最新。PR 很少会包含很多 commit,通常只有 1 个,我也会尽量让它们保持精简。
自从开始和 agent 一起工作后,我发现自己切换分支的频率高了很多。为此,我要么得用 git stash,要么就得提交一个无意义的 wip commit,来防止改动之间相互覆盖。
于是我们的新主角 jj 登场了!它的模型有些不同。我不打算写一份大而全的手册,因为已经有很多这样的手册了,而且它们写得比我随手记的笔记要好得多。
简而言之,最吸引我的地方在于,我的改动总是处于已提交状态。每次编辑都会自动 amend 到最新的 commit 上,而推送则会覆盖远程分支(或者用 jj 的说法,是“横向”移动它)。
编辑 change(变更) 非常轻松,不需要 checkout/rebase 那一套繁琐操作。感觉就像有人把我喜欢的 Git 工作流直接做成了一个工具。真不知道我为什么花了这么久才想明白这一点。
其实也就是随手记录一下。之后学到更多东西,我大概还会发布更多笔记。
工作流程
要做一次改动时,我的工作流程是这样的:
jj new main
<do some coding>
jj describe -m 'feat: something really cool'
jj git push -c @就这样。合并 PR,然后重复。
在 jj 里,我们更多谈论的是 change 而非 commit。change 是一个不断演进的 commit。而“@”指的就是当前的 change
开始一个新的 change
在开始工作之前,你应该先创建一个 change。接下来所有的编辑都会保存在这里
jj new main这会在 main 之上创建你的 commit(刚接触时,change 和 commit 的区别确实让人困惑),也就是在带有 bookmark(书签) main 的那个 commit 之上。如果你想在当前 change 之下再创建一个子 change,只需运行 jj new。
为 change 添加描述
你可能已经习惯了先把改动添加到暂存区,再用一条提交信息来提交。而在 jj 里,你的改动已经是已添加状态了。但你的 change 可能还没有描述!(也就是 commit message)
jj describe -m 'hello i am a message'你也可以在创建 change 时就直接加上描述:
jj new -m "hello i am a message"将 commit 推送到新分支
如果你正在一个 change 上,对它很满意,想把它推到 GitHub 上让某个 agent 来点评一下你(其实是 Claude 写的)代码?
很简单。
jj git push -c @也就是“用 Git 推送当前 commit”。它会自动创建一个分支并推送到远程。实际上,你可以在持续修改的过程中反复执行这个命令,它会自动把 jj 的魔法转换成 Git 的 amend 和 force push。
不过,我还是很期待 jj 能拥有自己的协议和托管平台。
编辑 commit
如果想回头编辑一个已有的 change?很简单:
jj edit <change-id>从这里开始,我才真正感受到它的强大,同时我原本试图套用的心智模型也开始失效了。这里没有 checkout、rebase 等等需要折腾的东西。
jj 既有 change 也有 commit。如果记得它是没有暂存区、而是不断演进 commit 的,那么就可以把 change 理解为稳定的 ID,而 commit ID 则是每次编辑都会变化的那个
撤销操作
你在 jj 里做的几乎所有操作都会被记录在 op log(操作日志) 中。如果做了某件事结果搞砸了?jj undo 来救场。
更多 Git 相关操作
jj 目前在很大程度上仍然是构建在 Git 之上的。他们已经让现有仓库的转换变得很容易,也能很好地与 GitHub 集成
# Create a jj repo inside of a git repo
jj git init
# Fetch from the remote (github etc)
jj git fetch
# Push
jj git push
# Clone
jj git clone那分支都去哪了
根本没有分支!至少不是你习惯的那种。jj 使用的是“bookmark”,也就是可以附加到 commit 上的一串字符串。当你推送一个 change 时,它会自动为你创建一个 bookmark——比如 push-refrefref
# bookmark the current change with a name
jj bookmark create <name>
# move a bookmark to a new commit
jj bookmark set <name> -r <commit>
# other things exist just use --help让我最困扰的一点是,当我创建一个新的 change 时,bookmark 并不会跟着移动。
来自术语表:
与 Git 不同,这里没有“当前 bookmark”的概念;创建新 commit 时,bookmark 不会移动。如果 commit 被重写,bookmark 会自动跟随它。
之后我会再写一篇关于变基、编辑以及其他有趣内容的文章
注:在 Vim 里写这篇文章特别痛苦,因为我把 jj 绑定到了 <esc>。
随机一篇博客