项目仓库文件太大怎么清理?如何清理git仓库大文件记录?

前言

事情的起因是因为公司项目git仓库大小有 6 个多G,这导致在首次拉项目的时候用的时间非常久(通常需要一个小时左右),并且项目拉下来之后在本地使用git工具的时候会非常卡,这时通常电脑风扇都要转冒烟了,因为项目里面的.git文件非常大,有6个多G,同事们深受其苦,于是我就想去解决掉这个问题。

原因

实际上项目体积不应该有这么大,项目正常仓库体积也就50M以内,导致这个问题的原因主要是历史记录里面存在大文件

为什么有些大文件已经删除掉了还会存在占用存储空间的情况呢?这是因为git提供了版本控制的能力,如果这些文件没有保存,那么回退到有这些大文件的提交的时候,这些大文件要从哪里获取呢?所以这些文件即使现在被删除了,但是还是会被保存在.git文件中,以便需要的时候恢复。

如何清理

首先需要明确一点,清理只是修改跟已清理文件相关的提交记录,不会删掉提交记录 ,所有的tag和分支的提交记录都能正常保留,只是无法回溯已经清理的文件(都已经清理了应该也不需要回溯了),所以基本可以放心清理。

前置操作

⚠️ 兼容处理【重要】

远程仓库通常是多人协同开发的,清理文件的时候不能让其他人的本地修改丢失,分下面两种情况操作:

  1. 如果是所有人都直接拉取原始仓库代码,后续直接提PR/MR到原始仓库的情况
    需要所有人把本地需要的修改提交到原始仓库上的临时个人分支,清理完成后重新拉取原始仓库就可以把自身的代码拉下来,然后再删除个人临时分支,防止本地代码后续无法推送到远程。
  2. 如果是每个人都分别fork原始仓库,后续提PR/MR到原始仓库的情况的情况
    可以在原始仓库先针对每个人建一个临时分支,让所有人的代码先合到原始仓库对应个人分支上,后续清理完成之后,所有人重新拉取原始仓库就可以把自身的代码拉下来,然后再删除临时个人分支。

总之,需要保证清理的仓库是原始仓库原始仓库中已经包含了所有需要的代码(不能有代码存在本地未提交到远程原始仓库,否则这部分代码后续无法直接提交,只能通过复制等手段到清理后的仓库提交),也就是说清理的期间不宜大量改动代码,本地可以少量改动,后续可以复制代码到新仓库提交。

重新克隆原始仓库

等所有人需要保留的代码都提交到远程原始仓库后,新建一个文件夹,在新的文件夹内重新克隆一下远程原始仓库,这么做是因为后续会推送本地所有分支到远程,防止本地测试分支后续被推送到远程,保证仓库的干净。

ps:清理前可以先记录一下.git文件大小,以便清理后对比分析清理效果~

清理完成之后,所有人需要重新克隆一下仓库(注意是clone不是fetch

本地清理

清理有两种方式,一种是使用原生git命令清理,这种方式清理比较慢,而且可能存在一些问题。另一种就是利用官方推荐的一款工具清理,这种方式非常高效,也是官方推荐的清理方式。

以下两种方式可任选一种方式进行清理。

⭐️ 工具清理(官方推荐)

使用工具 git-filter-repo 清理(该工具是官方推荐改写commit的工具,效率非常高,清理一个文件用时在秒级),也是我比较推荐的清理方式。

git官方文档:

清理步骤
  1. 首先在【项目根路径】打开命令行终端

  2. 安装 git-filter-repo(需提前安装 Python,安装非常简单)

    执行命令: pip install git-filter-repo

  3. 生成仓库文件大小分析报告

    执行命令: git filter-repo --analyze

    报告会生成在 .git/filter-repo/analysis 目录下,查看 path-all-sizes.txtpath-sizes.txt 找到大文件

    生成文件内容大致如下:

    列表按照文件大小从大到小排列,最上面的文件就是占用存储空间最大的文件

  4. 【关键】删除大文件

    执行命令:git filter-repo --path '你要删除的文件路径(路径最好从上面报告内容中复制)' --invert-paths

    参数说明

    • --path:指定要删除的目录路径
    • --invert-paths:反转匹配,即删除匹配的路径,保留未匹配的路径

    --path 参数说明

    • 比如从上述例子中可以看出占用空间最大的是 native/aaa-chrome-1.0.2.tgz文件,但是这个文件现在不用了,所以我们需要删掉。

      删除历史中所有名为 native/aaa-chrome-1.0.2.tgz 的文件:
      git filter-repo --path native/aaa-chrome-1.0.2.tgz --invert-paths

    • 删除以后会发现可能还有native/aaa-chrome-1.1.22.tgz的文件也需要删除,这时候就可以 使用通配符删除,native/* 表示删除native/下所有文件:
      git filter-repo --path-glob 'native/*' --invert-paths

    • 也可直接删除native文件夹及内部文件的资源和历史记录:
      git filter-repo --path 'native/' --invert-paths

    • 清理多个文件可多次执行此命令以后再执行后续命令

  5. 使所有引用日志(reflog)立即过期 (以便后续的垃圾回收可以清理这些日志)

    执行命令:git reflog expire --expire=now --all

    参数说明:

    • --expire=now:将所有引用日志标记为立即过期
    • --all:对所有引用(分支、标签等)生效
  6. 执行垃圾回收,清理未使用的对象并优化仓库

    执行命令:git gc --aggressive --prune=now

    参数说明:

    • --aggressive:执行更彻底的优化(耗时较长)
    • --prune=now:立即清理所有过期的对象

原生清理

使用git提供的原生方法清理,该方式效率较低,尝试过清理一个文件需要大概20+分钟,非常耗时。这里我还是列出来,方便不便安装工具的掘友使用。

清理步骤
  1. 首先在【项目根路径】打开命令行终端

  2. 使用git原生命令分析git中的前20大对象

    执行命令: git verify-pack -v .git/objects/pack/pack-*.idx | sort -k 3 -g | tail -20

    查询结果大致如下

    对象hash值 | 对象类型 | 对象原始大小 | 对象在pack文件中的压缩后大小 | 对象在pack文件中的偏移量

    f7c7cb9fe6dea5fa4b295aeae2b14f827798abd2 blob 278508674 278592188 28110370

    9af39b00b682139afe4f2a139709f8f3997413a9 blob 304147466 304238811 2471683414

    165e05b191e3c36fb5ba6fdfad5537f9187b005f blob 386827775 386943760 1108838686

    50d86002dd0e931f881daa13580b29e81dbd1c3c blob 386827790 386943774 2084739640

    8582ef8ccb42a9731d98d5029cd15c730ce89889 blob 386886524 387002668 574217690

    其中对象类型包含:

    • blob:文件内容
    • tree:目录结构
    • commit:提交记录
    • tag:标签
  3. 使用git命令查看大文件的路径

    执行命令:git rev-list --objects --all | grep 对象哈希值

    比如运行:
    git rev-list --objects --all | grep 8582ef8ccb42a9731d98d5029cd15c730ce89889

    查询结果大致如下:

    对象hash值 | 对象文件路径

    8582ef8ccb42a9731d98d5029cd15c730ce89889 native/aaa-chrome-1.1.22.tgz

  4. 【关键】删除文件并覆盖提交记录

    执行命令:git filter-branch --index-filter 'git rm --cached --ignore-unmatch 要删除的文件名'

    参数说明

    • --cached:从索引中删除文件,但保留工作目录中的文件
    • --ignore-unmatch:如果文件不存在,忽略错误(避免命令中断)
  5. 删除git在执行 filter-branch 时备份的原始引用(refs/original/),确保清理彻底

    执行命令:rm -rf .git/refs/original/

  6. 使所有引用日志(reflog)立即过期,以便后续的垃圾回收可以清理这些日志

    执行命令:git reflog expire --expire=now --all

    参数说明:

    • --expire=now:将所有引用日志标记为立即过期
    • --all:对所有引用(分支、标签等)生效
  7. 检查git仓库的完整性,并列出所有不可达的对象(即不再被任何引用指向的对象)

    执行命令:git fsck --full --unreachable

    参数说明:

    • --full:执行完整的检查
    • --unreachable:只列出不可达的对象
  8. 重新打包git对象,优化存储并删除冗余对象

    执行命令:git repack -A -d

    参数说明:

    • -A:重新打包所有对象,包括未引用的对象
    • -d:删除冗余的包文件
  9. 【关键】执行垃圾回收(Garbage Collection),清理未使用的对象并优化仓库

    执行命令:git gc --aggressive --prune=now

    参数说明:

    • --aggressive:执行更彻底的优化(耗时较长)
    • --prune=now:立即清理所有过期的对象

检查清理效果

最简单的办法就是直接查看仓库文件大小是否较清理前有显著减小,或者对比.git文件的大小。

清理前后commit的变化

清理前(有文件记录)

清理后(文件记录被改写)

查看提交记录是否完好

检查一下各个分支和tag的提交记录是否完好,可以尝试checkout某一个提交看看是否符合预期,但是本地最好不要有修改。


推送到远程

上述本地清理(任选一种方式)步骤完成之后,就需要把本地已经优化的仓库的所有修改(分支和tag中的commit被改写)推送到远程。

操作步骤

  1. 为了操作过程中出现问题可以回退,推送到远程之前,首先需要备份老仓库

    GitLab为例,点击fork: 配置信息:
    最后等待fork完成就好。

  2. 备份好仓库之后,就可以将本地优化后的仓库推送到【原始仓库】

    因为有备份,所以可以直接覆盖原始仓库代码。

    先执行命令git push --force origin --all (强制将本地仓库的分支推送到远程,覆盖远程仓库分支的历史记录)
    再执行命令git push --force origin --tags (强制将本地仓库的tag推送到远程,覆盖远程仓库tag的历史记录)

    这两条命令执行完成之后,清理就算全部完成了!

可能遇到的问题

在执行第2步git push --force origin --all的时候,可能会遇到如下报错:

scss 复制代码
 ! [remote rejected]     xxx分支 -> xxx分支 (pre-receive hook declined)
 ! [remote rejected]     xxx分支 -> xxx分支 (pre-receive hook declined)
 ! [remote rejected]     xxx分支 -> xxx分支 (pre-receive hook declined)

这通常是由于某些分支开启了分支保护导致的,需要取消分支保护后再重新推送。

GitLab为例,取消分支保护的操作(如果没有权限,可以联系仓库管理员操作):

上图操作执行完成之后再重新执行git push --force origin --allgit push --force origin --tags即可。

推送到远程成功之后,记得恢复之前的GitLab配置,如果只是打开了 Allowed to force push 的开关,则直接重新关闭就行,如果是删除了分支保护规则,则需要重新建立分支保护规则:

填写分支保护信息:

后置操作

清理完成之后需要重新再clone一下新推送上去的仓库,如果是fork的仓库也需要重新fork,操作完成后就可以在崭新的仓库中开始开发了~

以前的本地仓库就不再使用啦,用新的本地仓库啦~

清理效果

最后给大家看看我们公司项目经过我的清理以后,效果怎样吧。

公司项目文件的大小主要来自于.git文件, 除去.git文件,项目文件的实际大小只有 22 M

.git 文件大小

清理前(6.04G)

清理后(36.5M)

首次clone项目时间

清理前(53min)

清理后(7S)

总结

  • 体积缩小了169倍,从 6.04 GB 减少到 36.5 MB;
  • 清理了.git文件99.4% 的存储占用;
  • 项目拉取时间从1小时左右 缩减为10秒之内。

思考

如果项目中涉及到需要引用一些大文件,比如一些压缩包或者二进制文件之类的,最好是单独抽成一个npm包 ,不要直接放在开发的主项目中,否则会导致git保存这些大文件到.git目录,后面项目文件就会越来越大。


瘦身以后的仓库克隆对比以前真的是飞快,而且本地开发时git工具再也不卡了,非常流畅。如果你也有相同的苦恼,可以和我一样尝试清理一下~

相关推荐
五月高高30 分钟前
Idea使用git不提示账号密码登录,而是输入token问题解决
git
魏 无羡32 分钟前
idea实现git版本回退的常见场景
java·git·intellij-idea
ergevv4 小时前
不同场景下git指令的搭配
git·源代码管理·代码
nc_kai18 小时前
Android Git操作
git
GUET_一路向前18 小时前
【git】在Linux系统下clone指定分支
linux·运维·git
Arthurmoo21 小时前
Linux系统集群部署模块之Keepalived双机热备
linux·git·github
自学也学好编程1 天前
Git远程仓库与协作技巧详解
git
测试开发技术1 天前
git rm 命令与系统的 rm 命令有什么区别?
git·gitlab·github·面试题