答案是分支版本冲突源于dev-*分支对同一依赖提出无交集约束,需用prohibits和--dry-run-v定位,通过commit hash锁定或repositories隔离解决。composer动态解析dev分支的composer.json,导致约束打架、ci与本地不一致,且why-not无法查未锁定分支。

分支版本冲突不是“版本号不同”导致的,而是 Composer 在解析依赖图时,发现不同分支(如 dev-develop、dev-main)对同一依赖提出了无法共存的约束 —— 它必须选一个版本,但两个分支各自要求的范围没有交集。
为什么 dev-* 分支会引发隐性冲突
Composer 不把 dev-* 当作稳定版本处理,它会实时拉取对应分支的 composer.json,而该文件里的 require 可能和你主项目或其他包的约束直接打架。常见表现:
- 本地
composer install成功,CI 上失败:因为 CI 拉的是远程最新dev-main,而你本地锁在某个 commit - 报错信息里出现
dev-develop as 2.3.0这类别名,但why-not查不到具体包 —— 它根本没进composer.lock,是动态解析时卡住的 - 同一个
dev-branch,PHP 8.1 下能装,PHP 8.3 下报ext-json冲突:分支代码里写了"ext-json": ">=1.7.0",但新 PHP 把扩展版本号逻辑改了
composer why-not 查不到 dev-* 包?用 prohibits 和 --dry-run -v
why-not 只对已安装或已声明的包有效;dev-* 分支未锁定前,Composer 还没决定用哪个 commit,所以得换方式定位:
- 先跑
composer prohibits vendor/package:dev-main,它会列出所有「明确拒绝这个分支」的包(比如某 SDK 的conflict字段写了"vendor/package": "dev-*") - 再执行
composer update --dry-run -v,重点看日志里反复出现的Trying行 —— 如果卡在vendor/package dev-main和monolog/monolog ^2.0之间来回回溯,说明就是它 - 临时加
"minimum-stability": "dev"到composer.json,再跑show -t | grep vendor/package,确认它是否被某个require-dev工具间接拉入
多环境分支依赖隔离:不靠删 dev,靠 repositories + path
开发环境要 dev-main,测试环境要 dev-stable,生产环境只认 tag —— 硬切分支容易误操作,推荐用 repositories 显式控制:
- 在
composer.json里为每个环境加独立repositories块(通过COMPOSER_REPOSITORIES环境变量注入,或用composer config repositories.xxx动态写入) - 用
"type": "path"指向本地克隆的仓库,并加"options": {"symlink": false}避免 git hook 干扰 CI 构建 - 关键点:在
require中写死 commit hash 而非dev-*,例如"vendor/package": "dev-main#abc1234"—— 这样composer.lock会记录精确位置,不同环境切换时不会重算依赖图 - 如果必须用
dev-*,就在config里加"preferred-install": {"vendor/package": "source"},强制走 git clone,避免 packagist 缓存污染版本判断
分支升级后 composer update 卡住?别全量重算,定点更新依赖树
当你把私有包从 dev-develop 切到 dev-main,composer update 可能卡在 Resolving dependencies 超过 5 分钟 —— SAT 求解器正在暴力尝试所有分支 commit 组合。
- 先运行
composer update vendor/package --with-dependencies --dry-run,确认它只动哪些子依赖(比如只升guzzlehttp/guzzle,不动phpunit/phpunit) - 如果干系包太多,就拆成两步:
composer update vendor/package --no-update先替换composer.json中的版本,再composer update vendor/package --with-dependencies - 严禁在生产部署脚本里用
--ignore-platform-reqs绕过分支兼容性检查 —— 它跳过的是 PHP 扩展版本验证,不是依赖逻辑冲突,上线后可能因ext-curl缺失直接崩在 autoload 阶段
分支版本冲突最难缠的地方在于:它不报错在明面,而是在 composer.lock 生成时悄悄引入一个和本地开发不一致的 commit hash;下次你 git pull 后没重跑 composer install,就可能跑着跑着发现某个接口返回空数组 —— 因为分支里刚合并了一个破坏性变更,但你的 lock 文件还锁着旧 commit。











