目录
1.1.2另一个解决办法是 filter-branch (过滤分支)
1.背景
背景章节参考:https://www.oschina.net/translate/how-to-handle-big-repositories-with-git?cmp
两种大代码库
实际工作中可能会遇到git大仓库的场景。
如果仔细想想,大概会有两种导致仓库大规模增长的原因:
- 项目累积了非常长的历史(项目成长了很长一段时间并且积累了包袱)。
- 项目包括了巨大的二进制资产,需要与代码一起跟踪配对。
- 两者皆有。
1.1处理拥有大量历史记录的库
将一个库视为大规模库的界线非常高 - 比如 Linux 内核的最后一个版本记录了超过 1500 万行代码,但人们仍然愿意完整阅读。
1.1.1浅克隆是简单的的解决办法
为了更快、更节省开发者和系统时间也更节约磁盘空间,第一个解决办法是使用 git 进行浅克隆。通过浅克隆可以只克隆某个库最后的历史记录。怎么做到?只需要使用 --depth 选项,比如:
git clone --depth depth remote-url
想像一下,如果你的项目库中积累了 10 年甚至更长时间的历史记录 - 比如 JIRA 是我们往 git 迁移的一个 11 年的老库 - 累积节约的时间非常显著。完整的克隆 JIRA 有 677 MB,如果包含工作目录还有另外的 320+ MB,总共超过 47,000 多次提交。通过浅克隆的方式检出 JIRE 需要 29.5 秒,而检出完整的历史记录则需要 4 分 24 秒。随着时间地推移及项目二进制资产的增长,这个差距也会成比例的增长。任何情况下,构建系统都会大大受益于这种技术(指浅克隆)。
1.1.2另一个解决办法是 filter-branch (过滤分支)
巨大的库往往存在着大量错误的提交或无用的资源,对此,使用 filter-branch 是个很好的解决办法。这个命令可以根据预先定义的模式对项目历史进行过滤、整理、修改,甚至跳过一些文件。它是 git 工具集中的一个非常强大的工具。目前已经有脚本可以用于识别 git 库中的大型对象,所以它使用起来非常容易。
使用 filter-branch 的示例:
git filter-branch --tree-filter 'rm -rf /path/to/spurious/asset/folder' HEAD
filter-branch 有一个小小的缺点:一旦使用了 filter-branch,实际上已经重写了整个项目历史,因此每次提交的 ID 都会发生变化。这要求每个开发者都要重新克隆更新后的库。
所以,如果你打算使用 filter-branch 来进行一次清理行动,应该警告你的团队,计划一个短期的冻结来进行操作,然后通知大家重新克隆库。
1.1.3浅克隆的替代者:只克隆一个分支
从 2012 年 4 月发布的 git 1.7.10 开始,你可以通过只克隆某一个分支来限制历史记录的数量,就像这样:
git clone URL --branch branch_name --single-branch [folder]
对于长期运行分发的分支,或者你在有很多分支的情况下,这个特殊的技巧都非常有用。如果你只有极少数分支,那这个办法不会带来显著的效果。
1.2处理拥有巨大二进制资产的库
第二类大型仓库中的代码含有巨大的二进制资产。游戏团队要处理巨大的 3D 模型,Web 开发团队需要跟踪图像资产,CAD 团队可能需要操作和跟踪二进制交付物的状态。所以有各种不同的软件团队在使用 git 的过程中会遇到这样的问题。
git 在处理二进制资产的时候并不是特别差劲,但它也不会干得特别好。默认情况下,git 会完整压缩存储二进制资产的所有后续版本,如果你有很多二进制资产的情况下,这显然不是最佳方案。
可以通过一些基本的调整来改善情况,比如运行垃圾回收 git gc,或者在 .gitattributes 中对部分二进制类型进行调整,以使用 delta 方式的提交。
2.仓库现状
.
|-- icb1
| |-- image
| | |-- bzImage
| | `-- icb1_zeos_linux_defconfig
| `-- src
| `-- linux-4.13.2
| `-- linux-4.13.2_icb1.tar.gz
|-- imx8qm
| |-- android-8.1
| | |-- image
| | | |-- defconfig
| | | |-- fsl-imx8qm-mek.dtb
| | | `-- Image
| | `-- src
| | `-- kernel_imx_android.tar.gz
| `-- linux-4.9.69
| |-- image
| | |-- defconfig
| | |-- fsl-imx8qm-mek.dtb
| | `-- Image
| `-- src
| `-- kernel-imx8qm.tar.gz
`-- zcu102
|-- image
| |-- defconfig
| |-- Image
| `-- zcu102_phy.dtb
`-- src
`-- linux-2017.3
|-- linux-xlnx-2017.3_video_ea.tar.gz
`-- system-top.dts
从目录结构可以看出,我们资源库中存在着大量的二进制文件,占用空间已经达到十几个G字节,而且随着项目的增加,仓库必然更大,几百个G,甚至上T。随着项目的演进,资源库中会存入其他资料,如文档、脚本等轻量级文件。如果为了看某些文档而将整个仓库克隆下来,显然是不划算的。
3.仓库改造
目前仓库所有二进制代码都处于master分支。为了解决上面描述的问题,我们可以引入GIT的分支管理。
我们的构想是:轻量化master分支,对其他资源如文档、脚本、源码、镜像分别建立不同的分支来进行管理。Git 克隆时只克隆master分支,然后对需要的分支进行单独处理。这样,既节省了克隆时间,又节省了存储空间。
下面,就对目前仓库进行改造。
3.1第一步:先建立一个VM分支,并推送至服务器
因为目前仓库只有源码镜像文件,所以,只需要先把框架搭建起来;后面要增加文档、脚本等就比较方便。
git branch VM git checkout VM touch readme git add readme git commit -m “VM branch first commit” git push origin VM:VM |
3.2第二步:轻量化master分支
目前采取的措施很暴力,直接清空master分支,让其不保存任何资源。这里就会用到“背景”部分提到的核武器级选项“filter-branch”
git checkout master git filter-branch --tree-filter 'rm -f passwords.txt' HEAD git gc --aggressive --prune=now git push --force origin master |
--tree-filter 选项在检出项目的每一个提交后运行指定的命令然后重新提交结果。在本例中,你从每一个快照中移除了一个叫作 passwords.txt 的文件,无论它是否存在。
我们这里可以使用:git filter-branch --tree-filter 'rm -rf *' HEAD命令,直接删除所有。
3.3第三步:删除仓库目录,重新克隆
因为,前面利用 filter-branch修改了整个仓库的文件及提交记录,如前面“背景”部分提到的,你需要重新克隆工程,才能体验到“.git文件夹”减小的快感。
git clone user@gitpath |
这样,架构已经搭建好了,如果后面要添加新的分支,就可以直接操作了。在新建分支时,默认会保留master的提交记录,如果你不想这样,可以用下面的命令:
git branch newbranch git checkout --orphan newbranch |
--orphan参数可以让你去掉历史遗留提交记录
4.仓库使用
经历了上面的仓库改造后,如果新用户的你直接克隆整个仓库,仍然会将仓库包含的所有分支信息的.git文件夹下载下来。虽然其是压缩后的数据文件,但当二进制文件很多时,.git文件夹也会非常之大。
所以,我们可以只克隆仓库的master分支下来(几十KB),它包含了本地仓库与远程仓库的联系。然后,再建立一个本地分支来跟踪远程你需要的分支。这样,你就可以在本地只保留需要的分支的数据文件。时间和空间都可以节省。命令示例入下:
//只检出master分支 git clone gaojianyun@ipaddr:/home/gaojianyun/test.git --branch master --single-branch git branch test //建立本地分支 git checkout test //切换到本地分支 git pull origin vm //拉取远程需要的分支 //下面是需要提交更改步骤 git push --set-upstream origin test:vm //首次建立联系 git push origin //首次过后,就可以直接push了 |