branch-alias 是包作者在自身 composer.json 的 extra 字段中为 dev 分支定义的版本别名,如 "dev-main": "2.0.x-dev",使他人可用 "^2.0@dev" 安全依赖未发版代码,不改包名、不作用于 require 方,且必须带 x-dev 后缀。

branch-alias 是给 dev 分支“贴版本标签”,不是给包起新名字
Composer 的 branch-alias 只作用于你自己的包(即该包的 composer.json 里定义的),目的是让其他项目能用语义化版本号(比如 ^2.0)去依赖你的开发分支。它不改变包名,也不影响 require 中写什么——它解决的是“别人怎么安全地依赖我还没发版的代码”这个问题。
常见错误现象:在项目 A 的 composer.json 里给 monolog/monolog 加 branch-alias,结果没用。因为这个字段只在 monolog 自己的 composer.json 里才被读取。
-
branch-alias必须写在被依赖方(即你要发布的那个包)的composer.json的extra字段下 - 典型写法:
"branch-alias": { "dev-main": "2.0.x-dev" },表示把main分支当成2.0.x-dev这个开发版本号来解析 - 依赖方就可以写
"your-vendor/your-package": "^2.0@dev",而不用硬写dev-main - 注意
x-dev后缀不能省——这是 Composer 识别“这是一个开发版别名”的关键标记
require 里写 dev-main as 2.0.0 和 branch-alias 完全是两回事
前者是「你在项目里临时替换一个包」,后者是「你作为包作者为别人提供稳定依赖入口」。混淆这两者是踩坑最频繁的地方。
使用场景差异明显:
- 你 fork 了
symfony/console做定制,想让当前项目继续用^5.4约束但实际加载你的 fork → 用repositories + require: "symfony/console": "dev-main as 5.4.0" - 你维护自己的组件
acme/logging,希望下游项目能通过"acme/logging": "^3.0@dev"直接拉main分支 → 在acme/logging的composer.json里配"branch-alias": { "dev-main": "3.0.x-dev" } - 两者不能混用:你不能在自己项目的
composer.json里给别人的包加branch-alias,也不能在别人包的composer.json里用as语法
为什么 ^2.0@dev 有时不生效?稳定性设置是隐形开关
即使你正确配置了 branch-alias,下游项目执行 composer require your-vendor/your-package:^2.0@dev 仍可能失败,大概率卡在 minimum-stability 上。
根本原因:Composer 默认只接受 stable 版本,x-dev 属于 dev 稳定性级别,必须显式允许。
- 项目级允许:在项目
composer.json的根级加"minimum-stability": "dev"(影响全部依赖) - 包级允许:在 require 行末尾加
@dev后缀,如"your-vendor/your-package": "^2.0@dev"(推荐,更精准) - 注意
prefer-stable: true会压制@dev效果,二者共存时需确认优先级逻辑 - 运行后检查
composer show your-vendor/your-package,确认显示的版本是2.0.x-dev而非dev-main,才算 alias 生效
Monorepo 场景下 branch-alias 很容易被误用
在 Monorepo(比如用 Lerna 或自建脚本管理多个子包)中,有人试图靠 branch-alias 让不同子包共享同一套分支策略,结果发现依赖解析混乱——因为每个子包都是独立包,各自要维护自己的 branch-alias,且 CI 发布流程必须和分支命名严格对齐。
真实约束比想象中硬:
-
acme/logging的main分支配了"dev-main": "1.0.x-dev",但acme/http-client的main配的是"dev-main": "2.0.x-dev",它们之间无法靠 alias 自动对齐版本语义 - 如果下游项目同时 require 这两个包,并都用
@dev,Composer 会尝试找一个满足^1.0@dev和^2.0@dev的共同解——这几乎不可能,最终报冲突 - 正确做法是:Monorepo 内部子包之间用
path仓库类型本地链接;对外发布才走branch-alias+ tag + 私有 Packagist - 别指望
branch-alias能跨包协调版本节奏,它只是单包的“版本投影”机制
真正容易被忽略的是:branch-alias 不参与依赖求解的 SAT 规则生成,它只在版本字符串解析阶段起作用。一旦进入 solver 流程,Composer 看到的就是 2.0.x-dev 这个字符串,而不是背后的分支名——所以 alias 写错、拼写不一致、或漏掉 x-dev,都会导致下游根本解析不到这个版本。











