^1.2.3 等价于 >=1.2.3 =1.2.3 =0.2.3 =0.2.3 =0.2.5

^1.2.3 和 ~1.2.3 实际允许的版本范围完全不同
很多人看到两个符号都“允许升级”,就以为只是松紧程度不同,其实它们的锚点逻辑根本不一样。^1.2.3 等价于 >=1.2.3 ,意味着能升到 1.9.9;而 ~1.2.3 等价于 >=1.2.3 ,最多只到 1.2.99。关键不是“升多少”,而是“以哪一段为边界”:^ 锚定主版本(MAJOR),~ 锚定次版本(MINOR)。
0.x 版本下 ^ 和 ~ 行为一致但原因完全不同
^0.2.5 和 ~0.2.5 都等价于 >=0.2.5 ,看起来一样,但背后逻辑相反:^ 在 0.x 阶段主动降级为“只允许 patch 升级”,因为 SemVer 规定 0.x 是不稳定开发阶段;而 ~ 始终按“最左侧非零段”找锚点,0 是最左非零位,所以自然卡在 。这不是巧合,是两种策略在 0.x 下的收敛结果。
dev-main 这类分支别名不能当版本用
写 "monolog/monolog": "dev-main" 看似方便,但它不指向任何固定版本,每次 composer update 都可能拉下完全不同的代码,CI 构建不可重现,上线极易出问题。必须避免在 composer.json 的生产依赖中出现 dev- 前缀或 dev-master、dev-develop 这类写法。真要跟踪开发分支,应改用 "monolog/monolog": "dev-main as 2.99.99" 并配合 minimum-stability 调整,但依然不推荐。
选 ^ 还是 ~,取决于你对包维护节奏的信任度
^ 假设作者严格遵守 SemVer:minor 升级不破坏 API,适合 Laravel、Symfony 等主流包;~ 假设作者只在最后一段做安全修复,适合私有包或发布不规范的第三方包。误用 ~ 在大项目里可能导致长期卡在旧 minor 版本,错过关键 bug 修复;而滥用 ^ 在非 SemVer 包上,一次 minor 升级就可能引发行为变更。真正容易被忽略的是:0.x 包默认就不该用 ^ 放任 minor 升级——哪怕它看起来“更现代”。











