[分享] 极狐GitLab仓库瘦身

GitLab仓库瘦身

Git 仓库瘦身概述

Git仓库随着时间推移会变得越来越大,比如很多比较大的文件加入Git仓库时,可能引起以下问题:

  • 下载仓库越来越慢,因为每个人都要下载文件
  • 占用服务器大量存储空间
  • 触发Git仓库存储限制

重写Git仓库可以移除不想要的仓库历史,减少仓库大小。

工具方面,我们推荐使用git filter-repo. 不推荐使用git filter-branch 或者 BFG.

git filter-repo可以实现以下目的:

  • 剥离大文件(或大目录或大扩展名)

  • 按路径剥离不需要的文件

  • 提取想要的路径及其历史(剥离所有其他内容)

  • 重组文件布局(例如将所有文件移动到一个子目录中以准备与另一个 repo 合并,使子目录成为新的顶级目录,或将两个具有独立文件名的目录合并到一个目录中)

  • 重命名标签(也经常为与另一个 repo 合并做准备)

  • 替换或删除敏感文本,例如密码

  • 使用户名与电子邮件的邮件映射关系重写永久化

  • 使移植或替换引用(refs)永久化

  • 重写commits消息

特别注意: 重写仓库是一种具有破坏性的操作,不应轻易使用,建议操作之前备份仓库,最好的仓库备份方式就是导出项目

从仓库历史删除文件

想要对仓库瘦身,您必须首先从由 GitLab 自动创建的分支、标签和其他内部引用 (refs) 中删除对大文件的引用。这些引用(refs)包括:

  • refs/merge-requests/* 用于合并请求。
  • refs/pipelines/* 用于管道。
  • refs/environments/* 用于环境。
  • refs/keep-around/* 隐藏的refs,防止数据库中被删除

我们可以首先将项目导出,本地删除这些 refs,然后推送回去。

操作步骤:

  • 1.安装git filter-repo;

  • 2.备份和导出项目;

  • 3.解压文件:

    tar xzf project-backup.tar.gz
    

    解压后的文件夹中包含一个 project.bundle 文件,它是由 git bundle 创建的。

  • 4.使用 --bare--mirror 参数从bundle中克隆仓库的新副本:

    git clone --bare --mirror project.bundle
    
  • 5.进入project.git文件夹。理论上,使用git filter-repo可以从仓库的历史(包括master)中清除任何的文件。
    由于我们试图删除内部引用(refs),所以我们依靠每次运行生成的commit-map来告诉我们要删除哪些内部引用。

    git filter-repo每次运行都会生成一个commit-map文件,并覆盖上一次生成的commit-map

    比如,在所有分支中(包括master),清理10M大小以上的文件,使用参数--strip-blobs-bigger-than

    git filter-repo --strip-blobs-bigger-than 10M
    

    比如,指定大文件的具体路径,使用参数--path--invert-paths

    git filter-repo --path path/to/big/file.m4v --invert-paths
    

    参考git filter-repo官方文档,可以获取更多清理方式。

  • 6.配置remote仓库地址。

    由于从bundle文件克隆的副本使用的origin指向了本地,因此需要先删除,后指定远程仓库地址。

    git remote remove origin
    git remote add origin https://gitlab.example.com/<namespace>/<project_name>.git 
    
  • 7.推送变更,覆盖远程所有分支。

    git push origin --force 'refs/heads/*'
    

    如果远程分支是受保护的,请先禁用保护,然后推送,然后重新启用分支保护。

  • 8.从标签release中删除大文件。

    git push origin --force 'refs/tags/*'
    

    如果标签是受保护的,请先禁用保护,然后推送,然后重新启用标签保护。

  • 9.防止commit中存在死链。

    git push origin --force 'refs/replace/*'
    
  • 10.运行仓库清理。

仓库清理

仓库清理,就是允许上传一个包含目标对象的TXT文件。GitLab会清理这些目标对象的内部引用(refs)。你可以使用git filter-repo生成这样的文件(commit-map文件),用于清理。

从13.6开始,安全的清理仓库需要确保在操作期间,没有git push等写操作,确保仓库是只读的,否则清理仓库的请求将提交失败。

清理仓库步骤:

  • 打开项目

  • 打开Settings > Repository

  • 上传清单文件,比如git filter-repo生成的commit-map文件;
    如果commit-map文件大小超过250KB或者3000行,那么可以拆分文件并一个一个上传:

    split -l 3000 filter-repo/commit-map filter-repo/commit-map-
    
  • 点击Start cleanup
    页面会提示:Repository cleanup has started. You will receive an email once the cleanup operation is complete.

将执行的动作:

  • 移除任何对旧commits的内部引用。
  • 运行git gc --prune=30.minutes.ago删除未引用的对象,临时重新打包仓库会导致仓库的大小显着增加,因为在创建新的打包文件之前不会删除旧的打包文件。
  • 取消连接到项目的任何未使用的 LFS 对象,释放存储空间。
  • 重新计算磁盘上仓库的大小。

清理完成后,GitLab 会发送一封电子邮件通知,其中包含重新计算的仓库大小。

如果仓库大小没有减少,这可能是由于在过去 30 分钟内发生的 Git 操作中引用了松散对象而导致的。 在仓库休眠至少 30 分钟后,尝试重新运行这些步骤。

注意事项:

  • 项目统计信息是有缓存的,所以你可能需要等待 5-10 分钟才能看到存储使用率的降低。
  • 清理会修剪超过 30 分钟的松散对象,也就是不会立即删除过去 30 分钟内添加或引用的对象。 如果你有权限访问 Gitaly 服务器,你可以避开延迟,并运行 git gc --prune=now 立即删除所有松散的对象。
  • 这个操作从 GitLab 缓存和数据库中删除了一些重写提交的副本,但是覆盖范围仍然存在许多差距,并且一些副本可能会无限期地持续存在。

存储限制

仓库大小限制:

  • 可以由管理员在自我管理的实例上设置。

当项目达到其大小限制时,不能:

  • 推送到项目。
  • 创建一个新的合并请求。
  • 合并现有的合并请求。
  • 上传 LFS 对象。

您仍然可以:

  • 产生新问题。
  • 克隆项目。

如果超过了仓库大小限制,你可以:

  • 删除一些数据。
  • 进行新的提交。
  • 推回仓库。

如果这些操作还不够,你还可以:

  • 将一些 blobs 移动到 LFS。
  • 从历史中删除一些旧的依赖项更新。

删除commits中的文件实际上并没有减少仓库的大小,因为较早的commits和 blobs 仍然存在。你必须改写仓库历史,比如使用git filter-repo工具。

在 GitLab 服务端运行 git gc 之前,"删除"的commits和 blobs 仍然存在,你还必须将重写的历史记录推送到 GitLab。如果您已经触发最大大小限制,那可能会失败。

此过程不适用于从仓库中删除密码或密钥等敏感数据。 有关commits的信息(包括文件内容)缓存在数据库中,即使从存储库中删除它们也仍然可见。