如何从我的git仓库中删除未引用的blob


124

我有一个有两个分支的GitHub存储库-master和release。

版本分支包含二进制分发文件,这些文件导致很大的回购大小(> 250MB),因此我决定清理这些内容。

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

然后,我删除了本地发行分支。首先我尝试了git branch -d release,但是git说“错误:分支'release'不是您当前HEAD的祖先。” 的确如此,因此我确实git branch -D release强行将其删除。

但是,无论是本地还是在GitHub上,我的存储库规模仍然很大。因此,然后我没有遇到git gc --prune=today --aggressive任何麻烦就浏览了git命令的常用列表,例如。

通过遵循SO 1029969上 Charles Bailey的指示,我可以获得最大斑点的SHA1列表。然后,我使用SO 460331中的脚本 找到了Blob……尽管找到了较小的Blob,但五个最大的不存在,因此我知道脚本正在工作。

我认为这些博客是release分支的二进制文件,在删除该分支后,它们以某种方式消失了。摆脱它们的正确方法是什么?


您正在使用哪个版本的Git?您是否尝试过stackoverflow.com/questions/1106529/…
VonC

git版本1.6.2.3我尝试了gc和prune w /各种参数。我没有尝试重新打包-a -d -l,只是运行它,没有任何变化。
kkrugler 2009年

2
新信息-来自GitHub的新克隆不再包含未引用的Blob,从250MB减少到“仅” 84MB。
kkrugler 2009年

Answers:


219

...并且事不宜迟,我可以向您介绍这个有用的命令“ 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很复杂!

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

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

git tag | xargs git tag -d

我把所有这些都放在脚本中:git-gc-all-ferocious


1
有趣。一个很好的替代我更一般的答案。+1
VonC

10
这值得更多的投票。终于摆脱了许多其他方法保留的git对象。谢谢!
让-菲利普·佩莱

1
已投票。哇,我不知道我刚刚做了什么,但似乎可以解决很多问题。您能详细说明它的作用吗?我感觉它清除了我所有的东西objects。这些是什么,为什么它们(显然)无关紧要?
Redsandro 2014年

1
@Redsandro,据我所知,那些“ git rm origin”,“ rm”和“ git update-ref -d”命令删除了对远程等旧提交的引用,这可能会阻止垃圾回收。“ git gc”的选项告诉它不要保留各种旧提交,否则它将保留一段时间。例如,gc.rereresolved用于“您先前解析的冲突合并记录”,默认情况下保留60天。这些选项在git-gc联机帮助页中。我不是git方面的专家,也不完全知道所有这些功能在做什么。我从手册页中找到了它们,然后将.git grepping为提交引用。
山姆·沃特金斯2014年

1
git对象是git存储库中的压缩文件或树,或已提交,包括历史记录中的旧内容。git gc清除不需要的对象。它保留了当前回购及其历史记录仍需要的对象。
山姆·沃特金斯2014年

81

如上所述这里如果你想永久删除只能通过引用日志引用的一切,只需使用

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将其视为有价值的数据。


14
它奏效了,但是在某种程度上我失去了自己保存的资料(对我而言没有什么大不了的,对其他人来说只是警告)
Amro

1
为什么不进取?
JoelFan

2
我认为这个答案需要明确的警告,最好在顶部。我的编辑建议被拒绝,因为我想我应该在评论中建议给作者?请接受此编辑stackoverflow.com/review/suggested-edits/26023988或以您自己的方式添加警告。另外,这会丢弃所有藏匿处。警告中也应提及这一点!
Inigo

我使用git版本2.17进行了测试,隐藏的提交不会被上述命令删除。您确定没有运行任何其他命令吗?
Mikko Rantalainen

1
git fetch --prune进一步减小大小,因为删除了本地Blob。
hectorpal

33

本SO回答所述git gc实际上可以增加回购的大小!

另请参阅此线程

现在git具有一种安全机制,可以在运行' ' 时不立即删除未引用的对象git gc
默认情况下,未引用的对象会保留2周。这是为了使您能够轻松恢复意外删除的分支或提交,或者避免git gc在并行运行的' '进程中删除正在创建但尚未引用的刚刚创建的对象的竞争。

因此,为了给已打包但未引用的对象留出宽限期,重新打包过程会将那些未引用的对象从包中推送到它们的松散形式,以便可以对其进行老化并最终对其进行修剪。
成为非引用的对象通常并不多。有404855个未引用的对象很多,而通过克隆首先将那些对象发送给这些对象是愚蠢的,并且完全浪费了网络带宽。

无论如何...要解决您的问题,您只需要git gc使用--prune=now参数运行' ' 以禁用该宽限期并立即清除那些未引用的对象(仅在没有其他git活动同时发生时才是安全的)易于确保在工作站上)。

顺便说一句,使用' git gc --aggressive'和更高版本的git版本(或' git repack -a -f -d --window=250 --depth=250')

同一个线程中提到

 git config pack.deltaCacheSize 1

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

如果您的计算机是SMP并且没有足够的RAM,则可以将线程数减少到一个:

git config pack.threads 1

此外,您还可以通过--window-memory argument将“ git repack” 限制为内存使用。
例如,使用--window-memory=128M应该在增量搜索内存使用上保持合理的上限,尽管如果存储库包含大量大文件,则这可能会导致最佳增量匹配变差。


在分支过滤器的前面,您可以(谨慎)考虑此脚本

#!/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

stackoverflow.com/questions/359424/…也是该filter-branch命令用法的良好起点。
VonC

VonC大家好-NI运气不错,尝试过git gc prune = now。它确实看起来像一个git bug,在分支删除后,我在本地处理了未引用的blob,但是这些并没有与GitHub存储库的新克隆一起出现,所以这只是一个本地存储库问题。但是我还有其他要清除的文件,因此您上面引用的脚本很棒-谢谢!
kkrugler


12

每当您的HEAD移动时,git就会在中进行跟踪reflog。如果删除了提交,则仍然会有“悬空提交”,因为〜30 reflog天内仍会引用它们。当您意外删除提交时,这就是安全网。

您可以使用git reflog命令删除特定的提交,重新打包等,也可以仅使用高级命令:

git gc --prune=now


2

尝试使用git-filter-branch-它不会删除大的Blob,但是可以删除您从整个存储库中指定的大文件。对我来说,它可以将回购大小从数百MB减少到12 MB。


6
现在是一个可怕的命令:)当我的git-fu感觉更强大时,我将不得不尝试一下。
kkrugler 2009年

你可以再说一遍。我总是警惕任何操纵存储库历史记录的命令。当多个人从该存储库中推入和拉出并且突然有git期望的一堆对象不存在时,事情往往会出错。
乔纳森·杜马因

1

有时,“ gc”没有太大用处的原因是基于旧提交的未完成的rebase或stash。


或HEAD,ORIG_HEAD,FETCH_HEAD,reflog或git自动跟上的其他东西引用旧提交,以确保它永远不会丢失任何有价值的东西。如果您真的想失去所有这些,则必须付出更大的努力。
Mikko Rantalainen

1

要添加另一个提示,请不要忘记在使用git gc之前使用git remote prune删除远程对象的过时分支。

你可以用git branch -a看到它们

当您从github和分叉的存储库获取数据时,这通常很有用...


1

在执行git filter-branch和之前git gc,您应该检查存储库中存在的标签。任何具有自动标记功能(例如持续集成和部署)的真实系统都会使这些标记仍引用不需要的对象,因此gc无法删除它们,并且您仍然会怀疑为什么回购的规模仍然很大。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.