答案是 composer require 不支持 dev-branch#hash 语法,必须手动编辑 composer.json 写入 "vendor/package": "dev-main#abc1234" 并运行 composer update vendor/package;或配置 repositories 后使用 dev-abc1234 格式。

composer require 不能直接写 dev-branch#hash
执行 composer require vendor/package:dev-main#abc1234 会报错 Invalid version string —— 因为 require 命令底层调用的是版本解析器,而它不支持在命令行中识别 # 后缀。这不是 bug,是设计限制。
真正生效的方式只有两种:
- 手动编辑
composer.json的require字段,写入"vendor/package": "dev-main#abc1234" - 或使用
composer require vendor/package:dev-abc1234(前提是已配置repositories且该 hash 真实存在)
注意:后者中的 dev-abc1234 是“带 dev- 前缀的裸哈希”,不是分支名加 #,两者语法完全不同,适用场景也不同。
dev-branch#hash 和 dev-hash 两种写法的区别
dev-main#abc1234 要求远程仓库存在 main 分支,且目标 commit 必须在该分支历史中(哪怕只是临时分支);dev-abc1234 则不要求分支存在,只要该 commit 在仓库任意可访问分支/引用中即可——但必须配合 repositories 显式声明源。
常见误用:
-
"vendor/package": "dev-abc1234"却没配repositories→ Composer 查不到包,报Could not find package -
"vendor/package": "dev-main#abc1234"但远程main已被 force-push 覆盖 → 安装失败,报Failed to execute git checkout - 哈希用了大写或不足 7 位 → 某些镜像服务解析失败或匹配冲突
安装后怎么确认真锁住了?光看 composer.lock 不够
composer.lock 里的 source.reference 字段只记录当时解析结果,不防远程篡改。真正验证必须落到本地 Git 工作区:
- 进入包目录:
cd vendor/vendor/package - 执行:
git rev-parse HEAD,输出必须和composer.lock中对应项完全一致 - CI 中建议加校验脚本:
composer show -s vendor/package | grep -q "abc1234567890$"
如果 git rev-parse 报错,说明 vendor 下不是真实 Git 工作区(比如启用了 preferred-install: dist 或私有源没配凭证),得先确保拉的是 source 而非 zip 包。
更新时最容易漂移的两个点
你以为锁死了,但 composer update 还是拉了新 commit,大概率栽在这两个地方:
- 执行了全量
composer update,而不是限定包名:composer update vendor/package—— 后者才强制重新解析该包的版本约束 - 该包有子依赖也用了
dev-xxx写法,且没加#hash,--with-dependencies会连带升级它们
上线前务必跑一次 composer update --dry-run,重点检查目标包和它的间接依赖的 source.reference 是否变动。一旦发现漂移,立刻回退并检查 composer.json 格式、分支状态和远程仓库权限。











