将子目录分离(移动)到单独的Git仓库
将子目录分离(移动)到单独的Git仓库
技术背景
在软件开发过程中,随着项目的不断发展,可能会发现某个子目录下的代码有独立维护和管理的需求,例如该子目录下的代码可以作为一个独立的组件被多个项目复用,或者为了更好地组织代码结构。这时就需要将该子目录从原有的大仓库中分离出来,成为一个独立的Git仓库。
实现步骤
简单方法(Git 1.7.11及以上版本)
- 准备旧仓库注意:
1
2cd <big-repo>
git subtree split -P <name-of-folder> -b <name-of-new-branch><name-of-folder>
不能包含前导或尾随字符,例如名为subproject
的文件夹必须写成subproject
,而不是./subproject/
。<name-of-new-branch>
是在现有/旧仓库中创建的分支,而不是后面要创建的新仓库的分支。- Windows 用户:当文件夹深度大于 1 时,
<name-of-folder>
必须使用 *nix 风格的文件夹分隔符/
,例如path1\path2\subproject
必须写成path1/path2/subproject
。
- 创建新仓库
1
2
3mkdir ~/<new-repo> && cd ~/<new-repo>
git init
git pull </path/to/big-repo> <name-of-new-branch> - 将新仓库链接到 GitHub 或其他远程仓库
1
2git remote add origin <[email protected]:user/new-repo.git>
git push -u origin master - 清理旧仓库(可选)
1
git rm -rf <name-of-folder>
多子文件夹分离
- 准备旧仓库注意:
1
2
3
4pushd <big-repo>
git filter-branch --tree-filter "mkdir <name-of-folder>; mv <sub1> <sub2> <name-of-folder>/" HEAD
git subtree split -P <name-of-folder> -b <name-of-new-branch>
popd<name-of-folder>
不能包含前导或尾随字符。- Windows 用户:当文件夹深度大于 1 时,
<name-of-folder>
必须使用 *nix 风格的文件夹分隔符/
,并且不要使用mv
命令,而是使用move
。
- 创建新仓库
1
2
3
4mkdir <new-repo>
pushd <new-repo>
git init
git pull </path/to/big-repo> <name-of-new-branch> - 将新仓库链接到 GitHub 或其他远程仓库
1
2git remote add origin <[email protected]:my-user/new-repo.git>
git push origin -u master - 清理(可选)
1
2
3popd # 退出 <new-repo>
pushd <big-repo>
git rm -rf <name-of-folder>
使用 git filter-branch
- 克隆本地仓库
1
git clone /XYZ /ABC
- 保留要重写的分支并移除 origin
1
2
3cd /ABC
for i in $(git branch -r | sed "s/.*origin\///"); do git branch -t $i origin/$i; done
git remote rm origin - 移除与子项目无关的标签(可选)
1
git tag -l | xargs git tag -d
- 使用
filter-branch
排除其他文件1
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter ABC -- --all
- 删除备份引用日志以释放空间
1
2
3
4git reset --hard
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
git reflog expire --expire=now --all
git gc --aggressive --prune=now
使用 git filter-repo
1 |
|
核心代码
使用 git subtree
分离子目录
1 |
|
使用 git filter-branch
分离子目录
1 |
|
使用 git filter-repo
分离子目录
1 |
|
最佳实践
- 在进行分离操作之前,建议先在测试环境中进行尝试,确保操作不会导致数据丢失或损坏。
- 如果需要保留完整的历史记录,尤其是在子目录有重命名操作的情况下,可以采用复制仓库并手动删除不需要的文件的方法。
- 定期清理仓库中的无用数据,如使用
git reflog expire
和git gc
命令,以减小仓库的大小。
常见问题
合并提交未被移除
使用 filter-branch
时,合并提交可能不会被移除。这通常是一个外观问题,如果不影响实际使用,可以忽略。
提交重复
使用 filter-branch
后可能会出现提交重复的问题,这可能是由于删除了某个合并提交导致的。可以尝试手动解决或重新进行操作。
操作缓慢
对于大型仓库,使用 git filter-branch
可能会非常缓慢。可以考虑使用基于 libgit2 的 git_filter
工具,它可以显著提高处理速度。
推送失败
如果在推送时遇到保护分支的问题,可以参考相关文档(如 GitLab 的受保护分支文档)进行解决。
将子目录分离(移动)到单独的Git仓库
https://119291.xyz/posts/detach-subdirectory-into-separate-git-repository/