我们使用Git来管理项目的时候,可能会提交一些Blob的二进制文件,这些文件并不能像文本文件一样采用diff delta的形式进行版本控制。如果这些文件一直跟随master的主版本,那么就是属于有效的文件。

然而很多时候这些二进制文件会被删除重建,那么由于Git的特性,这些文件会一直留在Git的历史记录中,这样会导致Git仓库变得庞大,不利于版本控制和迁移。最直观的就是clone的时候会很慢,而使用--depth=1则无法看到历史提交的代码。

查找历史文件

历史提交的二进制文件通常我们可以认为是不需要的,然而在多人协作的时候这个事情我们并不能非常确定。因此我们需要主动查找较大的二进制文件来处理,最简单的办法就是直接扫描大文件。

git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')"

通过这个命令我们可以看到历史提交中最大的10个文件,然后我们可以根据这个文件的hash值来查找这个文件的提交记录。如果需要看到文件的大小,也可以再补充下输出。

git verify-pack -v .git/objects/pack/*.idx \
| grep blob \
| sort -k 3 -n \
| tail -10 \
| awk '{print $1, $3}' \
| while read -r hash size; do
file=$(git rev-list --objects --all | grep "$hash")
echo "$file $size"
done

BFG Repo-Cleaner

虽然可以使用git-filter-branch来处理历史提交,但是这个命令的效率比较低,且容易出现错误。BFG是一种更简单、更快捷的替代方法,主要是用来处理历史提交的文件,可以删除指定的文件,也可以替换文件内容。

BFG需要使用Java来运行,因此需要先确保运行环境JDK >= 8。之后使用git clone --mirror来克隆仓库,然后使用BFG来处理历史提交。

git clone --mirror git://example.com/some-big-repo.git

需要注意的是,使用--mirror标识会将整个仓库的完整副本克隆下来,包括所有的提交历史,并且不会存在任何普通文件。同样的,当处理完成之后这个仓库所有的分支都会被修改,因此需要谨慎操作。

而如果仅处理单个分支,那就是使用常规的git clone即可。但是需要注意的是,由于其他分支仍然有可能持有旧的分支内容,因此就必须要将所有涉及到的分支依次处理,这样就非常麻烦了。

接下来就是使用BFG来处理历史提交,BFG的使用非常简单,只需要指定需要处理的文件即可。下载BFGjar包可以在https://rtyley.github.io/bfg-repo-cleaner/中找到。

java -jar bfg.jar --delete-files path/to/file.txt some-big-repo.git

BFG还提供了一些其他的功能,比如删除指定大小的文件、替换文件内容、删除文件夹、删除文件通用匹配符。

java -jar bfg.jar --strip-blobs-bigger-than 1M some-big-repo.git
java -jar bfg.jar --replace-text pwd.txt some-big-repo.git
java -jar bfg.jar --delete-folders path some-big-repo.git
java -jar bfg.jar --delete-files '*.png' some-big-repo.git

但是这里的匹配模式也存在局限,例如无法同时指定文件和大小,例如需要移除> 1Mpng文件是做不到的,经过测试其匹配模式总是倾向于后设置的模式。不过这里并没有阅读源码,只是简单的测试判断。

此外,不需要担心BFG会删除HEAD的提交,BFG不会处理HEAD的提交,即使BFG会从早期的历史记录中删除文件,当然也可以通过--no-blob-protection来关闭保护。

WARNING: The dirty content above may be removed from other commits, but as
the *protected* commits still use it, it will STILL exist in your repository.

在处理完成后,在同级目录下会生成report文件夹,其中包含了处理的文件信息,可以查看处理的文件数量、大小等信息,以及HASH变更的信息。在检查无误后,要将历史记录的过期时间设置为现在,且需要使用git来清理无引用的数据,这样就可以将BFG处理的数据真正删除。

cd some-big-repo.git
git reflog expire --expire=now --all && git gc --prune=now --aggressive

此时就可以通过du命令检查文件夹的大小了,通常我们只需要关注.git文件夹即可。

du -sh .git

最后,直接使用git push来推送到远程仓库即可,这样就完成了历史提交的处理。注意如果使用分支模式的话,就需要加入-force选项来强制推送。

git push

这里还需要有一个额外的步骤,需要让每个参与者将本地的仓库删除,然后完整重新clone最新的仓库,防止持有旧数据的仓库重新推回仓库。此外,也可以使用git-filter-repo来实现类似的处理。

GitHub

从历史记录中删除文件并不是简单的事情,如果需要我们手动来执行操作的话,就很像我们从某一次提交开始,不断向后rebase。那么在这个过程中自然就会导致commithash值发生变化,从而出现一些问题,这里我们主要关注在GitHub的表现。

  1. 如果二进制文件是在很久前的提交,例如5年前的提交,而假设我们仅会删除此提交的某个文件,对于其他的提交并没有处理。但是由于需要重写历史提交记录,这就会导致从5年前引入的id开始到最新的提交全部被重写,这部分可以在BFGCommit Tree-Dirt History中关注到。
  2. 前边我们也提到了BFG会将hash变化信息写入report文件夹,实际上在重写的commit描述信息中,也会发现Former-commit-id: xxx的数据,用以标识这个commit重写前的引用。
  3. GitHubcontributions面板中,也就是绿色的瓷砖部分,会出现重写commit的历史提交出现重复贡献的现象,也就是说原来可能仅有1个提交,现在变成了2个,对于这个问题是可以减小影响面的。
  4. 虽然contributions面板中会出现重复的提交,但是通过api获取的提交记录总数中并不会出现重复的数量增量,也就是说GitHub并没有将重写的commit计入历史提交记录中。
  5. 对于通过分支模式而不是mirror模式清理的单独分支,虽然通过BFG可以将历史提交的二进制文件删除,但是其commit数量的计算会出现问题,其会切断fork之前的联系,也就是说原本fork分支的提交记录会被重新计算。
  6. 虽然对于主分支的提交数量不会影响,但是此时如果我们打开重写过后的commit描述,可以发现仍然可以找到原本的commit。这就意味着这个object实际上并没有被删除,只是不再被引用,而是处于游离指针的状态。

实际上这里的影响面还是挺大的,特别是对于早些时间引入的二进制文件,会导致大范围的历史提交记录重写。

对于contributions面板的影响,也是看起来比较大的问题,不过我们可以先fork主分支,然后再将主分支设置为fork分支,然后再更改名称的形式来解决这个问题。但是这个方式并没有完全解决问题,还是会因为提交日期的重写而导致contributions面板的数据不准确,不过影响面小了很多。

而对于其他问题,通常都是无法避免的问题,因为Git的特性决定了这个问题的存在。如果需要处理历史提交中私有文件的泄漏,则通常认为是不可靠的,此时必须要立即修正密钥。因此无论是当前提交还是在处理历史提交的时候,需要谨慎操作,不要泄漏私密文件,尽量避免对历史提交的处理。

Blog

参考

偶然发现Git文件夹非常大,使用BGF来处理Git历史Blob文件的更多相关文章

  1. win10用户文件夹重命名,启用administrator账户,删除文件夹时提示找不到该项目

    这一切都源自楼主洁癖一般的强迫症. 楼主在重置win10后的安装过程中用microsoft账户登录的电脑,发现用户文件夹名称怪怪的,于是想重命名一下.楼主发现重命名用户文件夹并不能简单地用F2搞定,于 ...

  2. python 查找文件夹下以特定字符开头的某类型文件 - os.walk

    Python os.walk() 方法 os.walk() 方法用于通过在目录树中游走输出在目录中的文件名,向上或者向下.os.walk() 方法是一个简单易用的文件.目录遍历器,可以帮助我们高效的处 ...

  3. VS 2010不显示头文件源文件和所有以前分类的文件夹,*.h 和*.cpp都显示在同一个文件

    打开VS后不显示头文件源文件和所有以前分类的文件夹,*.h 和*.cpp都显示在同一个文件 点击右图红色指示显示所有文件夹按钮,就能恢复.

  4. 用批处理文件自动备份文件及文件夹,并自动删除n天前的文件

    原文:用批处理文件自动备份文件及文件夹,并自动删除n天前的文件 ---恢复内容开始--- 下是备份的批处理,添加到"计划任务"中,设定时间自动运行 复制代码 代码如下:@echo ...

  5. Linux将一个文件夹或文件夹下的所有内容复制到另一个文件夹

    Linux将一个文件夹或文件夹下的所有内容复制到另一个文件夹     1.将一个文件夹下的所有内容复制到另一个文件夹下 cp -r /home/packageA/* /home/cp/packageB ...

  6. FTP文件夹错误:【打开FTP服务器上的文件夹时发生错误。请检查是否有权限访问该文件夹】

    资源管理器访问FTP服务器报错,提示FTP文件夹错误:[打开FTP服务器上的文件夹时发生错误.请检查是否有权限访问该文件夹]. 详细信息: 200 Switching to ASCII mode. 2 ...

  7. windows资源管理器多标签打开 windows文件夹多标签浏览 浏览器tab页面一样浏览文件夹 clover win8 win10 报错 无响应问题怎么解决 clover卡死 clover怎么换皮肤

    大家都知道,我们打开一堆文件夹的时候,是什么样子 “厚厚的一叠”图标堆叠在一起的,非常的不方便 那么,是不是可以像浏览器一样的tab页面展示呢? 答案是可以的 安装好就是这样子的 是不是方便漂亮了很多 ...

  8. Python模块包(pycharm右键创建文件夹和python package的区别)中__init__.py文件的作用

    在eclipse中用pydev开发Python脚本时,我遇到了一个这样的现象,当我新建一个pydev package时,总会自动地生成一个空的__init__.py文件,因为是python新手,所以很 ...

  9. QTemporaryDir及QTemporaryFile建立临时目录及文件夹(创建一个随机名称的目录或文件,两者均能保证不会覆盖已有文件)

    版权声明:若无来源注明,Techie亮博客文章均为原创. 转载请以链接形式标明本文标题和地址:本文标题:QTemporaryDir及QTemporaryFile建立临时目录及文件夹     本文地址: ...

  10. 用python遍历一个图片文件夹,并输出所有路径到一个 txt 文件

    1 #coding:utf8 2 import os 3 import sys 4 def listfiles(rootDir, txtfile, label=0): 5 ftxtfile = ope ...

随机推荐

  1. Kali 配置Proxychains4

    Kali 配置Proxychains4 Proxychains4 是一款在 Linux 系统中广泛使用的代理工具,其全称为 ProxyChains.作为一款代理切换软件,Proxychains4 能够 ...

  2. mysql之事务范例

    package com.yeyue.lesson04; import com.yeyue.lesson02.JdbcUtils; import java.sql.Connection; import ...

  3. C#进行word模板占位符替换的几种工具

    word模板中,包含一些需要替换的项,比如{{姓名}} {{年龄}}或者$姓名$ $年龄$,从数据库获取信息后,对模板进行替换操作生成新的word文档. 简单对以下四种工具做了一下测试: 1.NPOI ...

  4. [计算机/硬件/GPU] 显卡

    序 截至2025年,全球主流的AI大模型满血版的参数量均已突破百亿级,算力需求正以指数级飙升.特别是 DeepSeek 公司旗下的 R1 系列模型的问世,推动了很多中小企业部署私有化模型的需求. 然而 ...

  5. NFS服务器离线问题解决

    NFS服务器离线问题解决 NFS服务器挂了会导致挂载的NFS客户端主机卡顿延迟,或者提示找不到文件 因为在执行一些命令的时候会自动去同步,用作同步的NFS服务端挂了,命令执行就会卡住 不过听说NFS还 ...

  6. Edge、谷歌浏览器默认下载器开启多线程下载

    浏览器默认下载器开启多线程下载 Chrome 浏览器,地址栏输入并回车: chrome://flags/#enable-parallel-downloading Edge 新版浏览器,地址栏输入并回车 ...

  7. NCS开发学习笔记-基础篇-前言

    nRF5 SDK 与 nRF Connect SDK 目前Nordic有2套并存的SDK:老的nRF5 SDK和新的nRF Connect SDK(简称NCS),两套SDK相互独立. nRF5 SDK ...

  8. ITSM运维管理整理总结

    ITSM 和我们平常所说的软件管理最大的不同? 目标不是管理技术,主要任务是管理用户和客户的IT需求 2.人员.技术.流程[重要] 3.几大模块 模块名称 干什么 备注 服务台 1.对接客户的前方,负 ...

  9. Web前端入门第 14 问:HTML 语义化是什么?

    HELLO,这里是大熊学习前端开发的入门笔记. 本系列笔记基于 windows 系统. 语义化:通俗地说,就是让机器可以理解编写出来的 HTML 代码.比如:搜索引擎.屏幕阅读器等. 非语义化写法 网 ...

  10. 【WPF开发】HandyControl Growl控件Error通知不自动消失的问题

    需求 HandyControl Growl在Error类型的通知不自动消失,此时需要他跟其他的统一. 找寻原因 那么翻翻代码看看为啥不消失呗 1.这是决定关闭通知的计时器 2.这是通过_staysOp ...