基本工作流程
使用 Jujutsu 最简单的方式是先创建一组提交(commit stack)。只有在你需要将这组提交推送到远程仓库时,才需要创建书签(bookmark)。Jujutsu 有两种主要的工作流程:使用自动生成的书签名称,或使用自定义名称的书签。
使用自动生成的书签名称
下面这个例子中,我们让 Jujutsu 自动创建书签:
# 从默认书签开始一个新的提交
$ jj new main
# 重构一些文件,然后添加提交说明并开始新的提交
$ jj commit -m 'refactor(foo): restructure foo()'
# 添加一个新功能,添加提交说明并开始新的提交
$ jj commit -m 'feat(bar): add support for bar'
# 让 Jujutsu 生成一个书签名称,并将其推送到 GitHub。
# 注意我们推送的是工作副本提交的“父提交”,因为当前工作副本是空的。
$ jj git push -c @-
使用命名书签
这个例子中,我们创建了一个名为 bar
的书签,然后将其推送到远程仓库。
# 从默认书签开始一个新的提交
$ jj new main
# 重构一些文件,然后添加提交说明并开始新的提交
$ jj commit -m 'refactor(foo): restructure foo()'
# 添加一个新功能,添加提交说明并开始新的提交
$ jj commit -m 'feat(bar): add support for bar'
# 创建一个书签,以便可以推送到 GitHub。注意我们在“父提交”上创建书签,因为当前工作副本是空的。
$ jj bookmark create bar -r @- # `bar` 现在包含了前两个提交
# 将该书签推送到 GitHub(只会推送 `bar`)
$ jj git push --allow-new
虽然你也可以像 Git 那样预先创建一个书签并在其上继续提交,但那样每次创建新提交后都要手动移动书签。和 Git 不同,Jujutsu 不会自动移动书签。
更新仓库
截至 2023 年 10 月,Jujutsu 还没有类似 git pull
的命令(参见 issue #1039)。在该命令发布前,你需要使用以下方式来更新你的更改:
$ jj git fetch
$ jj rebase -d $main_bookmark
在 Git 共存仓库中工作
执行 jj git init --colocate
后,Git 会进入 detached HEAD 状态,这在 Git 中比较少见,因为 Git 通常是使用有名称的分支;但 jj 不是。
在共存(colocated)仓库中,每个 jj 命令都会自动同步 Jujutsu 和 Git 的仓库视图。例如,jj commit
会更新 Git 仓库的 HEAD,支持渐进式迁移。
$ nvim docs/tutorial.md
# 做一些修改
$ jj commit -m "Update tutorial"
# 在工作副本的父提交上创建书签
$ jj bookmark create doc-update -r @-
$ jj git push --allow-new
在 Jujutsu 仓库中工作
在 Jujutsu 仓库中,工作流程更简洁。如果你不需要显式命名书签,可以让 Jujutsu 为某个变更自动创建书签。
# 做你的开发工作
$ jj commit
# 推送变更 "mw",Jujutsu 会自动创建一个名为 "push-mwmpwkwknuz" 的书签
$ jj git push --change mw
处理代码审查评论
处理审查意见有两种常见方式,具体取决于项目偏好。很多项目建议你通过添加新的提交来回应评论(参考 1),而像 Jujutsu 和 LLVM 这样的项目则更倾向于你通过重写提交并强制推送来保持提交记录整洁(参考 2)。
添加新提交
如果你的项目推荐在已有提交上追加新的提交来处理审查意见,可以这样做:
# 在前面创建的 `your-feature` 书签上创建新提交
$ jj new your-feature
# 修改代码以回应评论,然后查看更改
$ jj diff
# 添加提交说明并创建新的工作副本
$ jj commit -m 'address pr comments'
# 更新书签指向新提交
$ jj bookmark move your-feature --to @-
# 推送到远程仓库
$ jj git push
上面的流程会为你创建一个新提交,也可以选择不创建新提交,直接修改当前提交。
⚠️ 注意
建议在以下操作后执行 jj new
,否则之后的所有更改仍会被追加到前一个提交中:
# 在 `your-feature` 书签上创建新提交
$ jj new your-feature
# 修改代码以回应评论,然后查看更改
$ jj diff
# 添加提交说明
$ jj describe -m 'address pr comments'
# 更新书签指向当前提交
$ jj bookmark move your-feature --to @
# 推送到远程仓库
$ jj git push
重写提交
如果你的项目推荐保持提交整洁,可以这样处理:
# 在 `your-feature` 倒数第二个提交上创建新提交(因为审查者要求修改那里)
$ jj new your-feature- # 注意这个结尾的连字符不是拼写错误!
# 修改代码以回应评论,然后查看更改
$ jj diff
# 把更改合并进父提交
$ jj squash
# 将更新后的书签推送到远程,Jujutsu 会自动执行强制推送
$ jj git push --bookmark your-feature
这个结尾的连字符来自 revset 语法。
与他人的书签协作
默认情况下,jj git clone
会导入默认远程书签(通常是 main
或 master
),但 jj git fetch
不会把新的远程书签导入到本地书签中。如果你想测试或基于其他贡献者的书签继续工作,需要使用:
$ jj new <bookmark>@<remote>
如果你想自动导入所有远程书签(包括不活跃的),可以在配置文件中设置:
git.auto-local-bookmark = true
这样就可以直接用:
$ jj new <bookmark>
而不需要指定 <remote>
。更多信息见:自动创建本地书签配置。
使用 GitHub CLI
在非共存仓库中,GitHub CLI 有可能找不到正确的 Git 路径(参见 issue #1008)。你可以通过设置 $GIT_DIR
环境变量来指定正确路径:
$ GIT_DIR=.jj/repo/store/git gh issue list
也可以用 direnv 自动设置这个变量。在仓库根目录创建 .envrc
文件并添加如下内容:
export GIT_DIR=$PWD/.jj/repo/store/git
然后运行:
$ direnv allow
这样在非共存仓库中 GitHub CLI 也能正常工作,可以使用 gh issue list
等命令。
常用 Revset 查询
列出所有本地书签下的提交(不包括 main
和远程):
$ jj log -r 'bookmarks() & ~(main | remote_bookmarks())'
列出所有你本人提交的、未在远程仓库中的提交:
$ jj log -r 'mine() & bookmarks() & ~remote_bookmarks()'
列出所有你本人参与提交的远程书签:
$ jj log -r 'remote_bookmarks() & (mine() | committer(you@email.com))'
列出当前工作副本的所有祖先提交(不在任何远程上):
$ jj log -r 'remote_bookmarks()..@'
合并冲突
关于 Jujutsu 如何处理合并冲突的详细说明,请参考 教程中的冲突部分。
使用多个远程仓库
在协作项目中使用多个远程仓库是很常见的做法。例如:
upstream
表示主项目仓库(最终要提交 PR 的地方)origin
表示你自己的私有 fork
$ jj git clone --remote upstream https://github.com/upstream-org/repo
$ cd repo
$ jj git remote add origin git@github.com:your-org/your-repo-fork
这会自动将你的仓库设置为跟踪上游仓库的主书签,通常是 main@upstream
或 master@upstream
。
你可以选择从 upstream
拉取(jj git fetch
),并向 origin
推送(jj git push
)。你可以在配置文件(例如 .jj/repo/config.toml
)中设置默认拉取和推送远程仓库:
[git]
fetch = "upstream"
push = "origin"
git.fetch
和 git.push
的默认值都是 origin
。
如果你经常在多台电脑上协作同一个项目,可以配置 jj 默认从多个远程仓库拉取,这样可以保持你自己在 origin
上的书签同步:
[git]
fetch = ["upstream", "origin"]
push = "origin"