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国内可不公开). 作者:太初 转载说明:请指明原作 ...
随机推荐
- 【mq读书笔记】mq索引文件刷盘
索引文件的刷盘并不是采取定时刷盘机制,而是每更新一次索引文件就会将上一次的改动刷写到磁盘. 同步刷盘: GroupCommitRequest将被提交到GroupCommitService线程,Grou ...
- LaTeX中的参考文献BibTex
设置: BibTex代码及注释: 显示效果:
- 用Python爬取英雄联盟(lol)全部皮肤
小三:"怎么了小二?一副无精打采的样子!" 小二:"唉!别提了,还不是最近又接触了一个叫英雄联盟的游戏,游戏中很多皮肤都需要花钱买,但是我钱不够呀..." 小三 ...
- 「刷题笔记」Tarjan
贴一个讲得非常详细的\(tarjan\)入门教程 信息传递 讲个笑话:我之前用并查集求最小环过的这题,然后看见题目上有个\(tarjan\)标签 留下了深刻的印象:\(tarjan\)就是并查集求最小 ...
- Docker中搭建FastDFS文件系统(多图)
关于FastDFS FastDFS 是以 C 语言开发的一项开源轻量级分布式文件系统,他对文件进行管理,主要功能有:文件存储,文件同步,文件访问(文件上传/下载)等,特别适合以文件为载体的在线服务,如 ...
- 【2020.11.28提高组模拟】T1染色(color)
[2020.11.28提高组模拟]T1染色(color) 题目 题目描述 给定 \(n\),你现在需要给整数 \(1\) 到 \(n\) 进行染色,使得对于所有的 \(1\leq i<j\leq ...
- 老猿学5G:融合计费场景的离线计费会话的Nchf_OfflineOnlyCharging_Release释放操作
☞ ░ 前往老猿Python博文目录 ░ 一.Nchf_OfflineOnlyCharging_Release消息交互流程 Nchf_OfflineOnlyCharging_Release是CHF提供 ...
- 第12.4节 Python伪随机数数生成器random模块导览
random模块实现了各种分布的伪随机数生成器,常用功能包括: random.seed(a=None, version=2):初始化随机数生成器,如果 a 被省略或为 None ,则使用当前系统时间. ...
- PyQt(Python+Qt)学习随笔: QAbstractItemView的dragDropMode属性
老猿Python博文目录 老猿Python博客地址 一.概述 dragDropMode属性用于控制视图拖放事件的处理方式,其类型为枚举类型DragDropMode. 二.枚举类型DragDropMod ...
- 第15.11节 PyQt(Python+Qt)入门学习:Qt Designer(设计师)组件Property Editor(属性编辑)界面中主窗口QMainWindow类相关属性详解
概述 主窗口对象是在新建窗口对象时,选择main window类型的模板时创建的窗口对象,如图: 在属性编辑界面中,主窗口对象与QMainWindow相关的属性包括:iconSize.toolButt ...