必须用 git filter-repo 而不是 git filter-branch,因其被官方弃用、慢且易错;filter-repo 速度快5–10倍、安全可靠,需python 3.8+,须用裸仓库副本并显式加--force。

直接用 git filter-repo 拆,别碰 git filter-branch —— 它已被 Git 官方弃用,运行慢、易出错、不支持大文件,且在 Git 2.38+ 中默认禁用。
为什么必须用 git filter-repo 而不是 git filter-branch
你执行 git filter-branch --prune-empty --index-filter 'git rm ...' 时,大概率会遇到:fatal: invalid object name 'HEAD' 或中途卡死几小时。这不是配置问题,是工具本身设计缺陷:它逐提交重写索引,对 10k+ 提交的仓库几乎不可用。
git filter-repo 是唯一能安全、线性处理大型历史的替代方案,它跳过 Git 内部对象缓存,直接操作 packfile,速度提升 5–10 倍,且默认保留作者/时间/签名等元数据。
- 必须用 Python 3.8+ 运行(
pip install git-filter-repo) - 不能在原仓库直接操作 —— 先
git clone --no-local --mirror得到裸仓库副本 -
--force参数必须显式加,否则遇到非空工作区会拒绝执行
git filter-repo --path 拆单个子目录的最小可行命令
假设你要从 MyHugeRepo 中拆出 services/auth 目录,生成一个只含该路径历史的新仓库:
git clone --no-local --mirror /path/to/MyHugeRepo auth-repo.git cd auth-repo.git git filter-repo --path services/auth/ --path-rename services/auth/: --force
注意两个细节:
监控一个或多个 GitCode 仓库的 PR,通过 OpenClaw Gateway 自动执行 AI 审查,发布 PR 评论,并发送钉钉和企业微信通知。
-
--path后面的斜杠不能省 ——services/auth和services/auth/匹配行为不同,后者才精确匹配目录 -
--path-rename services/auth/:的冒号后为空,表示把路径前缀完全抹掉,否则新仓库里文件还在services/auth/下 - 执行完后,
main分支名可能变成refs/heads/main,需手动git checkout -b main再推
同时拆多个子目录到不同仓库?别套用同一个 clone
常见错误:在一个 clone 出来的副本里反复跑 git filter-repo --path dir1/,再 git filter-repo --path dir2/ —— 第二遍会失败,因为第一次已删光所有非 dir1/ 的对象,dir2/ 根本不存在了。
正确做法是为每个子目录准备独立副本:
- 从原始仓库(非已过滤过的)重新
git clone --no-local --mirror一次 - 每个副本只跑一次
git filter-repo --path,目标唯一 - 拆
services/auth和services/payment需要两个隔离的.git目录,不能共享
脚本化建议:用循环 + 临时目录名(如 auth-split-$(date +%s)),避免手误覆盖。
拆完推送前,务必检查三件事
很多人推完才发现新仓库缺 tag、分支名不对、或测试用例全挂了。以下检查项不能跳:
- 运行
git log --oneline --graph --all—— 确认只剩目标路径的提交,且没有filter-repo自动生成的“synthetic”提交混入 - 执行
git tag和git branch -a——git filter-repo默认不处理 tag,需加--tag-rename ''或后续手动git push --tags - 进新仓库根目录,
ls看文件结构是否扁平(即没有残留的上级目录嵌套);若有,说明--path-rename写错了
最易忽略的一点:原仓库中通过 git submodule 引入的子项目,在拆分后不会自动变成独立仓库 —— 它们只是被当作普通文件留下,需要人工补初始化或替换为新的 submodule 地址。










