如何从 Git 存储库中删除未引用的 blobs

我有一个 GitHub 存储库,它有两个分支—— 师父释放

释放分支包含的二进制发行文件占用了非常大的存储库大小(超过250 MB) ,因此我决定清理这些文件。

首先,我通过 git push origin :release删除了远程发布分支。

然后我删除了本地 释放分支。首先我尝试了 git branch -d release,但 Git 说的是 “ error: 分支‘ release’不是当前 HEAD 的祖先。”,这是真的,所以我做了 git branch -D release强制删除它。

但是我的存储库规模,无论是在本地还是在 GitHub 上,仍然是巨大的。因此,我运行了通常的 Git 命令列表,比如 git gc --prune=today --aggressive,但是没有任何收获。

通过遵循查尔斯 · 贝利在 SO1029969上的指示,我得到了一个 SHA-1散列列表,其中包含最大的斑点。然后我使用 SO460331的脚本来找到斑点... ... 最大的五个斑点不存在,但是找到了更小的斑点,所以我知道脚本正在工作。

我认为这些博客是来自发布分支的二进制文件,在删除该分支之后,它们不知何故被遗留了下来。怎样才能摆脱他们?

114474 次浏览

尝试使用 Git 过滤器分支-它不会删除大的文件块,但是它可以删除整个存储库中您指定的大文件。对我来说,它将存储库大小从几百 MB 减少到12 MB。

正如在 这么回答中提到的,git gc实际上可以增加回购的大小!

参见 这根线

现在,git 有了一个安全机制,当运行“ git gc”时,没有可以立即删除未引用的对象。
默认情况下,未引用的对象保留2周。这样做是为了方便您恢复意外删除的分支或提交,或者避免刚刚创建但尚未被引用的对象可能被并行运行的“ git gc”进程删除的竞争。

因此,为了给被打包但未被引用的对象一段宽限期,重新打包过程将那些未被引用的对象从打包中推出,使它们变成松散的形式,这样它们就可以被老化并最终被修剪。
不过,未引用的对象通常不会太多。拥有404855个未引用的对象是相当多的,首先通过克隆发送这些对象是愚蠢的,完全浪费了网络带宽。

不管怎样... ... 要解决你的问题,你只需要运行带有 --prune=now参数的‘ git gc’来禁用这个宽限期,并立即删除那些未被引用的对象(只有当没有其他 git 活动在同一时间发生时才是安全的,这在工作站上应该很容易确保)。

顺便说一下,使用‘ git gc --aggressive’和后来的 git 版本(或‘ git repack -a -f -d --window=250 --depth=250’)

返回文章页面 同样的帖子提到:

 git config pack.deltaCacheSize 1

这将增量缓存大小限制为一个字节(有效地禁用它) ,而不是默认的0,这意味着无限大。这样,我就能够在一个拥有4GB RAM 的 x86-64系统上使用上面的 git repack命令重新打包存储库,并使用4个线程(这是一个四核心)。但常驻内存使用量增长到近3.3 GB。

如果你的机器是 SMP,你没有足够的内存,那么你可以减少线程的数量到只有一个:

git config pack.threads 1

此外,还可以进一步将 --window-memory argument的内存使用限制为“ git repack”。
例如,使用 --window-memory=128M应该对 delta 保持一个合理的上限 搜索内存的使用,虽然这可能会导致不太理想的增量匹配,如果回购 包含大量的大文件。


在过滤器分支前端,可以考虑(谨慎地) 这个剧本

#!/bin/bash
set -o errexit


# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2


if [ $# -eq 0 ]; then
exit 0
fi


# make sure we're at the root of git repo
if [ ! -d .git ]; then
echo "Error: must run this script from the root of a git repository"
exit 1
fi


# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch $files" HEAD


# remove the temporary history git-filter-branch otherwise leaves behind for a long time
rm -rf .git/refs/original/ && git reflog expire --all &&  git gc --aggressive --prune

或低水平 git prune --expire now

每次 移动时,Git 都会在 reflog中跟踪它。如果删除了提交,那么仍然存在“悬空提交”,因为它们仍然被 reflog引用约30天。这是意外删除提交时的安全网。

您可以使用 git reflog命令来删除特定的提交、重新打包等,或者只使用高级命令:

git gc --prune=now

在执行 git filter-branchgit gc之前,您应该检查存储库中的标记。任何真正的系统,如 连续整合部署自动标记将使不需要的对象仍然引用这些标记,因此 gc不能删除它们,你仍然会想知道为什么存储库的大小仍然如此之大。

摆脱所有不需要的东西的最好方法是运行 git-filtergit gc,然后将 master 推到一个新的裸仓库。新的裸仓库将拥有清理过的树。

有时,“ gc”没有多大用处的原因是有一个未完成的基于旧提交的 rebase 或存储。

我将向您展示这个有用的命令“ Git-gc-all”,它保证可以删除 所有的 Git 垃圾,直到它们可能出现额外的配置变量:

git -c gc.reflogExpire=0 -c gc.reflogExpireUnreachable=0 -c gc.rerereresolved=0 \
-c gc.rerereunresolved=0 -c gc.pruneExpire=now gc

您可能还需要首先运行以下代码:

git remote rm origin
rm -rf .git/refs/original/ .git/refs/remotes/ .git/*_HEAD .git/logs/
git for-each-ref --format="%(refname)" refs/original/ |
xargs -n1 --no-run-if-empty git update-ref -d

您可能还需要删除一些标签:

git tag | xargs git tag -d

要添加另一个提示,不要忘记在使用 Git GC之前使用 远程修剪删除远程的过时分支。

你可以看到他们与 Git Branch-a

当您从 GitHub 和分支存储库获取数据时,它通常非常有用..。

你可以(作为 在这个答案中详细说明) 永久删除仅在 reflog 中引用的所有内容

注意 : 这将删除您可能希望保留的许多对象: 隐藏; 不在任何当前分支中的旧历史记录; 等等。确保这是你想要的。

使 reflog 过期,然后删除分支以外的所有对象:

git reflog expire --expire-unreachable=now --all
git gc --prune=now

git reflog expire --expire-unreachable=now --all删除 reflog中所有不可达提交的引用。

git gc --prune=now删除提交本身。

注意 : 仅使用 git gc --prune=now将不起作用,因为这些提交仍然在 reflog 中引用。因此,清除 reflog 是强制性的。还要注意,如果使用 rerere,它还有其他引用没有被这些命令清除。有关详细信息,请参阅 git help rerere。此外,由本地或远程分支或标记引用的任何提交都不会被删除,因为这些被 git 视为有价值的数据。

你可以使用 git forget-blob

用法很简单:

git forget-blob file-to-forget

你可以在 Rel = “ nofollow noReferrer”> 使用“ Git forget-blob”完全从 Git 存储库中删除一个文件中获得更多的信息。

它将从历史记录中的所有提交、 reflog、标记等中消失。

我时不时会遇到同样的问题,每次我都要回到这篇文章和其他文章。这就是为什么我自动化了这个过程。

学分归 比如山姆 · 沃特金斯

试试这个 大意的方法:

git gc --prune="0 days"