最直接有效的方式是在 composer.json 中配置 "archive": {"exclude": ["/tests/data", "/fixtures", "/examples/**", "phpunit.xml"]},该规则专为 Composer 打包设计,不影响本地安装,且需手动验证压缩包内容。

composer.json 里用 archive 配置排除测试数据
Composer 默认打包(composer archive 或 Packagist 自动抓取)会包含整个 Git 仓库的文件,但你只想发布源码,不带 tests/data/、fixtures/ 这类测试专用文件——最直接有效的方式是在 composer.json 中声明 archive 规则。
它不是全局忽略(不像 .gitattributes),而是专为 Composer 打包行为设计的白名单/黑名单机制,且只影响 composer archive 输出和 Packagist 下载的 zip/tar 包,对本地 composer install 完全无影响。
实操建议:
- 在
composer.json的根级添加"archive": {"exclude": ["/tests/data", "/fixtures", "/examples/**", "phpunit.xml"]} - 路径支持 glob(如
**/*.md),但注意:开头加/表示从包根开始匹配,不加则模糊匹配所有子目录 - 排除项不递归生效于已匹配目录下的
.gitignore—— 它只管自己这一层规则 - 运行
composer archive --format=zip后手动解压验证,比依赖 Packagist 首次同步更可靠
.gitattributes 的 export-ignore 确实可用,但有隐藏限制
Git 原生的 export-ignore 是通过 git archive 命令起作用的,而 Composer 在某些场景下(比如 Packagist 抓取)底层确实调用 git archive,所以它「有时」能生效。但问题在于:它不保证被所有 Composer 发布流程识别。
常见失效场景:
- Packagist 使用 GitHub API 直接下载 release tarball(绕过
git archive),此时.gitattributes完全不生效 - 本地执行
composer archive时,默认不走git archive,而是基于文件系统扫描,无视.gitattributes - Windows 下 Git for Windows 对
export-ignore的路径大小写处理不稳定
所以,除非你完全控制发布链路(例如只用 git archive | gzip 手动上传),否则别单独依赖 .gitattributes。
为什么不用 .gitignore?它根本不起作用
.gitignore 只控制「哪些文件不进 Git 仓库」,跟 Composer 打包毫无关系。哪怕你把 tests/data/ 加进 .gitignore,只要它已经提交过历史,就依然会被打包进去;如果没提交,那连本地开发都拿不到测试数据,违背了「附带但不发布」的初衷。
换句话说:.gitignore 解决的是「不跟踪」,而你要的是「跟踪但不出现在发行包里」——这是两个正交问题。
验证是否生效:别信文档,要看实际压缩包内容
最可靠的验证方式永远是生成真实包并检查结构:
- 执行
composer archive --format=zip --file=test-pkg.zip - 用
unzip -l test-pkg.zip | grep -E "(data|fixture|example)"快速确认敏感目录是否消失 - 对比
git ls-tree -r --name-only HEAD | grep -E "(data|fixture)",确保这些路径确实在 Git 中存在(否则测试根本跑不起来) - 如果用 GitHub Actions 自动发布,记得在 workflow 中加一步解压校验,而不是仅靠 CI 通过就认为 OK
真正容易被忽略的是:Packagist 的缓存可能延迟刷新,即使你改了 composer.json 的 archive 配置,旧版本包仍可能被用户下载到。遇到这种情况,只能发新版本号(哪怕只是 patch)来强制触发重打包。











