根本原因是composer默认递归解析整个依赖图;执行update时会重新计算所有约束,若目标包新版本要求更高子依赖,就会连带升级,--no-update-with-dependencies仅在现有子依赖满足约束时生效,否则报冲突。

Composer 自动更新无关包,根本原因不是“它想更新”,而是你没告诉它“只动这个”——默认行为就是递归求解整个依赖图。
为什么 composer update vendor/package 还是升级了其他包?
执行 composer update monolog/monolog 时,Composer 并不会只替换那个包的 dist 文件。它会重新运行依赖解析器,从 composer.json + 当前 vendor/ 状态出发,重新计算满足所有约束的最优版本组合。
- 如果目标包的新版本要求更高版本的子依赖(比如
monolog/monolog ^3.0需要psr/log ^3.0),而当前 lock 中仍是^2.0,Composer 就必须升级psr/log - 哪怕你没改
composer.json,只要composer.lock里记录的版本已不满足新约束(例如被手动删过 vendor 或 lock 被编辑),它也会“补全”整条链 -
--with-dependencies是默认开启的,不是可选项——除非你显式禁用
--no-update-with-dependencies 真的能锁死子依赖吗?
这个 flag 在 Composer 2.2+ 才可用,但它不是“强制不升级”,而是“仅当现有子依赖版本已满足新约束时才允许安装”。一旦不满足,直接报 conflict,不尝试降级或绕过。
围绕关键发现、作用机制、临床相关性及研究局限性展开讨论。适用于撰写或优化任何生物医学论文的“讨论(Discussion)”部分——包括结果解读、与既往文献关联、阐释意外发现、界定研究局限性,以及撰写结论。当用户输入以下任一指令时也会自动触发该功能: - “write my discussion” - “help me discuss my findings” - “how do I compare to prior studies” - “write the limitations par
- 它不修改
composer.json,也不重写composer.lock的子依赖字段 - 适合你明确知道目标包和当前子依赖兼容(比如你刚跑过
composer update --dry-run确认只有单行Updating monolog/monolog) - 若失败,错误信息通常是
Conclusion: don't install xxx,而不是模糊的“conflict”——这是关键信号
为什么 composer update --lock-only 不会触发无关更新?
因为它跳过了依赖解析环节:不读包元数据、不查 Packagist、不校验版本约束,只对当前 vendor/ 目录做快照,把已安装包的 dist.sha256、源地址、嵌套结构原样写回 composer.lock。
- 前提是
vendor/必须完整且可解析;缺包或损坏会导致命令失败,不是静默跳过 - 它不响应
composer.json里的新增包声明,也不会修正content-hash不匹配警告 - CI 流水线中常配合
--dry-run预检:先composer update --lock-only --dry-run,失败则说明本地环境和 lock 已不一致,必须让开发者先修复
真正可控的单点更新,得靠组合动作
想只升 amasty/module-fpc-warmer 而不动 bsscommerce/customer-approval,不能只靠一条命令。你得先确认它的直接依赖是否已在 lock 中满足——否则 Composer 一定会连带升级。
- 先运行
composer depends amasty/module-fpc-warmer查清它依赖哪些包及其当前版本 - 再用
composer show amasty/module-fpc-warmer:2.4.6看该版本 require 的具体约束(比如amasty/base >=1.13.4) - 如果
amasty/base当前是1.12.15,就必须先决定:是接受升级它(并承担连锁影响),还是用replace声明已提供兼容实现 - 最后执行
composer update amasty/module-fpc-warmer --no-update-with-dependencies,失败就说明约束冲突不可避
最易被忽略的一点:Composer 的“无关包”其实是你的依赖图里真实存在的节点,它没有“无关”概念——只有你没意识到它们被连带约束了。










