composer不支持自动合并composer.json,因其仅读取单个文件并基于内存依赖图重写全量内容,不保留scripts/config/extra等手动字段;require、scripts、extra需人工核对冲突,lock文件必须删除后通过composer install --no-scripts和composer update --lock重建。

两个项目的 composer.json 不能直接“合并”——Composer 不解析、不比较、不融合多个 composer.json 文件,它只读取当前目录下的那一个。
为什么 composer.json 没有自动合并功能
Composer 在运行 require、update 或写入时,是基于内存中构建的完整依赖图重写整个 composer.json。它不保留你手写的 scripts、config、extra 字段的原始结构,也不会尝试“合并”两个不同来源的 JSON 内容。
常见错误现象:
- 把项目 A 的
extra.deploy和项目 B 的extra.build复制进同一个文件,结果运行composer update后其中一个被清空 - 用 git merge 自动合并
composer.json,但没注意require-dev里某包版本冲突,导致后续install失败
根本原因:Composer 把 composer.json 当作“声明式配置”,不是“增量式补丁”。它只关心最终状态是否可解,不关心你怎么填进去的。
手动合并时必须检查的三个字段
真正需要人工核对、不能靠工具糊弄的,就这三个位置:
-
require和require-dev:逐个比对包名,看是否有同名但版本范围互斥(比如一个要"laravel/framework": "^10.0",另一个写"laravel/framework": "^11.0");若有,必须选一个或找兼容中间版 -
scripts:字段本身可共存,但同名 script(如"post-install-cmd")会被后定义的覆盖;建议统一改用数组写法:"post-install-cmd": ["@php artisan optimize:clear", "npm run build"] -
extra:完全自由格式,无规范约束;合并时需人工判断语义是否冲突(例如两个项目都定义了"laravel-ide-helper": {"include": [...]},就得手动去重或分条件加载)
别忽略 minimum-stability 和 prefer-stable:如果一个项目设 "minimum-stability": "dev",另一个是默认 "stable",合并后不显式统一,可能让某些 dev-main 包意外装上。
composer.lock 怎么处理才不翻车
composer.lock 是单项目快照,绝不能拼接、不能 diff 合并。两个项目的 lock 文件混在一起,等于把 SAT 求解器的输入喂成一团乱麻。
正确做法只有这一种:
- 删掉现有
composer.lock和vendor/ - 确保合并后的
composer.json已通过人工校验(尤其require无硬冲突) - 运行
composer install --no-scripts先生成干净 lock - 再跑
composer update --lock强制重算依赖关系,但不改composer.json
如果中途卡住或报错,说明 composer.json 里还有没发现的约束冲突——这时回退,别硬拖。Lock 文件不是“能跑就行”,它是可复现部署的唯一依据。
想共享配置?别合并,换思路
频繁合并 composer.json,往往暴露的是架构问题:本该抽离的公共逻辑,被复制粘贴在多个项目里。
更可持续的做法:
- 把共用的
scripts、extra提炼成私有包,通过repositories加载,然后require它 - 用 Makefile 或
just封装跨项目命令,绕过composer.json的耦合 - 禁用已废弃的
merge-plugin:它早就不维护了,且会干扰现代 Composer 的依赖解析流程
真正麻烦的从来不是怎么合并,而是合并之后没人再敢动这个文件——因为谁也不知道哪行是项目 A 的,哪行是项目 B 的。留痕、拆分、收敛,比“合并”重要得多。











