composer不支持git subtree,其依赖管理完全基于composer.json位置,无作用域概念;子项目需通过--no-autoloader/--no-scripts、独立vendor-dir、显式psr-4 autoload、子目录专属.gitignore及ci中cd后安装来实现物理与加载隔离。

不支持,Composer 本身完全不感知 Git subtree —— 它只认 composer.json 文件和依赖声明,对 Git 目录结构、历史重写、子树合并等零认知。 所有“subtree + Composer”协作问题,本质是 Git 操作与 Composer 执行环境的边界冲突,必须手动隔离。
为什么 composer install 在 subtree 目录里会污染主项目 vendor
Composer 没有作用域概念,它从当前目录向上查找最近的 composer.json(通常是主项目根目录),然后把所有依赖装进那个 vendor/。哪怕你在 packages/http-client/ 下执行,只要该目录没独立 composer.json 或没切断查找链,就会往主项目的 vendor/ 写。
- 子目录下直接运行
composer install是危险操作,99% 场景会导致类加载失败或版本错乱 - 即使子目录有自己
composer.json,也必须显式禁用 autoloader 和 scripts:composer install --no-autoloader --no-scripts - 主项目
autoload或autoload-dev中若包含子目录路径(如"packages/http-client/src"),composer dump-autoload会把它扫进主 autoloader,引发命名空间泄漏
如何让 subtree 子包拥有独立 vendor 和 autoload
关键不是“让 Composer 支持 subtree”,而是用配置和流程强行制造隔离:物理路径隔离 + 加载逻辑隔离 + Git 忽略策略。
- 在子包根目录(如
packages/http-client)的composer.json中强制指定"vendor-dir": "lib/http-client/vendor",路径必须唯一且不与主项目或其他子包重叠 -
autoload必须用psr-4显式绑定,例如"Acme\HttpClient\": "src/",不能留空或依赖自动发现 - 在子包目录下放一个
.gitignore,内容仅一行:/vendor(开头的/确保只忽略本级vendor),并执行git add -f packages/http-client/.gitignore - CI 中执行
git clean -xffd packages/http-client/vendor清残留,再cd packages/http-client && composer install --no-autoloader --no-scripts
git subtree push 时 vendor 被误提交怎么办
git subtree push 默认递归提交整个子目录内容,如果 packages/http-client/vendor 没被正确忽略,就会连同代码一起推到上游仓库 —— 这违反了 vendor 不进 Git 的铁律,且让历史体积失控。
- 主项目根目录的
.gitignore里写packages/*/vendor无效:subtree 子目录下的.gitignore才生效 -
git clean -xffd必须在git subtree push前执行,尤其 CI 环境容易残留旧vendor - 推送前检查:
git status --ignored packages/http-client,确认vendor/显示为 ignored 状态 - 一旦误推,只能用
git filter-repo彻底清理历史(不可逆),所以预防比修复重要得多
最易被忽略的点是 autoload 链的隐式耦合:主项目 dump-autoload 会扫描所有已声明路径,哪怕子包自己有独立 autoload.php,只要主项目映射了它的源码路径,就可能覆盖或干扰子包自己的加载逻辑。隔离不是靠一次配置完成的,而是每条路径、每个命令、每次 CI 构建都要验证是否真正断开了连接。











