根本原因是用了--prefer-source安装或全局配置preferred-install为source,导致Composer对包执行git clone并残留不完整.git目录;应改用--prefer-dist、配置preferred-install:"dist"或手动清理子目录.git。

为什么 vendor 里会有 .git 文件夹?
根本原因是用了 --prefer-source 安装(或全局配置了 preferred-install 为 source),让 Composer 对每个包执行 git clone。一旦 clone 中断(网络抖动、磁盘满、权限不足),就可能留下不完整 .git 目录——有文件夹,但缺 HEAD 或 objects,后续所有 composer update 都会卡在这儿。
另外,某些 dist 包打包不规范,zip 里也混进了 .git,这时 --remove-vcs 才能兜底。
安全删除 vendor/.git 的三种方式
别直接 rm -rf vendor/.git——那只是删顶层(一般不存在),目标是清理 vendor/{package-name}/.git 这类子目录。
- 最稳妥:逐个进包目录,确认没做本地修改后,手动运行
rm -rf .git - 批量清理(慎用):
find vendor -name ".git" -type d -exec rm -rf {} +——前提是你从没在vendor/里 commit 或切分支 - 一劳永逸:删完后统一改用
composer install --prefer-dist(默认行为),它只解压 zip,不生成.git;避免再用--prefer-source,除非真要调试源码
如何永久禁用 vendor 里的 .git?
临时加参数最简单:composer install --prefer-dist 或 composer update --prefer-dist。
想长期生效,有两种配置方式:
- 仅当前项目:
composer config preferred-install "dist"(写入本地composer.json的config字段) - 全局生效:
composer config --global preferred-install "dist"(影响所有项目)
注意:path 类型仓库不受该设置控制——只要源路径含 .git,就会被原样复制进来。这种场景得靠 CI/CD 阶段用 rsync --exclude=.git 同步。
删完 .git 还报错?检查这些地方
删了 .git 不代表问题消失,常见残留坑点:
-
composer.lock里仍记录着 commit hash(不是 tag),如果你曾手动进vendor改过代码又没提交,下次install会强制git reset --hard,覆盖你的修改 - 某些包的
autoload映射里写了tests/下的路径(如旧版monolog/monolog),删了.git没用,还得配合archive.excludes或--classmap-authoritative彻底隔离 - OPcache 或 APCu 缓存了旧的 autoload 映射,需手动调用
opcache_reset()或apcu_clear_cache()
真正安全的清理,从来不是“删完就完”,而是重建 + 映射固化 + 缓存刷新三者闭环。


















