CHANGELOG.md仅为人服务,对composer install/update无任何影响;其唯一作用是帮使用者判断版本是否值得升级或bug是否已修复,必须与Git tag和composer.json version严格对齐,否则将导致信息误导。

CHANGELOG.md 不是给 Composer 看的,它对 composer install 或 composer update 完全没影响。它的唯一作用是:帮人快速判断「这个版本值不值得升」「我遇到的 bug 是否已修复」。
为什么改了代码但 composer update 后功能没生效?
常见错误现象是:你看到 CHANGELOG.md 里写了「修复了 User::save() 的空指针异常」,但升级后问题还在。结果花半天排查,最后发现 CHANGELOG 没和 tag 对齐——那条记录其实是写在 v3.2.1 下的,而你装的是 v3.2.0(因为 composer.json 写的是 "my/package": "^3.2",还没拉到新版本)。
根本原因:CHANGELOG.md 是纯人工维护文档,Composer 不读它、不校验它、不拿它做任何决策。你必须手动确认当前安装的版本号(composer show my/package)是否匹配 CHANGELOG 中对应标题段落的版本号。
- 用
git tag --sort=version:refname | tail -n 5快速查最近发布的 tag - 运行
composer show my/package看实际安装的 commit hash 和 version 字段 - 如果版本对不上,要么改
composer.json放宽约束,要么加--with=my/package:v3.2.1强制拉取
composer.lock 才是真·版本契约,CHANGELOG.md 只是辅助说明
composer.lock 记录每个包确切安装的 commit、dist URL、哈希值,CI/生产环境靠它保证一致性;而 CHANGELOG.md 只是给人看的摘要。一旦两者脱节(比如发版时漏打 tag 或忘了更新 CHANGELOG),协作效率会断崖式下跌。
- 每次
git push新 tag 前,必须确认CHANGELOG.md已新增对应版本段落,且所有条目都描述可观察行为(如「UserRepository::find()现在返回 null 而非抛出UserNotFoundException」) - 别写「优化查询性能」这种模糊项——没人知道到底改了哪条 SQL 或加了什么索引
- 如果项目用 GitHub Actions 自动发布,建议在 workflow 里加一步:用
conventional-changelog校验 CHANGELOG 是否包含未提交的变更
怎么让 CHANGELOG 和版本真正对齐?
靠人盯容易漏,得靠流程卡点。最轻量的方式是在 composer.json 的 scripts 里加一个 pre-release 钩子:
"scripts": {
"pre-release": "git diff --quiet CHANGELOG.md || (echo 'ERROR: CHANGELOG.md has uncommitted changes' >&2; exit 1)"
}这样每次运行 git tag v2.1.0 && git push origin v2.1.0 前,就会强制检查 CHANGELOG 是否已提交。再配合 CI 脚本验证 tag 名是否出现在 CHANGELOG 最顶部的 ## [v2.1.0] 行,基本能堵住大部分脱节漏洞。
- 别把 CHANGELOG 当成 release note —— 它要面向使用者,不是内部周报
- 如果包有 breaking change,必须在对应版本段落开头用
### Breaking Changes显式标出,并给出迁移路径 - 日期不是必须字段,但版本号和 Git tag 必须一字不差(包括
v前缀)
哪些场景下可以不写 CHANGELOG?
只有两种情况可以跳过:私有包(仅限内部项目,且团队明确约定不维护日志),或 纯工具类脚本包(无外部使用者,只供 CI pipeline 调用)。只要包被其他项目 require,就必须有 CHANGELOG——否则别人升级时只能翻 commit log,成本远高于你多写两行。
最容易被忽略的一点:CHANGELOG 不是补丁清单。它不记录「修复了 CI 构建失败」这类基础设施问题,只记录对使用者可见的行为变化。这点一旦混淆,日志就迅速失去可信度。


















