在项目迭代过程中,随着代码量与模块复杂度不断攀升,将一个大型 Git 工程拆分为多个独立工程,能让项目结构更清晰,便于团队协作与维护。关键是要保留完整的提交历史,这就需要借助 Git 的 filter-branch 工具来实现。
一、准备工作
使用 filter-branch
git filter-branch 是 Git 内置的强大工具,无需额外安装,可以直接使用。虽然相比 filter-repo 效率稍低,但功能完整且稳定可靠。
验证可用性:
git filter-branch --help
备份原仓库
拆分前务必备份原仓库,防止操作失误导致数据丢失:
git clone --mirror 原仓库地址 备份仓库地址
二、拆分示例
假设原仓库有两个独立模块,需拆分为:
- 工程 A:只保留
module-a/目录及相关历史 - 工程 B:只保留
module-b/目录及相关历史
三、具体步骤
拆分工程 A
1. 克隆原仓库
git clone 原仓库地址 工程A
cd 工程A
2. 使用 filter-branch 保留指定目录
git filter-branch --prune-empty --subdirectory-filter module-a master
参数说明:
--subdirectory-filter module-a:将module-a目录作为新仓库的根目录--prune-empty:删除空提交master:指定要重写历史的分支
3. 清理无关分支(可选)
# 查看所有分支
git branch -a
# 删除不需要的分支
git branch -D 无关分支名
git branch -dr origin/无关分支名
4. 提交到新仓库
# 关联新仓库
git remote add origin 工程A的新仓库地址
# 推送所有分支和标签
git push -u origin --all
git push --tags
拆分工程 B
按照相同步骤操作:
# 重新克隆原仓库
git clone 原仓库地址 工程B
cd 工程B
# 只保留 module-b 目录
git filter-branch --prune-empty --subdirectory-filter module-b master
# 关联工程 B 的远程仓库并推送
git remote add origin 工程B的新仓库地址
git push -u origin --all
git push --tags
四、重要说明
历史完整性
filter-branch 会重写提交历史,只保留与指定目录相关的提交(包括修改、删除、重命名等操作),其他提交会被过滤,最终历史记录"干净"且相关。
处理大型仓库
若仓库体积大(含大量大文件),可先清理超大文件:
# 使用 filter-branch 清理大文件
git filter-branch --tree-filter 'rm -f large_file.zip' HEAD
高级用法
filter-branch 提供了多种过滤方式:
按文件大小过滤:
git filter-branch --tree-filter 'find . -size +10M -delete' HEAD
按文件类型过滤:
git filter-branch --tree-filter 'rm -rf *.log' HEAD
保留多个目录:
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch module-c module-d' HEAD
五、高级用法
复杂目录结构拆分
如果需要拆分更复杂的目录结构,可使用以下方法:
使用 tree-filter 进行复杂操作:
# 只保留特定文件类型
git filter-branch --tree-filter 'find . -name "*.js" -o -name "*.ts" | xargs rm -f' HEAD
# 排除特定目录
git filter-branch --tree-filter 'rm -rf temp/ build/ node_modules/' HEAD
# 重命名目录结构
git filter-branch --tree-filter 'mkdir -p new_structure && mv module-a/* new_structure/' HEAD
使用 index-filter 提高性能:
# 只保留指定目录(性能更好)
git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch module-b module-c' HEAD
通过以上步骤,两个新工程会分别保留原仓库中与自身模块相关的完整提交历史,且相互独立。这样既保持了代码的连续性,又实现了模块的独立管理。
