因为真正决定“该拉哪些版本”的是 repo/ 下的元数据缓存(如 packages.json、provider-*.json),而非 files/ 中的 ZIP 包;删 files/ 后 Composer 仍读取旧元数据,误判无更新,导致“秒完成”实为假快。

composer update 为什么删了 files 还是慢?
因为 files/ 只存下载好的 ZIP/TAR 包,而真正决定“该拉哪些版本”的是 repo/ 下的元数据缓存(比如 packages.json、provider-2024-07.json)。删了 files/,Composer 仍会从本地 repo/ 读取旧的包列表,然后发现版本没变,就跳过更新——你看到的“秒完成”,其实是假快。
典型现象:composer clear-cache 后立刻 composer update,日志里仍有 Reading /path/to/cache/repo/https---packagist.org/packages.json,说明元数据根本没动。
-
files/:按vendor/name/hash/存压缩包,只在--prefer-dist模式下被读取 -
repo/:存packages.json、provider-*.json、packages-dev.json等,控制版本发现逻辑 -
vcs/:存 Git 克隆副本(--prefer-source时用),和元数据无关,但影响源码拉取速度
为什么 vcs/ 缓存不清理也常出问题?
vcs/ 缓存不是“元数据”,而是真实 Git 工作区快照。它不会自动 git fetch --prune,旧分支、过期 tag、甚至本地 commit 都可能滞留其中。当你 require 一个新 tag(如 v2.1.0),Composer 会先查 vcs/ 目录下的 Git 仓库有没有这个 tag;如果没有,才触发 git fetch——但若远程已删掉该 tag,本地又没同步,就会报 Could not find branch or tag。
常见诱因:
- 私有 Git 仓库频繁 force-push 或删除历史 tag
- CI 环境复用同一
vcs/目录但未定期git clean -fdx && git fetch --prune -
vcs/目录权限错乱(如 root 写入后普通用户无法覆盖)导致 fetch 失败却静默 fallback
如何验证当前生效的缓存路径是否包含这三类目录?
别依赖记忆或文档,直接问 Composer:
运行 composer config --global cache-dir,拿到输出路径后,立刻检查它下面是否存在 files/、repo/、vcs/ 三个子目录。缺任何一个,说明:
- 该目录从未被写入(可能是权限不足、
COMPOSER_CACHE_DIR被覆盖为无效路径) - 被手动删过但未触发重建(
repo/和vcs/是懒创建的,首次update才生成) - 配置被
COMPOSER_HOME偏移(比如设了COMPOSER_HOME=/tmp/composer,那缓存就在/tmp/composer/cache/)
验证命令示例:ls -d $(composer config --global cache-dir)/{files,repo,vcs} 2>/dev/null || echo "missing one or more"
部署脚本里必须区分清理 repo/ 和 vcs/ 的时机
repo/ 清理要激进(删整个 https---packagist.org/ 子目录),因为元数据一旦过期,update 就不会发现新版本;而 vcs/ 清理可以更克制——除非确认远程 Git 有重大变更(如重置 main 分支),否则只需 git -C $cache_vcs_path fetch --prune 即可。
错误做法:
- 只跑
composer clear-cache—— 它默认不碰repo/和vcs/ - 用
rm -rf ~/.composer/cache/*—— 可能误删config.json或其他用户配置 - 在 CI 中把
vcs/加入缓存键(如key: vcs-${{ hashFiles('**/.git/config') }})—— Git 配置变化太频繁,缓存几乎不命中
真正关键的是:元数据缓存决定“能不能看到新包”,VCS 缓存决定“能不能拉到那个包”。两者失效的表现不同,修复方式不能混用。











