Composer拉VCS子模块仍为旧commit,根本原因是其将Git克隆副本缓存在~/.composer/cache/vcs/且默认不校验远程HEAD变化;clear-cache虽可清vcs/,但常因用户权限、路径换行符或插件干扰而失效;应手动删除vcs目录并配合--prefer-source或--no-cache强制刷新。

为什么 composer update 拉的 VCS 子模块还是旧 commit?
Composer 对 VCS 类型包(如 "type": "vcs")的缓存逻辑和普通 dist 包不同:它把 Git 克隆副本存在 ~/.composer/cache/vcs/ 下,且默认不校验远程分支 HEAD 是否变化。哪怕你 git push 了新 commit,composer update 仍可能复用本地已有的克隆目录,导致 vendor 里代码没更新。
composer clear-cache 不清 vcs/ 目录?
它会清,但只在「显式触发」时生效:composer clear-cache 确实会删掉 vcs/ 子目录,但有三个常见漏点:
- CI/CD 中用不同用户执行命令(比如 root 跑
clear-cache,www-data 跑update),实际缓存路径权限不匹配,删了也白删 - PowerShell 下
$(composer config --global cache-dir)不自动 trim 换行符,导致路径拼错,rm 命令静默失败 - 某些 Composer 插件(如
hirak/prestissimo旧版)会绕过缓存清理钩子,vcs 目录残留
更稳妥的做法是手动删:rm -rf $(composer config --global cache-dir)/vcs/(Linux/macOS)或 rmdir /s /q "%APPDATA%\Composer\Cache\vcs\"(Windows CMD)。
怎么让 VCS 包每次拉最新 commit,又不全清缓存?
不需要禁用全部缓存,只需强制刷新 VCS 元数据和工作区:
- 加
--prefer-source:跳过 dist ZIP,直接 clone + checkout,天然绕过files/缓存干扰 - 加
--no-cache:虽然重,但它会强制重新 clone,确保 vcs 目录从头来过 - 在
composer.json的仓库配置里加"no-api": true,避免 Composer 尝试读取 Packagist API,直连 Git - 对私有 GitLab/GitHub,确认
repositoriesURL 是 SSH 形式(如git@gitlab.example.com:group/repo.git),比 HTTPS 更稳定,不易被中间缓存劫持
CI 构建中怎么安全复用 VCS 缓存?
VCS 缓存可复用,但必须满足两个前提:
- 缓存 key 必须包含 Git 仓库 URL 和分支名(比如 GitHub Actions 中用
${{ hashFiles('**/composer.json') }}-${{ matrix.php-version }}-vcs-${{ env.REPO_URL }}-${{ env.BRANCH }}) - 每次构建前运行
git -C $(composer config --global cache-dir)/vcs/https---github.com-user-repo.git fetch --all --prune同步远程引用,否则缓存里的 ref 可能过期
注意:vcs/ 缓存目录结构是 URL 转义后的,比如 https://github.com/user/repo.git → https---github.com-user-repo.git,手写路径时别漏掉 .git 后缀。


















