确认Composer复用VCS缓存的最可靠方式是加-vvv运行composer update并观察日志:若出现“Reading /path/to/cache/vcs/.../refs/heads/main”则缓存生效;若显示“Cloning”或“git fetch --all”,说明未命中。

Composer 的 VCS 缓存是否生效,不能靠 composer clear-cache 或目录是否存在来判断,而要看它是否跳过 Git clone、复用已有的 .git 目录,并在 update 时避免重复 fetch —— 最可靠的方式是加 -vvv 观察日志行为。
怎么确认 Composer 正在复用 VCS 缓存?
VCS 缓存(Git/Hg/Svn)存在 ~/.composer/cache/vcs/ 下,但“存在”不等于“生效”。真正生效的表现是:Composer 不重新克隆仓库,也不执行完整 git fetch --all,而是直接读取本地 .git 并解析 ref。
- 运行
composer update vendor/package -vvv 2>&1 | grep -E "(Cloning|Fetching|Reading)" - 若看到
Reading /path/to/cache/vcs/https---github-com-vendor-package.git/refs/heads/main,说明缓存被读取 - 若出现
Cloning to /path/to/cache/vcs/https---github-com-vendor-package.git或Executing command (CWD): git fetch --all --prune,说明缓存未命中或已失效 - 注意:
Reading后跟的是refs/子目录下的文件(如heads/main),不是整个.git目录路径;后者是旧版行为,新版只读元数据快照
为什么删了 ~/.composer/cache/vcs/ 还是不重新 clone?
因为 Composer 的 VCS 缓存有两层:一层是 cache/vcs/ 下的 Git 元数据快照,另一层是 vendor/ 下的 symlink 或 dist 包引用。即使清空前者,如果 vendor/vendor/package 已存在且满足版本约束,Composer 就不会触发任何 VCS 操作。
- 强制触发 VCS 解析:先删掉
vendor/vendor/package,再跑composer update vendor/package --no-cache -
--no-cache会跳过所有缓存(包括 vcs 和 files),但不会跳过 vendor 中已安装的包——所以必须先删 vendor 子目录 - 若仍不 clone,检查
composer.json中该包是否被声明为"dist"类型(如指定了url和shasum),此时 Composer 会绕过 VCS 直接下 ZIP - 确认包来源:运行
composer show vendor/package,看source行是否为git https://github.com/vendor/package.git;若是dist,VCS 缓存压根不参与
哪些配置会让 VCS 缓存静默失效?
VCS 缓存依赖于 URL 归一化和 Git 配置一致性。稍有偏差,Composer 就会当作新仓库处理,重建缓存目录。
- 同一仓库用不同协议访问:
https://github.com/a/b.git和git@github.com:a/b.git被视为两个独立仓库,缓存不共享 - URL 末尾带或不带
.git:虽然 Git 客户端能自动补全,但 Composer 缓存 key 是精确匹配的,github.com/a/b和github.com/a/b.git是两个 key - 全局
git config --global url."https://".insteadOf git://类重写规则,可能让实际克隆地址与缓存 key 不一致 - 项目级
repositories中 type 写成vcs而非git(或反之),会导致 Composer 放弃复用已有缓存,新建目录
CI/CD 环境中 VCS 缓存为何经常不生效?
CI 流水线通常使用干净容器或临时工作区,~/.composer/cache/vcs/ 默认为空。但即使你挂载了缓存卷,也容易踩坑:
- 缓存卷权限错误:Linux 下
~/.composer/cache/vcs/目录需属主可写,否则 Composer 静默 fallback 到/tmp,且不报错 - 多 job 并行写入同一缓存路径:多个
composer update同时操作一个vcs/目录,可能损坏 refs 快照,导致后续请求 fallback 到完整 clone - Git shallow clone 导致 ref 不全:CI 默认拉取 shallow repo(
--depth=1),但 Composer 需要完整refs/heads/*和refs/tags/*来解析版本,缺失则强制重新 fetch - 解决方案:在 CI 中显式启用完整 clone(如 GitHub Actions 加
fetch-depth: 0),并确保缓存路径挂载后执行chown -R $(whoami) ~/.composer/cache/vcs
真正难的不是“有没有缓存”,而是“Composer 在什么条件下决定不用它”。VCS 缓存行为高度依赖 URL 标准化、Git 配置、以及当前 vendor 状态——哪怕只改了一个斜杠或协议头,都可能让缓存形同虚设。











