需求描述
:
现有一个非常之庞大(大的过分)
的
git
仓库,包含了
N多
个项目的源码,项目各个阶段的文档,原型等。对于新用户来说,
clone
一次需要很长时间(网速也是槽点)。因此决定将原仓库拆分,将源码子目录作为一个新的仓库,并且需要保留和子目录相关的
log
记录。
一.前期准备
所有的命令在Git-shell中进行
1. 原仓库在本地的目录结构如下图:
2. 描述约定
为了更好的描述命令,先定义一下命令中占位符的意义
原仓库:<old-repo>
新仓库:<new-repo>
想要分离出来的子文件夹名称: <name-of-folder>
新的远端地址:<new-git-url>
注意:
如果你在使用 Windows
,且该文件夹深度
> 1
,你
必须使用斜杠 /
作为目录分隔符而不是默认的反斜杠 \
二.
迁移(使用
filter-branch
命令
)
由于我需要迁移的子目录包含中文名,因此需要使用filter-branch
命令来实现迁移,当然,如果不包含中文的目录也可以使用git1.8
版本以后的
subtree
来实现,该方法稍后说明。
1.
首先,clone
一份原仓库并删掉原来的
remote
:(依次执行以下命令)
(1
)git clone <big-repo> <new-repo>
(2
)cd <new-repo>
(3
)git remote rm origin
2. 然后运行如下命令(
这是重点):
(1
)
git filter-branch --tag-name-filter cat --prune-empty --subdirectory-filter <name-of-folder> -- --all
这条命令同样会过滤所有历史提交,只保留所有对指定子目录有影响的提交,并将该子目录设为该仓库的根目录。这里说明各下个参数的作用:
--tag-name-filter
该参数控制我们要如何处理旧的
tag
,
cat
即表示原样输出;
--prune-empty
删除空的(对子目录没有影响的)提交;
--subdirectory-filter
指定子目录路径;
-- --all
该参数必须跟在
--
后面,表示对所有分支进行操作。如果你只想保存当前分支,也可以不添加此参数。
3.
清理.git
的
object
当上述命令执行完毕后,就可以看到本地的新仓库已经是原仓库子目录中的内容了,且保留了关于该子目录所有的提交历史。不过只是这样的话新仓库中的
.git 目录里还是保存有不少无用的 object,我们需要将其清除掉以减小新仓库的体积(如果你用subtree 的方法的话是不需要执行这一步的)。
依次执行以下命令:
(1
)git reset --hard
(2
)git for-each-ref --format="%(refname)" refs/original/ |xargs -n 1 git update-ref -d
(3
)git reflog expire --expire=now --all
(4
)git gc --aggressive --prune=now
4. 将新的本地仓库推送到远端
cd
到
<new-repo>
(1)
添加远端地址:
git remote add origin <new-git-url>
(2)推送到远端:
git push -u origin master
特别注意:
如果当前远端库是空的话,上述命令是好使的,由于我开始手贱,已经在新的git地址clone到本地,并且还新建了一个测试文件夹,因此导致新的git仓库的master不再是最新的了。因此可以先提交到dev分支:git push origin master:dev
5.
合并dev
到
master
(可选)
如果第4
步已经直接推动到
master
了。第
5
步可忽略。
(1)
首先将本地dev
合并到本地
master
①
切换到master
分支
②git merge origan/dev
--allow-unrelated-histories
由于是第一次合并,因此需要加上
--allow-unrelated-histories
,允许两个没有关联的历史合并在一起。后续的合并就不需要了。
(2)
将本地master push
到远端
(3)
至此,使用
filter-branch
方式拆分git库已经完成。有木有心动,去试试吧。
三.
补充subtree方式迁移
要求拆分的目录没有中文名
1.
首先,进入<
big-repo>
所在的目录,创建一个<name-of-new-branch>的临时分支,运行:
git subtree split -P <name-of-folder> -b <name-of-new-branch>
2.
然后,我们创建一个新的 git
仓库:
(1
)
mkdir <new-repo>
(2
)
git init
3. 接着把原仓库中的临时分支拉到新仓库中:
git pull </path/to/big-repo> <name-of-new-branch>
好了,完成。现在看看你的新仓库,是不是已经包含了原子文件夹中的所有文件和你之前在原仓库中的所有提交历史呢?后续步骤就可参照第二章中的3,4,5步了。
虽然网上一搜一大堆,还是希望能帮助有需要的同学,至少可以避免我踩过的坑了。