Jujutsu 中的 GitHub Stacks
摘要
关于使用 GitHub 的堆叠拉取请求功能与 Jujutsu 版本控制系统结合使用的技术入门指南,涵盖先决条件、jj 配置以及创建和链接堆叠的手动工作流程。
<p><a href="https://lobste.rs/s/keiw21/github_stacks_jujutsu">评论</a></p>
查看缓存全文
缓存时间: 2026/08/13 15:24
# Jujutsu 中的 GitHub Stacks
来源:https://alan.norbauer.com/articles/github-stacks-with-jujutsu/
GitHub 于 2026-07-30 将其 堆叠拉取请求(PR)功能(https://github.blog/changelog/2026-07-30-stacked-pull-requests-are-now-in-public-preview/) 公开预览。该功能主要面向 git 用户,但也记录了对其他兼容 git 的版本控制系统的支持,比如 Jujutsu(jj)(https://docs.jj-vcs.dev/)。这是一份使用 jj 配合 GitHub stacks 的入门指南。
## 前提条件(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#prerequisites)
### 安装 CLI(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#install-clis)
- 你需要安装 `git`(安装说明(https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)) 和 `jj`(安装说明(https://docs.jj-vcs.dev/latest/install-and-setup/)) CLI。
- 你需要安装 `gh` GitHub CLI(安装说明(https://cli.github.com/))。
- 使用 `gh extension install github/gh-stack` 安装 stacking 扩展。
本文撰写/测试时使用的版本:
- jj v0.43.0
- git v2.52.0
- gh v2.96.0
- github/gh-stack v0.1.0
### 配置 jj(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#configure-jj)
我将复用上一篇文章《Jujutsu 中的 Stacks》(https://alan.norbauer.com/articles/stacks-in-jujutsu#full-configuration)中定义的一些 jj 别名和 revset 别名。
### 额外福利:学习 `jj` 的书签别名(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#bonus-learn-jjs-bookmark-aliases)
GitHub 的 Stacked PRs 要求每个 PR 都有一个 `jj` 书签(git 分支),因此你需要创建很多书签。未来的 UI 工具必定会自动在底层创建分支名称,但现在,先学习用于轻松管理书签的 `jj` 别名:
| 命令 | 描述 |
|------|------|
| `jj b l` | 列出书签 |
| `jj b s -r ` | 创建(或移动)书签到指定提交 |
| `jj b a` | 将最近的书签推进到当前提交(类似 `jj tug`) |
| `jj b --help` | 包含所有命令别名的文档 |
💡 题外话:你知道吗?你可以通过自定义 revset 来改进默认的 `jj bookmark advance`(`jj b a`)行为。如果你位于一个带有代码的提交之上的新空白提交,你可能希望将书签推进到下方包含代码的提交。你可以通过以下配置实现:
```
[revset-aliases."closest_pushable(to)"]
definition = 'heads(::to & mutable() & ~empty() & description(regex:".+"))'
doc = "Closest mutable, non-empty, described commits at or behind to"
[revsets]
bookmark-advance-to = "closest_pushable(@)"
```
jj 使得通过覆盖内置的 `bookmark-advance-to` revset 来自定义行为变得非常简单,你可以将其设置为任何你想要的内容。
## 手动工作流(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#manual-workflows)
`gh stack` CLI 有一堆子命令,其中大部分用于管理或消费本地对 stack 的跟踪(例如 `init`、`view`)。在 jj 仓库中,你只会使用少数几个用于管理 GitHub 上远程 stack 的子命令(例如 `link`、`push`),具体如下:
### 创建一个 stack(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#create-a-stack)
1. 为 stack 中的每个提交创建一个书签:
```
# 使用我们的 revset 别名转到 stack 底部
jj edit -r "stack_bottom()"
# 为提交命名
jj b c
# 转到下一个提交
jj next --edit
# 为提交命名
jj b c
# ... 对 stack 中的每个提交重复操作
```
2. 将你的书签上传到 GitHub,为每个书签创建草稿 PR,并将它们全部链接到一个 stack 中:
使用我们的 stack 别名(https://alan.norbauer.com/articles/stacks-in-jujutsu#stack):
```
gh stack link $(jj sb -r "stack()")
```
或者,使用 jj 修订版本范围:
```
gh stack link $(jj sb -r commit1::commitN)
```
或者,由于 `jj sb` 默认使用你的 substack(https://alan.norbauer.com/articles/stacks-in-jujutsu#substack),你可以转到顶部(https://alan.norbauer.com/articles/stacks-in-jujutsu#top-and-bottom)(此时你的 stack 和 substack 是同一回事),然后链接整个 stack:
```
jj top --edit
gh stack link $(jj sb)
```
最后一种可能是最容易记的。
### 在 stack 的**顶部**添加新提交(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#add-a-new-commit-to-the-top-of-the-stack)
假设新提交的变更 ID 是 `abc`:
1. 确保你正在编辑你的新提交:
2. 为新的、当前提交创建一个书签:
3. 如果你修改了任何 downstack 提交,你需要推送更新(因为 `jj git push` 会执行 `--force-with-lease` 推送,但 `gh stack link` 不会):
4. 使用所有 stack 书签重新运行 link:
现有的 PR(用于所有 downstack 的)将被跳过,新分支将获得一个新的草稿 PR,并被链接到 stack 中。
### 在 stack 的**底部**或**中间**添加新提交(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#add-a-new-commit-to-the-bottom-or-middle-of-the-stack)
你不能在 stack 的中间添加内容(否则会收到错误:“无法更新 stack:新的 PR 必须添加到现有 stack 的顶部”),因此你必须删除你的 stack 并重新创建它。假设新提交的变更 ID 是 `abc`:
1. 转到 GitHub Web UI 并移除该 stack:
截图:GitHub stack UI 显示“Unstack pull requests”“ 按钮
2. 为新提交创建一个书签:
3. 由于你修改了已变基的 upstack 提交,请重新推送所有内容:
4. 使用所有 stack 书签(包括新的书签)重新运行 link:
```
jj top --edit
gh stack link $(jj sb)
```
现有的 PR 将被跳过,新分支将获得一个新的草稿 PR,并被链接到 stack 中。
### 从 stack 中移除一个提交(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#remove-a-commit-from-the-stack)
你必须删除你的 stack 并重新创建它。假设要移除的提交的变更 ID 是 `abc`:
1. 转到 GitHub Web UI 并移除该 stack:
截图:GitHub stack UI 显示“Unstack pull requests”“ 按钮
2. 确保你位于 stack 的顶部:
3. 如果你还没有放弃该提交,请放弃它:
4. 推送你的更新:
5. 使用所有 stack 书签重新运行 link:
```
jj top --edit
gh stack link $(jj sb)
```
现有的 PR 将被跳过,stack 将被重新创建。
6. 你移除的提交将在 GitHub 上留下一个孤立的 PR。如果你愿意,可以手动关闭它和/或删除远程分支。或者,使用 `jj git push --deleted` 推送*所有*已删除的书签。
注意:删除远程分支会关闭其 PR,因此如果你在重新运行 `gh stack link ...` *之前* 运行 `jj git push --deleted`,你最终会同时关闭被删除分支上方的 PR。在链接之后删除以避免这种情况。
### 更新现有 stack 的现有提交(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#update-the-existing-commits-of-an-existing-stack)
1. 重新推送你的 stack:
### 合并你的 stack(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#merge-your-stack)
与取消堆叠一样,理论上可以从 CLI 执行,但太复杂了:
```
gh stack link --open $(jj sb -r "stack()") # draft => published
gh stack merge $(gh pr view "$(jj sb -r "stack()" | awk '{print $NF}')" --json number --jq .number) --yes --squash
```
所以,干脆从 Web UI 操作吧。反正你可能也会在那里查看 CI 信号。🤷🏽♀️
## 更新(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#updates)
几乎可以肯定有改进上述工作流的方法,而我完全没有想到。如果你有好的建议,请联系我(https://alan.norbauer.com/contact),我会更新这篇文章。
## 印象与结语(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#impressions-and-closing)
我希望的未来改进:
1. 基于上述手动步骤构建的 GUI 工具,自动消除繁琐操作。我个人使用*出色的* VisualJJ VSCode 扩展(https://www.visualjj.com/)来完成我 80% 的 jj 命令,我猜想开发者们正在努力添加 GitHub stack 支持。
2. `gh stack` 扩展能获得一个单一的 `gh stack submit` 子命令,自动搞定一切,让上面的所有内容变得过时。
3. 对需要删除 stack 并重新创建的工作流的改进,例如向 stack 中间添加提交。
就目前而言,这些东西大体上能工作,我对 GitHub 中获得 stacking 功能感到*非常*兴奋。我最初的印象是,人们可以立即将这个功能用好,我很感谢开发这个功能的开发者们。话虽如此,让我们承认房间里的大象:这是一个缺乏雄心的 stacked PR 实现。GitHub 的 stacked PRs 完全建立在现有、有缺陷的 GitHub PR 模型之上,没有改变任何基础:
> 我为我的直接表示道歉,但你们参考了哪些先例?为什么让人们为 stack 中的每个变更创建一个分支?为什么开发者在迭代 PR 时必须创建新的提交?对 interdiffs 的适当支持在哪里?变更 ID 呢?
> ——sunshowerson Hacker News(https://news.ycombinator.com/item?id=49117162)
没有自动的变更 ID,我们得到的是手动分支。没有自动的 `gh stack submit`(如 graphite 的(https://graphite.com/docs/cheatsheet#syncing-and-submitting) 或 git-branchless 的(https://github.com/arxanas/git-branchless/wiki/Command:-git-submit) submit 命令),你只能手动将分支列表喂给 `gh stack`。等等。
简而言之,对开发者体验期望放低一些,你会对最终结果感到满意:一个能在 GitHub 中正常工作的 PR stack。
## 生产说明(https://alan.norbauer.com/articles/github-stacks-with-jujutsu/#production-notes)
AI 事实
每容器 1 份
份量约 1 篇文章
每份含量
机器手写文字 0%
% 机器* - 散文手写。机器校对。手工应用修改。0% - 代码与配置0% - 创作所有代码/配置均为手工编写。0% - 验证所有代码/配置均由 Claude Opus 5 进行程序化验证,确保正确性和一致性。手工应用修复。100% - 人工审查100%* % 机器 告诉你这篇文章有多少是由机器编写的。数值是随意估计的,并非精确测量。本标签未经美国食品药品监督管理局评估。
相似文章
Jujutsu 如何重新思考 Git 的工作副本和冲突模型
Jujutsu (jj) 是一个新的版本控制系统,它重新思考了 Git 的工作副本和冲突模型,在保持完全 Git 兼容性的同时,提供了更一致的工作流程。
Evan的Jujutsu教程
面向熟悉Git的用户,关于版本控制系统Jujutsu (jj)的简明教程。
jujutsu v0.42.0 发布
Jujutsu (jj) 版本控制系统发布了 v0.42.0。Jujutsu 是一款开源 VCS,以 Git 作为存储后端,同时提供更符合人体工程学的操作界面,其功能设计灵感来源于 Mercurial、Sapling 和 Darcs。
用Jujutsu战胜Git严谨疲劳
本文介绍了一种使用Jujutsu版本控制系统的工作流程,旨在克服在Git中保持严格提交纪律的疲劳感,允许开发者先进行杂乱提交,最后再整洁地重新组织它们。
spr:GitHub上的堆叠拉取请求
spr 是一个 CLI 工具,可将 Git 分支上的每个提交转换为 GitHub 上的独立拉取请求,无需手动管理分支即可实现堆叠式 PR。