在维护开源包和工具库的这些年里,我几乎每天都在跟"版本管理"这四个字较劲。手动改package.json里的版本号、写完代码再回头补 changelog、发布前纠结到底是patch还是minor——这套流程在只有一个仓库、两三个包的时候还能勉强应付,一旦上了 monorepo、仓库里躺着十几个互相依赖的包,每次发版都像在走钢丝。而 Changesets 就是我在踩了无数次坑之后,最终留下来并一直在用的版本管理方案。这篇文章我会从设计思路到实操落地,掰开揉碎讲清楚它到底解决了什么问题、怎么用、以及那些文档里不会写但你会真实遇到的坑。
1. 内容整体设计与核心思路
1.1 版本管理到底难在哪里
先说说我自己的经历。早期维护一个工具库,每次发布前都要手动做三件事:改版本号、更新 changelog、打 tag。听起来不复杂,但实际执行起来全是问题。
第一,版本号改错。主版本、次版本、补丁版本的语义规则大家背得滚瓜烂熟,但真到了"这个 PR 是加了新功能还是修了 bug"的判定环节,人脑很容易双标。同一个功能,有人觉得是minor,有人觉得是patch,最后谁嗓门大听谁的。
第二,changelog 靠回忆。发版那天才想起来要写 changelog,于是开始翻 commit 记录。而大部分 commit message 写得跟天书一样,什么 fix、refactor、update,根本看不出来到底改了什么,更别提交替用户梳理哪些是破坏性变更。
第三,monorepo 场景下彻底失控。我维护过一个使用 pnpm workspace 的项目,仓库里有 8 个包,其中 3 个包互相依赖。发布 A 包时改了它依赖的 B 包版本,却忘了检查 C 包是否也需要同步升级。结果是用户在装 C 包时拉到了升级前版本的 B,线上出现了诡异的兼容问题。
这些问题的本质在于:版本管理不是发布那一刻的动作,而是从代码提交那一刻就应该开始的持续过程。Changesets 解决的就是"过程的记录"与"决策的沉淀"。
1.2 Changesets 的核心设计哲学
Changesets 的设计思路和传统方案有一个根本区别:传统方案是"我告诉你我改了什么,你帮我生成版本号",Changesets 是"你描述这次改动的影响,我根据描述决定版本怎么变"。
具体来说,它要求开发者在每个 PR 里附带一个 changeset 文件。这个文件不是随便写个数字,而是包含三件事:这次改动涉及哪些包、版本变化的类型(patch、minor、major)、以及给用户看的变更说明。多个 changeset 可以汇总发布,也可以独立发布,全看你的发布节奏。
这套设计带来的直接好处是决策前置。提交代码的时候,你刚刚写完这个功能,对它的影响面记忆最清晰。而此时写下"这是 breaking change",比发布前翻 commit 去回忆要准确得多。而且因为 changeset 文件是跟着 PR 一起 review 的,团队里其他人也能在代码评审阶段就参与版本决策的讨论,而不是等到发版那天才来吵。
这个机制还间接解决了一个问题——changelog 不再需要"写"。Changesets 会在changeset version阶段自动把你的变更说明合并进项目的 CHANGELOG.md,格式统一、分发到位。你只需要在写 changeset 时把话说清楚,剩下的排版工作交给工具。
1.3 为什么不用语义化版本的自动化推导
有的团队会用 commit message 的规范来自动推导下一个版本号,比如 conventional commits + semantic-release。这套方案我试过,在简单项目里体验确实顺滑,但在 monorepo 里会碰上两个大问题。
第一个问题是多包版本联动的不确定性。monorepo 里一个 commit 可能同时改了多个包,如果用 commit message 推导版本,你很难在单个 commit 的 message 里精确表达"A 包是 minor、B 包是 breaking、C 包不变"这种组合。硬要表达也不是不行,但 commit message 会变得非常啰嗦,而且解析规则稍微一复杂就容易出错。
第二个问题是发布粒度偏粗。语义化提交方案往往倾向于"一个 commit 对应一个版本",但在实际开发中,某个功能可能需要拆成 5 个 commit 才能完成,或者一个 PR 里既有新功能又有修复。用 commit 推导版本,灵活性远不如单独维护一个 changeset 文件来得高。
所以我的结论是:自动化程度最高的方案不一定是最适合团队的方案。Changesets 提供了恰到好处的自动化——版本变更的"记录"自动化了,但"决策"依然留给人。这种平衡在实际工程里往往更可持续,因为版本号的语义本质上是一种公共契约,它应该经过人的确认,而不是完全交给规则。
2. 核心机制与关键技术拆解
2.1 changeset 文件到底是什么
你可能已经在很多开源仓库里见过.changeset目录,里面的 markdown 文件常常以young-pandas-raise.md这种随机风格命名。这就是 changeset 文件,它看起来像这样:
--- "@scope/package-a": minor "@scope/package-b": patch --- 这是一个新功能,支持了某某参数,用法如下:...三段式结构:YAML 前置信息描述哪些包需要版本变更以及变更级别,正文描述给用户看的变更说明。文件名的随机后缀纯粹是为了避免同名冲突,没有实际语义。
这里有个细节值得展开:并不是所有包都要出现在 changeset 文件里。如果你的某个改动只涉及 A 包,那 changeset 里就只写 A 包。B 包虽然没有出现在 changeset 里,但如果发布了,Changesets 会根据内部依赖关系判断它是否需要连带更新版本,这个机制我在后面会详细讲。
2.2 changeset add 命令的实际执行过程
实际开发中不会手动创建这些 markdown 文件,而是通过命令生成。在项目根目录执行:
npx changeset或者使用 pnpm:
pnpm changeset这个命令会启动一个交互式问答。先让你选择本次变更涉及哪些包(支持多选),然后逐个询问每个包的版本变更级别,最后打开一个编辑器让你写变更说明。整个过程结束后,会在.changeset目录下生成一个新的 markdown 文件。
有些朋友第一次用会觉得这个过程繁琐,毕竟它强制你停下来想清楚。但我发现坚持用一段时间后,这个"繁琐"反而是它的价值所在——它把"版本决策"这个原本发生在发版日、耗时且容易出错的动作,成功拆解成了日常提交时一个轻量的习惯。每条变更的影响面清清楚楚,发版变成了一次点击按钮就能完成的执行动作。
2.3 版本策略配置:fixed 和 linked
默认情况下,Changesets 把每个包当作独立的版本线来控制。但在 monorepo 中,很多团队其实使用 fixed 模式——所有包保持相同的版本号,发布时一起发。最典型的例子就是 React 或 Vue 这类框架,一个仓库里虽然拆分了多个包,但对外版本号必须保持一致。
配置方式是在.changeset/config.json中声明:
{ "fixed": [ ["@scope/pkg-a", "@scope/pkg-b"] ] }凡是出现在同一个数组里的包,版本号会被绑定在一起。其中任何一个包产生了major变化,其他包也会跟着升major;任何一个包发版,其他包也会在同一个版本更新中一起发。这样就不会出现"主包升级到 2.0,副包还停在 1.4"的尴尬状态。
linked模式就温和一些。它只保证包之间的版本号保持在相同的"刻度"上——比如某个数组里的包都是1.2.x,那就必须都是1.2.x,但不需要完全相同的版本号。它更适合那些没有互相依赖,但希望版本节奏保持一致的兄弟包。
我个人的经验是:如果包之间没有硬依赖关系,尽量避免使用fixed,不然每个包的版本号都会被"木桶效应"拖着走,一个包的频繁改动会让所有包都跟着发版,产生大量无意义的 changelog 记录。
2.4 内部依赖声明的自动更新逻辑
monorepo 中最烦人的一个问题是:A 包依赖 B 包,B 包升了版本,A 包的package.json里"@scope/b": "^1.0.0"这个声明是否要跟着变?
Changesets 的答案是:默认情况下来个自动更新。在changeset version执行时,工具会检查所有受影响的包之间的依赖关系,如果某个包的依赖也发生了版本变更,会在该包的package.json中自动把这个依赖的版本范围更新到新版本。这个行为由配置项updateInternalDependencies控制,默认值是patch,意思是只要内部依赖的补丁版本升级了,就同步更新声明。
这里有三种行为级别:patch(补丁版升级就更新)、minor(次版本升级才更新)、none(永远不自动更新)。在 workspace 协议(workspace:*)下,pnpm 和 Changesets 配合得非常好,生成发布后的版本声明时会将workspace:*替换为实际版本号。
这个自动更新机制解决了 monorepo 里的老大难问题。以前我手动处理这个问题时,经常忘了更新某个下游包的依赖声明,导致用户拿到的是过时的依赖版本。现在这个环节完全自动化了,发布出去的包,依赖声明永远是准确的。
2.5 changelog 的生成与定制
Changesets 自带一套 changelog 生成逻辑,会读取 changeset 中 markdown 部分的文字内容,再附带上版本号和日期,最终生成 CHANGELOG.md。默认的格式长这样:
# @scope/pkg-a ## 1.2.0 ### Minor Changes - 支持了某某参数,用法如下:...如果你对格式不满意,可以通过配置changelog字段指向一个自定义的生成函数。这个函数接收两个参数——changesets 数组和 options 对象,返回一段字符串作为追加到 changelog 中的内容。
我见过有的团队把 changelog 生成函数直接对接自己的文档系统,这样版本发布后文档自动更新,整个链路彻底贯通。也有人用它做自动化通知——在 changelog 里插入特定的标记,CI 发布后通过 webhook 推送到钉钉或飞书群。这块的玩法非常多,全看你的想象力。
3. 实操过程:从零到一完整走一遍
3.1 安装和初始化配置
如果你的项目还没有安装 Changesets,第一步是安装:
pnpm add -D @changesets/cli然后初始化:
npx changeset init这个命令会在项目下生成.changeset目录,里面包含一个config.json文件和一个README.md文件。默认的config.json长这样:
{ "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [], "linked": [], "access": "restricted", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] }有几个配置项需要解释一下:
changelog:指定 changelog 生成逻辑。默认使用@changesets/cli内置的生成器。如果你想用自定义的生成器,这里改成模块路径即可。commit:设置为true时,执行changeset version会自动生成 git commit。我通常设为false,因为我希望 commit 是手动控制,而不是工具代劳。团队协作时尤其谨慎,自动化生成的 commit message 往往不够直观。access:发布到 npm 的访问级别。如果是公开包,设为public;如果是私有包,保持restricted。baseBranch:告诉 Changesets 你的主分支是哪条。这关系到changeset status --since-master这类命令的比较基准。
3.2 开发过程中:创建和积累 changeset
项目初始化完成之后,日常开发流程就变成了这样:拿到需求 → 创建功能分支 → 写代码 → 提交代码前执行pnpm changeset→ 回答几个问题 → 把生成的 markdown 文件一起提交。
这里我踩过一个有意思的坑。最初引入 Changesets 的时候,团队成员经常忘记执行pnpm changeset,结果到了发版日发现这周改了 20 个文件,但.changeset目录里一个 changeset 都没新增。版本信息严重缺失,又走上了"临时翻 commit 回忆"的老路。
后来我总结出一个规律:把changeset命令放进 PR 的检查清单里,最好直接写进仓库的 CONTRIBUTING.md 文档。如果你用的是 GitHub,还能在 CI 里加一步检查——用changesets/action配合changeset status命令,如果当前 PR 没有附带 changeset 文件,就让 CI 直接报错。这一步非常有效,我现在维护的项目几乎不会出现缺少 changeset 的情况。
对于"临时修改、补丁之类的没必要写 changeset"的想法,我的建议是:只要这个改动会影响包的使用者,你就写。无论是修了个 typo 还是调整了样式,对用户来说都是行为变化。唯一不需要写 changeset 的情况,就是纯内部重构、测试代码变更这类用户完全感知不到的改动。
3.3 发版流程:changeset version 的幕后逻辑
当你的改动积累到差不多该发一版的时候,执行:
pnpm changeset version这个命令会做几件重要的事。第一,它会读取.changeset目录下所有未被消费的 changeset 文件。第二,它会根据这些文件里记录的版本变更级别,结合当前包的版本号,计算出每个包的新版本号。第三,它会更新所有受影响包的package.json、生成或追加 CHANGELOG.md 内容,并在必要情况下更新内部依赖声明。第四,它会删除这些已经被"消费"掉的 changeset 文件——本质上是把 changeset 的信息"沉淀"进了 changelog。
这个动作完成后,建议手动跑一下全量测试和构建,确认版本更新之后代码一切正常。然后把这些变更提交并推送。此时如果你配置了 CI 发布流程,发布时机是 release 分支的代码被合并进主干之后,而不是版本命令执行之后。版本命令只负责把各种信息落实到文件里,真正的发布动作靠后续的changeset publish命令。
3.4 发布过程:changeset publish 的注意事项
执行发布命令:
pnpm changeset publish这个命令等价于先对每个需要发布的包执行一次npm publish,然后为每个包在 git 上打 tag。它会跳过那些没有版本变化的包,只发布真正有更新的包。
发布过程中容易踩的坑主要有三个。
第一个坑:npm registry 的访问权限配置。如果你的包是 scoped 包(比如@scope/pkg-a),默认情况下 npm 会把它当作私有包处理,发布要求登录且包存在权限控制。如果你要发公开包,config.json里的access要设为public,并且需要配置 npm 的 token 环境变量(如NODE_AUTH_TOKEN)。在 CI 里这步经常被漏掉,导致 publish 时报 403。
第二个坑:包的publishConfig字段。建议在每个包的package.json里显式声明:
{ "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" } }这样可以避免因为环境变量缺失或 npm 配置残留导致发布到错误的 registry。我之前就遇到过因为电脑上残留了淘宝镜像源配置,发布时把包推到了镜像源上,花了半天才排查明白。
第三个坑:tag 的一致性。每发布一个包,Changesets 都会打一个形如@scope/pkg-a@1.2.0的 tag。如果你的发布流程中还有其他打 tag 的逻辑,注意别给同一个版本重复打 tag,否则会在 git 历史里留下混乱的标记。
3.5 monorepo 场景下的完整配置案例
这里给出一份我在 pnpm monorepo 项目中使用的完整配置,可以直接参考。项目结构:
packages/ core/ # 核心逻辑 react/ # React 封装 vue/ # Vue 封装 utils/ # 工具集.changeset/config.json:
{ "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [], "linked": [], "access": "restricted", "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [] }其中,packages/core是被packages/react和packages/vue依赖的基础包。core发布后如果版本有变化,react和vue的依赖声明会在changeset version时自动更新。
这个配置适合大多数 monorepo 项目。如果你的团队希望核心包和其他框架封装保持版本一致,可以把react和vue放进linked数组,让它们的版本刻度保持一致;反之,如果你希望完全独立决策每个包的版本号,就保持默认的独立模式。
3.6 通过 CI 自动化整个流程
人工发版的失误率再低也架不住次数多,能交给 CI 的就别用手点。我现在的项目用的是 GitHub Actions,配合官方提供的 changesets/action ,核心流程是这样的:
main 分支的每次 push 都会触发一个 workflow,运行一次changeset version,如果发现.changeset目录里有未消费的 changeset 文件,就创建一个名为changeset-release/main的 PR。这个 PR 里包含更新后的版本号、changelog 和内部依赖声明。团队 review 这个 PR 并合并后,再触发发布 workflow 执行pnpm changeset publish。
这套流程有个好处:版本变化和发布动作天然分离。版本更新的 PR 专门负责"告诉仓库接下来版本应该变成什么",合并发布则负责"把版本落到实处"。中间插入了人工 review 环节,相当于给发版操作加了一道安全闸。
发布 workflow 的publish脚本大概是这样的:
name: Release on: push: branches: - main jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: pnpm/action-setup@v2 - uses: actions/setup-node@v4 with: node-version: 20 registry-url: https://registry.npmjs.org/ - run: pnpm install --frozen-lockfile - run: pnpm changeset publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}这里特别要留意registry-url的配置。在 CI 中如果不显式指定 npm registry 地址,而是依赖.npmrc文件或环境变量,很容易出现 403 或 EPublish 错误。我在本地测试时遇到过由于全局.npmrc配置了私有镜像源,CI 里又忘了覆盖,发布时包被推到了镜像源上,等发现时已经产生了脏状态,只能删除包重新发,非常被动。
3.7 快照发布:让预发布环境也能拿到新版本
多分支协作时,你可能希望测试环境或其他下游团队能拿到某个功能分支的最新版本,而不是等正式发布。Changesets 提供了快照发布功能(snapshot release),允许你从任意分支发布带特殊后缀的预发布版本。
pnpm changeset version --snapshot pnpm changeset publish --tag beta这个举动有多重要呢?在你正式发版之前,下游团队可以先用上1.2.0-beta-20240910这种版本进行集成验证,发现问题及时反馈,而不是等到正式版发布后才发现兼容性问题。值得注意的是,快照发布打上的 git tag 会带上快照标识,不会污染正式版本号。我维护的组件库目前就采用这个策略,效果非常好,下游业务方的反馈周期从"月"缩短到了"天"。
这里有一个快照发布参数速查表格,方便你快速了解不同的用法:
| 参数组合 | 效果 | 适用场景 |
|---|---|---|
version --snapshot+publish --tag beta | 生成1.2.0-beta-<commit>预发布版本 | 功能分支集成验证 |
version --snapshot --snapshot-prerelease-template | 自定义快照版本格式(如1.2.0-canary) | 自定义预发布命名规则 |
不加--snapshot | 常规版本更新 | 正式发布流程 |
使用快照发布后,用户需要显式安装带betatag 的包才能在测试项目里获取该版本。正式发布时,主流程会忽略这些快照版本,正常走latesttag。我建议所有长期维护的库都建立快照发布机制,它是一个低成本高回报的协作方式。
4. 常见问题与排查技巧实录
4.1 忘记创建 changeset 文件
这是团队落地 Changesets 时最常见的失败模式。代码写完了,PR 也提交了,就是没执行pnpm changeset,导致发版时版本信息缺失。
如果项目已经用了 GitHub Actions,可以在 CI 中加入一个检查步骤:
- uses: actions/checkout@v4 with: fetch-depth: 0 - run: pnpm changeset status --since-origin/main其中--since-origin/main表示比较当前分支和 main 分支的差异。如果检测到有改动涉及已发布的包,但没有对应的 changeset 文件,这个命令会以非零退出码结束,CI 就会失败。
4.2 一个 PR 里写了多个 changeset 文件
有人会问:一个 PR 能否创建多个 changeset?可以,而且并不少见。比如一个 PR 里同时做了一个功能增强和版本重构,你希望分开记录,那么在执行pnpm changeset时选择不同的包、写不同的说明即可,工具会为每次交互生成独立的 markdown 文件。
但要注意,如果你在同一个 PR 里为同一个包创建了多个 changeset,在changeset version时这些 changeset 会被合并为同一次版本变更。比如两个 changeset 都声明patch级别,最终只会计算一次版本升级,不会叠加出两个版本号。
4.3 版本升级后发现变更级别错了
有朋友遇到过这个问题:发版后发现某个变更声明成了major,但实际只是minor,这个时候版本号已经写进了 changelog 和 package.json,还能改吗?
能改,但要趁早。只要还没执行changeset publish,你完全可以直接改 package.json 里的版本号和 CHANGELOG.md 的内容,把错误的版本修正后重新提交。如果已经发布了,你就只能发布一个新的正确版本,比如对版本2.0.0发布2.0.1修复错误声明。这是版本管理里的"覆水难收",所以执行changeset version时最好停下来确认一下所有变更级别是否符合预期。
4.4 依赖声明被更新成了workspace:*怎么办
这个坑主要出现在 pnpm 用户中。pnpm 的 workspace 协议允许包之间使用workspace:*作为依赖声明,开发和构建时 pnpm 会自动解析到本地 workspace 的包。但发布时,npm 要求包里的package.json必须是合法的语义化版本声明,不能有workspace:*。
Changesets 之所以能和 pnpm 无缝配合,是因为它在发布时会把workspace:*替换为对应的具体版本号。但有的时候这个替换没有生效,原因通常是 Changesets 版本过旧,或者你的package.json里写的是workspace:^1.0.0这样的范围而非精确匹配。
我的处理经验是:依赖声明统一写workspace:*,避免在 workspace 协议里使用精确版本或范围版本。Changesets 在处理workspace:*时最稳妥,会自动替换为新版本号;而workspace:^1.0.0这类写法在不同版本的 pnpm 和 Changesets 组合下行为不完全一致。
4.5 发布顺序的问题:先发依赖还是先发被依赖方
在 monorepo 中,包之间存在依赖关系是很正常的。发布时,Changesets 的处理逻辑是:先遍历所有需要更新的包,找到没有内部依赖或者依赖均已满足的包先行发布,再发布依赖了它们的包。这个顺序由工具内部自动计算,你通常不需要手动干预。
但有一种情况需要注意:如果你的发布流程不是用 Changesets 命令,而是自己写的脚本,那就要确保发布顺序正确。我曾经见过一个项目,发布脚本里直接遍历目录逐个npm publish,结果某个包先于它依赖的包发布,npm 检查时发现依赖版本不存在,直接报错中断。
4.6 私下发布但没打 tag
Changesets 发布时默认会给 git 打 tag。但如果你的 CI 配置出问题,发布成功了而 tag 没打上,就会导致 git 仓库和 npm 上的版本记录不一致。
排查方式是检查 npm 上每个版本的全局标签:
npm dist-tag ls @scope/pkg-a然后对比 git tag 列表。不一致时执行npx changesets tag可以补打 tag。这个命令只做一件事——为当前 package.json 里的版本创建对应的 git tag。
4.7 注意 prerelease 模式的使用边界
Changesets 提供了对 prerelease(预发布)版本的原生支持。在.changeset/pre.json文件中声明"mode": "pre"后,后续的changeset version会生成1.0.0-beta.0这类版本号,而不是常规版本。要退出预发布模式,则把"mode"改为"exit"再次运行changeset version。
这里有一个重要的坑:在 pre 模式下创建的 changeset,它的版本变更级别会被追加到预发布序列中,而不是主版本序列。如果你在 pre 模式中积累了多个 changeset,退出 pre 模式后这些 changeset 已经被消耗掉了,主版本号不会自动带上它们的变更。因此,如果你打算在 pre 模式结束后发一个版本包含全部累积变更,记得在退出前确认好 changeset 的累积情况,必要时提前合并。
4.8 常见的 CI 配置细节
最后整理几个 CI 配置中容易忽视的细节:
fetch-depth: 0:changeset 计算版本变化时需要完整的 git 历史,浅克隆会导致它看不到某些 commit 的变化。- 使用 pnpm 时,发布前最好跑一次
pnpm install --frozen-lockfile,避免 lockfile 被意外修改。 - CI 中执行
changeset version时,如果出现"找到了一个没有对应 changeset 的包"这类错误,说明该包在 package.json 中声明了版本变化,但没有可用的 changeset 来解释变化,通常是手动改了版本号导致的。 NODE_AUTH_TOKEN在 npm publish 时是必需的,且 token 需要有对目标包写入权限。如果你用的是 GitHub Actions 的actions/setup-node,记得在with里配置registry-url。
5. 从实际项目中总结的几条经验
最后说几点我在真实项目中获得的体会,这些未必写在官方文档里,但对实践很有帮助。
第一,把 changeset 视作 commit 的一部分。不要把它当成流程负担,而是把它当成一条需要随代码一起提交的"元数据"。当它成为习惯后,你会发现版本发布变成了一件非常轻松的事。执行version命令后生成的 changelog 内容,质量远高于你发版当天用十分钟赶制出来的说明。
第二,在团队引入 changeset 时,先从小的模块试运行。不必急于要求所有仓库一次性迁移。我在一个大型 monorepo 里推行时,把 Changesets 先应用在一个不承担核心发布责任的子包上,跑通流程、建立信任之后,再逐步扩大到核心包和全量包。流程变更最怕一步到位,小步快跑往往更容易落地。
第三,不要过度依赖自动化。changeset 虽然帮你记录变更、生成版本号,但"这个改动算不算 breaking change"这个问题的答案,永远需要人的判断。我在不少项目里见过因为"偷懒"把major一律写成patch的情况,这在维护公共库时是非常危险的习惯——用户会因为没有足够显眼的版本升级提示,错过重要的兼容性说明。
后续如果你在实践过程中遇到了我这里没提到的问题,可以顺着changeset status --verbose的提示信息、changelog 里的上下文,以及.changeset目录里的历史记录一点点去排查。版本管理这件事,工具能帮你承担的已经越来越多了,但最终的判断力始终在自己的团队手里。