唯一可靠锁定包版本的方式是将 composer.json 中该包版本写为不含运算符的精确值,如 "monolog/monolog": "2.9.1";修改后须执行 composer update monolog/monolog 以更新 composer.lock,否则锁定不生效。

Composer 本身没有“禁止自动更新”开关,所谓“自动更新”其实是你或别人误执行了 composer update,或者用了带 ^ 或 ~ 的版本约束——它从不偷偷升级,只是按你写的规则做事。
怎么让某个包死死锁住,连 composer update vendor/package 都不生效
唯一可靠的方式,是在 composer.json 里把版本写成不含任何运算符的精确值。
- ✅ 正确写法:
"monolog/monolog": "2.9.1"—— 运行composer update monolog/monolog会输出Nothing to install or update - ❌ 错误写法:
"monolog/monolog": "^2.9"或"~2.9.0"—— 即使你没动它,别人执行全量update就可能升到2.10.0 - ⚠️ 注意:改完后必须运行一次
composer update monolog/monolog,否则composer.lock里可能还存着旧的解析结果,导致锁定不生效
为什么改了 composer.json 还是被升级了?常见掉坑点
表面看是“自动更新”,实际多是环境或操作链路出了问题。
-
composer.lock被删了或没提交到 Git ——composer install会重新生成 lock,而新生成的版本取决于当前composer.json中的约束(如果还是^2.9,就又松了) - CI/CD 脚本里写了
composer update或composer require—— 这类命令天然重写 lock 文件,哪怕只加一个 dev 包 - 本地执行过
composer update --with-all-dependencies—— 它会无视你“只想更新 A”的意图,把所有间接依赖也拉一遍,包括你本想锁死的 B - 用了
composer install --no-lock或没加--locked—— 前者强制忽略 lock,后者才是真·只认 lock 不许改
不想改 composer.json,但想临时跳过某包更新
没有 --exclude vendor/package 这种原生命令,但有可落地的绕行方案:
- 用白名单代替黑名单:
composer update "laravel/framework" "illuminate/support"—— 只列你想更新的包,漏掉的那个自然不动 - 先预览再动手:
composer update --dry-run monolog/monolog看是否真会变;composer show monolog/monolog确认当前装的是不是你要的版本 - CI 中加防护:
git status --porcelain composer.lock检查 lock 是否被修改,非零退出就中断构建 —— 这比靠人盯更靠谱
真正难的不是写对那一行 "package": "x.y.z",而是让整个协作流(本地开发、PR、CI、部署)都默认信任 lock 文件、避开 update 命令。一旦有人在 CI 里加了一行 composer update,所有手动锁定都会瞬间失效。










