Git 仓库拆分
方案对比
subtree
使用命令 git subtree split -P dirPath -b branchName
将目标文件夹的代码都保存到指定分支。试了下,该方案虽然保留了 commit,但是所有分支全都没了
filter-branch
git filter-branch --prune-empty --subdirectory-filter dir1 -- --all
--prune-empty
:表示如果修改后的提交为空则扔掉不要
--subdirectory-filter
:指定子目录路径
-- --all
:针对所有的分支
当上述命令执行完毕后,就可以看到本地的新仓库已经是原仓库子目录中的内容了,且保留了关于该子目录所有的提交历史。看了下仓库大小和操作前没有变化,因为 .git 目录里还保留着无用的 object。还需要清理一下无用文件。下面来实操一下 filter-branch
怎么拆分仓库。
实战
模拟 git 仓库
为了比较好追踪问题,我们先模拟出一个 git 仓库,这样数据量小,排查起来会比较方便。
看一下 objects 文件夹:
在操作之前,先看一下仓库大小,使用 git count-objects -v
命令可以计算仓库大小:
count: 27
size: 31528
in-pack: 0
packs: 0
size-pack: 0
prune-packable: 0
garbage: 0
size-garbage: 0
查找仓库里的大文件
使用下面的命令可以查找仓库中的大文件:
git rev-list --all --objects | \
grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -n 3 | awk -F ' ' '{print $1}')"
大概说明一下上面脚本使用到的命令:
git rev-list
:查看指定对象的文件路径、文件名
git rev-list --objects --all |grep a6d75f
//将列出文件路径
a6d75f7315534b7cfe73597f1ae0f388b9494332 aaa/Main.java
git verify-pack
:查看 git 打包文件的信息,输出 SHA-1、size 等字段。这里用到 $ 先保存 git verify-pack
的输出。
如果直接使用上述脚本会发现什么也没输出,因为脚本首先是分析 git 打包文件,然后再进行大小排序的。所以我们需要先使用命令 git gc
打包 git 本地目录存储的文件。
执行 gc 命令后,blob 对象会被打包,再查看 objects 目录可以看到创建了一个包文件(pack 文件)和一个索引文件。Git 会对大文件进行打包,生成 .pack
格式的文件以及同名的 .idx
格式的索引文件,存放在 .git/object/pack 目录中。通常来说,Git仓库的大文件都是.pack格式的,存放在这个目录中。
gc 后,我们再看一下仓库大小:
count: 0
size: 0
in-pack: 27
packs: 1
size-pack: 30671
prune-packable: 0
garbage: 0
size-garbage: 0
可以看到比 gc 之前稍稍少了一点。现在可以列出 idex 索引文件中存储的文件:
git verify-pack -v .git/objects/pack/pack-e3b9f038d3df8b6214fef04e37477f98a0b48911.idx
690898ddaf1b386531b9c1c81eec09eb18f4efa8 commit 208 166 12
2c9790dbda1a83f64c7c7a8660f11aea87e14e78 commit 215 168 178
c4111b93fc90f70885dc71eae6be44811658197c commit 209 163 346
0c5e8cf09a2574a38e26c1c7cb0361f8e3b7f2b1 commit 218 175 509
50b29eb5e83cf486a72c3dfa6f0ba5ee3b2b2e32 commit 214 166 684
f05893bf834b85795e8e592bbe51130bc1792ce1 commit 252 192 850
dbd772576def32be5be8aa55fdbc2e5143551d5d commit 157 117 1042
2095176e6f0116345c3fe223f724deb057ee8b73 blob 166 136 1159
...
上述每行里各项值分别对应着:
SHA-1, type, size, size-in-packfile, offset-in-packfile
执行 filter-branch
filter-branch
需要指定目录,拆分仓库时可能需要同时保留多个目录,可以使用以下脚本(这里仅保留 aaa,如果填的是 aaa bbb,即可同时保留 aaa 和 bbb 文件夹):
git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- aaa' --prune-empty -- --all
执行完命令后,git 图表变成:
可以看到,与 aaa 文件夹下文件无关的 commit 都被移除了。分支也都被保留了,棒棒的!正是我们想要的效果。再看保留下来的各个 commit 的内容:
可以看到,虽然 commit 的提交信息没有变,但是内容却变了,first commit 原先新增了 aaa/Main.java 和 bbb/Main.java,但是操作后,提交记录里只剩下了 aaa/Main.java。内容变化了,同样的 SHA1 值也变化了。
再查看一下仓库大小:
count: 10
size: 40
in-pack: 27
packs: 1
size-pack: 30671
prune-packable: 0
garbage: 0
size-garbage: 0
额,size-pack 的大小没有变化,我们直接查看一下 objects 目录。
虽然工作目录中不需要的文件已经被清除了,但是 git/objects/pack 目录里存储的 pack 文件和索引文件却没有被删除,重新读取一下 idx 文件的索引:
git verify-pack -v .git/objects/pack/pack-e3b9f038d3df8b6214fef04e37477f98a0b48911.idx
——————
690898ddaf1b386531b9c1c81eec09eb18f4efa8 commit 208 166 12
2c9790dbda1a83f64c7c7a8660f11aea87e14e78 commit 215 168 178
c4111b93fc90f70885dc71eae6be44811658197c commit 209 163 346
0c5e8cf09a2574a38e26c1c7cb0361f8e3b7f2b1 commit 218 175 509
50b29eb5e83cf486a72c3dfa6f0ba5ee3b2b2e32 commit 214 166 684
f05893bf834b85795e8e592bbe51130bc1792ce1 commit 252 192 850
dbd772576def32be5be8aa55fdbc2e5143551d5d commit 157 117 1042
...
和执行 filter-branch
之前是一模一样的。要不再 gc 一下看看?再次 gc 后,blob 文件又被打包,并生成了两个新的 idx 文件和 pack 文件:
再次查看索引:
git verify-pack -v .git/objects/pack/pack-af08fc2fbea78dfae1503ae4b03578a4113da969.idx
——————
690898ddaf1b386531b9c1c81eec09eb18f4efa8 commit 208 166 12
fa322194e3401dfd14591d82951a9aca3b620631 commit 215 169 178
2c9790dbda1a83f64c7c7a8660f11aea87e14e78 commit 215 168 347
c4111b93fc90f70885dc71eae6be44811658197c commit 209 163 515
bb57bc14d163c5fc7b9bbce7f6e006a8e5c37564 commit 218 172 678
0c5e8cf09a2574a38e26c1c7cb0361f8e3b7f2b1 commit 218 175 850
50b29eb5e83cf486a72c3dfa6f0ba5ee3b2b2e32 commit 214 166 1025
2c4d78cfe2251613b1f23693a3ebe6f4ee9e793d commit 214 166 1191
...
690898
这个 commit 对象是最初添加大文件时的 commit,可以看到该 commit 对象依然存在在最新的 idx 文件中。实锤了,一番操作实际上只是工作目录看起来空旷了,git 仓库里不该有的文件还是一样没落下。
移除无用文件
执行到这一步目标很清楚,就是把 690898
这类已经不可达的 commit 和 pack-af08fc2fbea78dfae1503ae4b03578a4113da969.pack
这些已经没有任何历史提交引用的文件都删除掉。
谷歌一搜,网络上流传的方法试一下:
rm -Rf .git/refs/original
rm -Rf .git/logs
git gc
可以看到,执行到这里,git/refs/original
里空空如也,并不需要删什么东西,而且 git gc 我们刚也试过了,并没有什么卵用。git prune
也是一样的,因为 gc 实际上就是调用 git prune
。
事已至此,不方用 Java 垃圾回收的思想来理解:文件之所以没有被删掉,肯定是哪里还存在这引用,找出引用应该是解决问题的关键。
引入的大文件,从上面可以看出就是 04ddd8
这个,使用命令查看以下文件路径
git rev-list --objects --all |grep 04ddd8
————
04ddd80c36932757f15327b35f5bcc123082c454 bigfile.zip
再随便翻翻 git 本地目录,查看 packed-refs
文件,refs
= references
,感觉有点东西啊,打开一看果然发现文本里记录着 refs/original 相关的东西。
# pack-refs with: peeled fully-peeled sorted
fa322194e3401dfd14591d82951a9aca3b620631 refs/heads/master
bb57bc14d163c5fc7b9bbce7f6e006a8e5c37564 refs/heads/test
690898ddaf1b386531b9c1c81eec09eb18f4efa8 refs/original/refs/heads/master
c4111b93fc90f70885dc71eae6be44811658197c refs/original/refs/heads/test
690898
这个 SHA1 值很眼熟了,就是最开始的一个 commitID。再回看最初的 git 图表,可以看到 690898
和 c4111b
分别指向操作前的 master 分支和 test 分支。
filter-branch
后,整个 git commit 树都变了,大清都亡了,之前的这两个引用肯定是没用的旧引用了,删删删!然后再使用命令检查可达性 :
git fsck --full --unreachable
:验证数据库中对象的连通性和有效性。
Checking object directories: 100% (256/256), done.
Checking objects: 100% (37/37), done.
unreachable blob 04ddd80c36932757f15327b35f5bcc123082c454
unreachable commit 0c5e8cf09a2574a38e26c1c7cb0361f8e3b7f2b1
unreachable blob ab7268ccda89f9f58e6ae60050b360301cc91a71
unreachable commit 2c9790dbda1a83f64c7c7a8660f11aea87e14e78
unreachable tree 2fc5bb169f78176029674d4b5234d51e287df50f
unreachable commit c4111b93fc90f70885dc71eae6be44811658197c
unreachable tree 45a12cd512c7ae28d05b7b391e8d872031f1a89c
unreachable tree 4ed47074261cd7cb9f26b38452b56f88014ff2ac
unreachable commit 50b29eb5e83cf486a72c3dfa6f0ba5ee3b2b2e32
unreachable tree 5207eed7e90f90d9c6b6bf63cfea997033136def
unreachable tree 53b3421a3db498b5b15e96141a46e3c0b2431881
unreachable tree 598e2a6bb6dec5bb9b62c0bd757b66504413d5a2
unreachable commit dbd772576def32be5be8aa55fdbc2e5143551d5d
unreachable tree e27ea026bb5098ab33079172ae7835b27e40f4c4
unreachable tree 651d867dfc1f337090bf8bec6fef4459b369ac1a
unreachable commit 690898ddaf1b386531b9c1c81eec09eb18f4efa8
unreachable tree ed9833e93207376f729c517a5780aa7063bca0bb
unreachable commit f05893bf834b85795e8e592bbe51130bc1792ce1
unreachable tree 708923165513ea1259dcaff627b201b142545c6d
可以看到我们最想删掉的大文件 04ddd8
被列出来,并且是不可达的。
再执行命令 git repack -A -d
,确保不可达的对象被解压并保持解压。然后再调用 git prune
重新计算项目大小:
count: 0
size: 0
in-pack: 18
packs: 1
size-pack: 2
prune-packable: 0
garbage: 0
size-garbage: 0
可以看到,无用的旧文件已经被清除了~
相关命令
这里记录以下调研过程中遇到的命令,git 接触很久了,但是很多命令却还是第一次见,深感自己之渺小。
保存镜像
git clone --mirror xxx地址
查看仓库大小
git count-objects -v
Git 仓库拆分的更多相关文章
- 抛砖系列之git仓库拆分工具git-filter-repo
最近负责把团队内的git仓库做了一次分拆,解锁一个好用的工具git-filter-repo,给大伙抛砖一波,希望以后遇到类似场景时可以信手拈来. 背景 笔者团队目前是把业务相关的java项目都放到了一 ...
- 如何将硕大笨重的git仓库拆分成灵活轻巧的模块小仓库
方法1.拆分一个子目录为独立仓库 参考链接:https://segmentfault.com/a/1190000002548731 以前是用 filter-branch 来实现,这个需求太常见了,有人 ...
- git 仓库拆分方案对比
此文已由作者张磊授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 前言 git 拆分仓库在网上已有的案例上来看,分为 submodule 和 subtree. 还有基于这两个方 ...
- git仓库拆分
例如: # 这就是那个大仓库 big-project $ git clone git@github.com:tom/big-project.git $ cd big-project # 把所有 `co ...
- 【Git】原Git库拆分子目录作为新仓库,并保留log记录
一.需求描述: 现有一个git仓库,Team A和Team B的人操作同一仓库的不同目录,Team A的dev希望Team B的dev没有权限review属于Team A的代码目录,故现需要先将这个g ...
- GIT 如何合并另一个远程Git仓库的文件到本地仓库里某个指定子文件夹并不丢失远程提交记录?
问题背景: 最近在重新整理手中的一个项目,目前该项目分为PC项目,手机项目,某第三方接口项目,第三方接口服务项目和手机项目 因为之前规划的原因,原来的四个项目是分两个解决方案来管理的 ...
- 清理git仓库
参考 https://harttle.land/2016/03/22/purge-large-files-in-gitrepo.html https://git-scm.com/docs/git-re ...
- 流程自动化RPA,Power Automate Desktop系列 - 批量备份Git仓库做好灾备
一.背景 打个比如,你在Github上的代码库需要批量的定时备案到本地的Gitlab上,以便Github不能访问时,可以继续编写,这时候我们可以基于Power Automate Desktop来实现一 ...
- 多本地代码工作点更新到2个远端GIT仓库
摘要:本文介绍了笔者多个本地工作节点(地方)的多台电脑(PC/笔记本电脑)同步源码到2个远端的GIT(一个GITHUB国外强制公开,一个oschina国内可不公开). 作者:太初 转载说明:请指明原作 ...
随机推荐
- CentOS硬软链接
硬软链接说明 软链接: 1.软链接,以路径的形式存在.类似于Windows操作系统中的快捷方式 2.软链接可以 跨文件系统 ,硬链接不可以 3.软链接可以对一个不存在的文件名进行链接 4.软链接可以对 ...
- nginx,wsgi项目部署
1.一些重要概念 https://www.cnblogs.com/xiaonq/p/8932266.html 1.1web容器 什么是web容器 1.web容器是帮助我们部署java丶php丶pyth ...
- 在执行gem install redis时 : ERROR: Error installing redis: redis requires Ruby version >= 2.2.2
在执行gem install redis时 提示: gem install redis ERROR: Error installing redis: redis requires Ruby versi ...
- Python中判断一个中文是否中文数字的方法
Python内置功能非常强大,在字符串内置函数中提供了一个判断字符串是否全数字的方法,而且这个方法不只是简单判断阿拉伯数字,包括中文数字和全角的阿拉伯数字都认识,这个函数就是字符串的isnumeric ...
- Python的富比较方法__le__、__ge__之间的关联关系分析
Python的富比较方法包括__le__.__ge__分别表示:小于等于.大于等于,对应的操作运算符为:"<=".">=".那么是否象普通数字运算一 ...
- 第十二章、Designer中的menu菜单、toolBar工具栏和Action动作
老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 一.引言 Qt Designer中的部件栏并没有菜单.toolBar以及Action相关的部件,仅在 ...
- 74CMS3.0储存型XSS漏洞代码审计
发现一个总结了乌云以前代码审计案例的宝藏网站:https://php.mengsec.com/ 希望自己能成为那个认真复现和学习前辈们思路的那个人,然后准备慢慢开始审计一些新的小型cms了 骑士cms ...
- 【复习笔记】重习 AC 自动机
发现已经忘了许多....于是复习一下 基础要点概况 AC 自动机基于 Trie 树 的结构,即构建 AC 自动机前需要先建 Trie. 一个状态中除了转移 \(\delta\) 之外还有失配指针 \( ...
- tcp/ip原理/三次握手/四次挥手
@ tcp/ip原理 1.1 tcp/ip三次握手 1.1.1 建立过程说明 a) 由主机A发送建立TCP连接的请求报文, 其中报文中包含seq序列号, 是由发送端随机生成的, 并且还将报文中SY ...
- webstorm2017.02版本如何使用material theme
本想废话一番,表达对material theme的喜欢.还是直接说方法吧~ file-settings-Plugins-Browse repositories-搜索 material theme -选 ...