我们使用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. 离线安装IDEA插件:详细步骤指南

    离线安装IDEA插件:详细步骤指南 网络环境下载插件包 访问 https://plugins.jetbrains.com/ 一.准备工作 找到可用的插件文件 访问 https://plugins.je ...

  2. Hetao P1307 树的剖分 题解 [ 蓝 ] [ 树形 dp ] [ 贪心 ]

    树的剖分:很厉害的性质题,代码也很好写.运用到了奇偶性拼凑答案的 trick. 观察 首先发现一个很重要的条件:一个点的点权只可能是 \(0,1,2\). 这个条件开始我们可能无法用上,于是先想最后的 ...

  3. Luogu P9646 SNCPC2019 Paper-cutting 题解 [ 紫 ] [ manacher ] [ 贪心 ] [ 哈希 ] [ BFS ]

    Paper-cutting:思维很好,但代码很构式的 manacher 题. 蒟蒻 2025 年切的第一道题,是个紫,并且基本独立想出的,特此纪念. 判断能否折叠 我们先考虑一部分能折叠需要满足什么条 ...

  4. .Net 配置绑定 IOptions

    准备   首先准备下appsettins.json以及目标类   appsettins.json "StudentSettings": { "Id": 1023 ...

  5. OneDrive分享、多人操作电脑中大文件的方法

      本文介绍基于OneDrive网盘实现电脑大文件共享.协同办公的方法. 1 前言   作为网盘的重度用户,在学习.工作.生活中可以说少不了与各类云盘打交道.在这一过程中,也慢慢了解到不同网盘软件的特 ...

  6. VS2019 找不到资产文件 “xxxx\obj\project.assets.json”运行NuGet包还原以生成此文件

    参考地址:https://blog.csdn.net/weixin_42835409/article/details/107033059 下载 log4net 源码打开,编译报错: 严重性 代码 说明 ...

  7. ABC393C题解

    大概评级:橙. 送分题. 题意就是让你统计有多少条边是重边或自环. 设 \(u_i\) 表示第 \(i\) 条边的左端点,\(v_i\) 表示第 \(i\) 条边的右端点. 那么如果 \(u_i = ...

  8. 【配置化】C# dapper是怎么实现的?精短ORM

    目录 一.什么是dapper 二.实现问题与思路 & 源码参考 三.小结 一.什么是dapper dapper是个组件,一个dll文件,可以通过NuGet下载. 作用:快速访问数据库并自动完成 ...

  9. sql sever查询库中每个表是否存在某个列名 列出表名

    select t.TABLE_NAME from information_schema.columns t where t.COLUMN_NAME='列名';

  10. linux服务问题传文件连不上问题远程问题等

    通过iptables相关命令实现防火墙的打开和关闭 1.首先可以在打开的终端使用iptables --help查看帮助使用命令: 2.查看防火墙状态:service iptables status(此 ...