Jujutsu 版本发布完全指南:从 CHANGELOG 整理到 crates.io 上线的全流程
2026/9/10 23:09:14 网站建设 项目流程

Jujutsu 版本发布完全指南:从 CHANGELOG 整理到 crates.io 上线的全流程

【免费下载链接】jjA Git-compatible VCS that is both simple and powerful项目地址: https://gitcode.com/GitHub_Trending/jj/jj

本文基于 jj 仓库的官方发布流程文档(web/docs/src/content/docs/releasing.md,与 docs/releasing.md 内容一致),完整讲解 Jujutsu(jj)项目维护者如何发布一个新版本:包括更新 CHANGELOG 与统一管理 Cargo 版本号、借助 jj 自身的变更集(changeset)能力生成 Changelog diff 与贡献者名单、在 GitHub 上打 tag 并创建 Release,以及按依赖顺序将各 crate 发布到 crates.io。读完本文,你将掌握 jj 项目完整的发版 SOP,并能直接套用到自己维护的 Rust workspace 项目中。

发布前的准备:更新 CHANGELOG 与 Cargo 版本

一次正式的 jj 发版,第一步不是打 tag,而是准备一个包含CHANGELOG 更新 + Cargo 版本号变更的 Pull Request(参考 jj 历史中类似 PR #7954 的做法)。这个 PR 通过评审并合并后,才进入打 tag 阶段。

整理 CHANGELOG 的四个检查要点

在准备该 PR 时,可以对 CHANGELOG 内容做自由改写(copy-edit),重点核对以下四点:

  1. 必要时填充 "Release highlights" 小节:重大版本需要在 CHANGELOG 顶部用简短段落概括本版本最值得关注的亮点;
  2. 把更重要的条目排在前面,避免读者错过关键变更;
  3. 统一条目的语言与格式,保持整份文档风格一致;
  4. 通过查看 CHANGELOG 的 diff 找出放错位置的条目(例如把Fixed bugs误写进New features)。

用 jj 自身生成 CHANGELOG diff

项目利用 jj 的 revset 与 diff 能力,直接从版本历史中提取上一个 tag 到main之间的 CHANGELOG 改动:

jj log -r 'heads(tags())' # 检查该 revset 是否显示上一个版本 jj diff --from 'heads(tags())' --to main CHANGELOG.md

这里heads(tags())表示"所有 tag 中最新的那个"(即上一个发版提交),jj diff --from ... --to ...则精确展示两次提交之间 CHANGELOG.md 的变更,便于逐条核对每个条目是否都进了正确的小节。

补充版本对比引用链接

在 CHANGELOG 文件底部有一组"引用链接"(reference link),形如:

[unreleased]: https://github.com/jj-vcs/jj/compare/v0.45.1...HEAD [0.45.1]: https://github.com/jj-vcs/jj/compare/v0.45.0...v0.45.1

发布新版本时需要做两件事:

  • 新版本的 tag添加一条对比链接,格式为"上一个版本 tag 与当前版本 tag 的 compare URL",例如https://github.com/jj-vcs/jj/compare/v0.32.0...v0.33.0
  • 同时把[unreleased]链接更新为"最新版本 tag 对比 HEAD"。

在 CHANGELOG.md 底部可以看到这条既成的约定:当前仓库的[unreleased]v0.45.1...HEAD,而[0.45.1]v0.45.0...v0.45.1,逐版本串成一条完整的对比链。

CHANGELOG 的格式规范

从 CHANGELOG.md 的头部可见,本项目遵循两个公开约定:

  • Keep a Changelog:每个版本小节下统一使用### Release highlights### Breaking changes### Deprecations### New features### Fixed bugs等固定小节;
  • Semantic Versioning:版本号按主.次.补丁规则递增,每个小节以## [版本号] - 日期作为标题,例如## [0.45.1] - 2026-09-03

Cargo 版本号的统一管理:workspace.package.version

jj 是一个 Cargo workspace,所有 crate 的版本号都从根 Cargo.toml 的[workspace.package]统一继承:

[workspace.package] version = "0.45.1" rust-version = "1.89" edition = "2024"

各成员 crate(clicorecore/proc-macrosliblib/gen-protoslib/testutils)在自己的Cargo.toml中通过version = { workspace = true }引用,因此发版时只需要在根 Cargo.toml 改一处版本号。这一点可以在 lib/Cargo.toml、core/Cargo.toml、cli/Cargo.toml 等处逐一验证。

值得留意的是,根 Cargo.toml 中rust-version = "1.89"旁边有一行注释提醒维护者:升级最低 Rust 版本时,还要同步更新 CI、mise.toml、contributing.md、changelog.md 和 install-and-setup.md 等相关文件,避免文档与代码事实脱节。也就是说,发版 PR 不仅要改版本号与 CHANGELOG,也要顺带检查这类跨文件的一致性。

另外,版本号与 CLI 输出直接关联:jj version命令的实现位于 cli/src/commands/version.rs,它通过command.app().render_version()渲染版本信息,因此 workspace 版本号一旦更新,用户通过jj version看到的版本也会随之变化。

生成贡献者名单

整理贡献者名单"有点麻烦",文档给出了两套方案。

方案一:通过 GitHub REST API(推荐),利用 jj 的 revset 先算出"上一个版本 tag 到trunk()主干之间的全部提交"的根提交,再调用 GitHub 的 compare API 分页拉取提交列表并用jq去重、格式化:

root=$(jj log --no-graph -r 'heads(tags(glob:"v*.*.*") & ::trunk())' -T commit_id) filter=' map(.commits[] | select(.author.login | endswith("[bot]") | not)) | unique_by(.author.login) | map("* \(.commit.author.name) (@\(.author.login))") | .[] ' gh api "/repos/jj-vcs/jj/compare/$root...main" --paginate | jq -sr "$filter" | sort -f

其中filter的作用是:过滤掉以[bot]结尾的机器人账号、按作者 login 去重、再输出* 姓名 (@用户名)的 Markdown 列表格式。

方案二:纯本地生成,不依赖网络 API:

jj log --no-graph -r 'heads(tags())..main' -T '"* " ++ author ++ "\n"' | sort -fu

此方案直接枚举heads(tags())..main范围内所有提交的作者并去重排序。由于 jj 本地只存作者名(author模板字段),还需要手动为每个人找到对应的 GitHub 用户名,并从对方的 GitHub 主页复制姓名与用户名补进名单。相比之下,方案一能直接产出"姓名(@用户名)"的完整格式,省去手工匹配。

名单整理完成后,与 CHANGELOG、Cargo 版本号改动一起提交 PR,走常规评审流程合并。

打 tag 与创建 GitHub Release

PR 合并后,在 GitHub Releases 页面完成打 tag 与发布,文档给出的操作步骤如下:

  1. 进入仓库的Releases页面,点击 "Draft a new release";
  2. 点击 "Choose a tag",输入v0.<number>.0(例如v0.26.0)以创建新 tag;
  3. 点击 "Target" → "Recent commits",选中刚合并的发版 PR 对应的提交;
  4. 以 tag 名称(如v0.26.0)作为 "Release title",把 CHANGELOG 中新版本对应的条目粘贴到正文;
  5. 勾选 "Create a discussion for this release",方便社区围绕该版本展开讨论;
  6. 点击 "Publish release" 完成发布。

将 crate 发布到 crates.io

为什么必须从全新 clone 发布

发布前先在临时目录创建一个全新的仓库 clone:

cd $(mktemp -d) jj git clone https://github.com/jj-vcs/jj cd jj jj new v0.<number>.0

jj new v0.<number>.0会把工作副本切换到刚打好的 tag 提交上。文档特别强调建议从全新 clone 发布,原因是一个安全风险:cargo publish打包时会包含[include]模式匹配到的、即使已被 git 忽略的文件。如果在长期使用的本地 clone 里发布,忽略文件中可能遗留敏感内容(密钥、凭据等)而被一并打进 crate 包。在 lib/Cargo.toml 可以看到这种include字段的真实写法:

include = [ "/LICENSE", "/src/", "/tests/", "!*.pending-snap", "!*.snap*", ]

cli/Cargo.toml同样有include = ["/LICENSE", "/build.rs", "/examples/", "/src/", ...]的配置。在陌生环境的新 clone 中,工作目录干净、无残留敏感文件,可以把这个风险降到最低。

按依赖顺序逐个发布 crate

jj 的 Rust workspace 包含 6 个成员 crate(见根 Cargo.toml):

[workspace] members = [ "cli", "core", "core/proc-macros", "lib", "lib/gen-protos", "lib/testutils", ]

但并非所有 crate 都需要发布:从 lib/gen-protos/Cargo.toml 和 lib/testutils/Cargo.toml 可以看到这两个 crate 都声明了publish = false,它们只是构建期工具与测试辅助库,不进入 crates.io。真正需要发布的 crate 及依赖关系为:

  • jj-core-proc-macros(core/proc-macros):jj-core的派生宏,必须先发布;
  • jj-core(core):依赖jj-core-proc-macros,核心类型与算法;
  • jj-lib(lib):依赖jj-core,核心库实现;
  • jj-cli(cli):依赖jj-lib,最终命令行程序。

因此发布命令依次为:

(cd core/proc-macros && cargo publish) (cd core && cargo publish) (cd lib && cargo publish) (cd cli && cargo publish)

(原文档发布时仓库结构为lib/proc-macroslibcli三个 crate,命令为(cd lib/proc-macros && cargo publish)(cd lib && cargo publish)(cd cli && cargo publish);当前仓库因拆分出jj-core而多出一层依赖,CHANGELOG.md 中0.45.1版本条目明确提到"修复了新的 jj-core crate 无法发布的问题",印证了这一依赖顺序的必要性。)每个 crate 依次发布,前一个成功后再发布下一个,确保 crates.io 上的依赖版本始终可用。

小结:一次完整发版的检查清单

把整个流程收拢成一份可执行的清单:

  1. 准备发版 PR:基于heads(tags())main的 diff 整理 CHANGELOG.md,填充 Release highlights、按重要性排序、统一格式、归位错放的条目;在文件底部添加新版本 compare 引用链接并更新[unreleased]链接;
  2. 升级版本号:修改根 Cargo.toml 中[workspace.package] version(及各 crate 继承的版本),生成贡献者名单(优先用gh api+jq方案),提交并合并 PR;
  3. 打 tag 并发 GitHub Release:以v0.<number>.0命名 tag,指向发版 PR 的提交,粘贴 CHANGELOG 内容,勾选讨论区后发布;
  4. 发布 crates.io:在mktemp -d的全新 clone 中jj new v0.<number>.0切到 tag,再按jj-core-proc-macros → jj-core → jj-lib → jj-cli的依赖顺序逐个cargo publish

整个过程的一个独特之处在于:整个发版流程大量使用 jj 自身的命令jj logjj diffjj new、revset 表达式heads(tags())::trunk()等)来辅助版本管理——用 jj 来发布 jj,既是流程,也是对新版本自身能力的一次实战检验。

【免费下载链接】jjA Git-compatible VCS that is both simple and powerful项目地址: https://gitcode.com/GitHub_Trending/jj/jj

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询