为什么我的git存储库这么大?

时间:2009-06-22 23:52:09

标签: git

145M = .git / objects / pack /

我编写了一个脚本,用于将每个提交和提交的差异大小相加,然后再从每个分支的尖端向后移动。我得到129MB,没有压缩,也没有考虑分支机构中的相同文件和分支机构之间的共同历史。

Git考虑了所有这些因素,所以我期望更小的存储库。那么为什么.git这么大呢?

我做完了:

git fsck --full
git gc --prune=today --aggressive
git repack

要回答有多少文件/提交,我有19个分支,每个文件大约有40个文件。 287次提交,发现使用:

git log --oneline --all|wc -l

不应该使用10兆字节来存储有关此内容的信息。

13 个答案:

答案 0 :(得分:140)

我使用的一些脚本:

GIT-fatfiles

git rev-list --all --objects | \
    sed -n $(git rev-list --objects --all | \
    cut -f1 -d' ' | \
    git cat-file --batch-check | \
    grep blob | \
    sort -n -k 3 | \
    tail -n40 | \
    while read hash type size; do 
         echo -n "-e s/$hash/$size/p ";
    done) | \
    sort -n -k1
...
89076 images/screenshots/properties.png
103472 images/screenshots/signals.png
9434202 video/parasite-intro.avi

如果您想要更多行,请参阅相邻答案中的Perl版本:https://stackoverflow.com/a/45366030/266720

git-eradicate(对于video/parasite.avi):

git filter-branch -f  --index-filter \
    'git rm --force --cached --ignore-unmatch video/parasite-intro.avi' \
     -- --all
rm -Rf .git/refs/original && \
    git reflog expire --expire=now --all && \
    git gc --aggressive && \
    git prune

注意:第二个脚本旨在完全从Git中删除信息(包括来自reflogs的所有信息)。请谨慎使用。

答案 1 :(得分:61)

我最近将错误的远程存储库拖到了本地(git remote add ...git remote update)。删除不需要的远程引用,分支和标签后,我的存储库中仍然有1.4GB(!)的浪费空间。我只能用git clone file:///path/to/repository克隆它来摆脱它。请注意,file://在克隆本地存储库时会产生重大影响 - 只会复制引用的对象,而不是整个目录结构。

编辑:这是Ian的一个班轮,用于重建新回购中的所有分支:

d1=#original repo
d2=#new repo (must already exist)
cd $d1
for b in $(git branch | cut -c 3-)
do
    git checkout $b
    x=$(git rev-parse HEAD)
    cd $d2
    git checkout -b $b $x
    cd $d1
done

答案 2 :(得分:60)

git gc已经执行了git repack,因此手动重新打包没有任何意义,除非您要向其传递一些特殊选项。

第一步是查看大部分空间(通常情况下)是否是对象数据库。

git count-objects -v

这应该会报告存储库中有多少个解压缩的对象,它们占用了多少空间,有多少包文件以及它们占用了多少空间。

理想情况下,在重新打包后,您将没有解压缩的对象和一个打包文件,但是当前分支不能直接引用的某些对象仍然存在并解压缩是完全正常的。

如果你有一个大包,而你想知道占用空间的是什么,那么你可以列出构成包的对象及其存储方式。

git verify-pack -v .git/objects/pack/pack-*.idx

请注意verify-pack采用索引文件而不是包文件本身。这给出了包中每个对象的报告,它的真实大小和包装大小,以及关于它是否已经“消除”的信息,如果是,那么三角链的起源。

要查看存储库中是否存在任何异常大的对象,您可以在第四列的第三列(例如| sort -k3n)上以数字方式对输出进行排序。

通过此输出,您将能够使用git show命令查看任何对象的内容,尽管无法准确查看引用该对象的存储库的提交历史记录中的位置。如果您需要这样做,请尝试this question中的内容。

答案 3 :(得分:27)

仅供参考,你最终可能会留下不需要的对象的最大原因是git维护了一个reflog。

当您意外删除主分支或以某种方式灾难性地损坏您的存储库时,reflog可以保存您的屁股。

解决这个问题的最简单方法是在压缩之前截断你的reflog(只是确保你永远不想回到reflog中的任何提交)。

git gc --prune=now --aggressive
git repack

这与git gc --prune=today的不同之处在于它会立即使整个reflog过期。

答案 4 :(得分:8)

如果要查找git存储库中占用空间的文件,请运行

POST

然后,提取占用最多空间(最后一行)的blob引用,并检查占用大量空间的文件名

git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5

这甚至可能是您使用git rev-list --objects --all | grep <reference>删除的文件,但是git会记住它,因为仍然有对它的引用,例如标记,遥控器和reflog。

一旦你知道要删除哪个文件,我建议使用git rm

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

它易于使用,只需

git forget-blob

这将从git中删除每个引用,从历史记录中的每个提交中删除blob,并运行垃圾收集以释放空间。

答案 5 :(得分:7)

如果你想看到所有blob的大小,那么来自Vi的答案的git-fatfiles脚本很可爱,但它的速度太慢而无法使用。我删除了40行输出限制,它试图使用我所有的计算机内存而不是完成。所以我重写了它:它快了几千倍,添加了功能(可选),并删除了一些奇怪的bug - 如果你对输出求和以查看文件使用的总空间,那么旧版本会给出不准确的计数。 / p>

#!/usr/bin/perl
use warnings;
use strict;
use IPC::Open2;
use v5.14;

# Try to get the "format_bytes" function:
my $canFormat = eval {
    require Number::Bytes::Human;
    Number::Bytes::Human->import('format_bytes');
    1;
};
my $format_bytes;
if ($canFormat) {
    $format_bytes = \&format_bytes;
}
else {
    $format_bytes = sub { return shift; };
}

# parse arguments:
my ($directories, $sum);
{
    my $arg = $ARGV[0] // "";
    if ($arg eq "--sum" || $arg eq "-s") {
        $sum = 1;
    }
    elsif ($arg eq "--directories" || $arg eq "-d") {
        $directories = 1;
        $sum = 1;
    }
    elsif ($arg) {
        print "Usage: $0 [ --sum, -s | --directories, -d ]\n";
        exit 1;
    } 
}

# the format is [hash, file]
my %revList = map { (split(' ', $_))[0 => 1]; } qx(git rev-list --all --objects);
my $pid = open2(my $childOut, my $childIn, "git cat-file --batch-check");

# The format is (hash => size)
my %hashSizes = map {
    print $childIn $_ . "\n";
    my @blobData = split(' ', <$childOut>);
    if ($blobData[1] eq 'blob') {
        # [hash, size]
        $blobData[0] => $blobData[2];
    }
    else {
        ();
    }
} keys %revList;
close($childIn);
waitpid($pid, 0);

# Need to filter because some aren't files--there are useless directories in this list.
# Format is name => size.
my %fileSizes =
    map { exists($hashSizes{$_}) ? ($revList{$_} => $hashSizes{$_}) : () } keys %revList;


my @sortedSizes;
if ($sum) {
    my %fileSizeSums;
    if ($directories) {
        while (my ($name, $size) = each %fileSizes) {
            # strip off the trailing part of the filename:
            $fileSizeSums{$name =~ s|/[^/]*$||r} += $size;
        }
    }
    else {
        while (my ($name, $size) = each %fileSizes) {
            $fileSizeSums{$name} += $size;
        }
    }

    @sortedSizes = map { [$_, $fileSizeSums{$_}] }
        sort { $fileSizeSums{$a} <=> $fileSizeSums{$b} } keys %fileSizeSums;
}
else {
    # Print the space taken by each file/blob, sorted by size
    @sortedSizes = map { [$_, $fileSizes{$_}] }
        sort { $fileSizes{$a} <=> $fileSizes{$b} } keys %fileSizes;

}

for my $fileSize (@sortedSizes) {
    printf "%s\t%s\n", $format_bytes->($fileSize->[1]), $fileSize->[0];
}

将此git-fatfiles.pl命名并运行它。要查看文件的所有修订使用的磁盘空间,请使用--sum选项。要查看相同的内容,但对于每个目录中的文件,请使用--directories选项。如果安装Number::Bytes::Human cpan模块(运行&#34; cpan Number :: Bytes :: Human&#34;),则将格式化大小:&#34; 21M /path/to/file.mp4&# 34。

答案 6 :(得分:4)

您确定只计算.pack文件而不是.idx文件吗?它们与.pack文件位于同一目录中,但没有任何存储库数据(如扩展名所示,它们只不过是相应包的索引 - 事实上,如果你知道正确的命令,你可以从包文件中轻松地重新创建它们,git本身在克隆时执行它,因为只使用本机git协议传输包文件。)

作为代表性样本,我看了一下linux-2.6存储库的本地克隆:

$ du -c *.pack
505888  total

$ du -c *.idx
34300   total

这表明大约7%的扩张应该是常见的。

还有objects/以外的文件;根据我的个人经验,indexgitk.cache往往是最大的(在我的linux-2.6存储库的克隆中共计11M)。

答案 7 :(得分:3)

.git中存储的其他git对象包括树,提交和标记。提交和标记很小,但如果您的存储库中有大量小文件,树木就会变大。你有多少个文件和多少次提交?

答案 8 :(得分:2)

您是否尝试使用git repack

答案 9 :(得分:1)

在做git filter-branch&amp; git gc你应该检查你的仓库中的标签。任何真正的系统都会对持续集成和部署等事情进行自动标记,这些标记仍然可以使用未标记的对象,因此gc无法删除它们,您仍然会想知道为什么回购的大小仍然很大。

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

答案 10 :(得分:1)

如果您意外地添加了大量文件并将其分级,则可能会发生这种情况,而不一定是提交它们。这可能发生在rails应用中,当您运行bundle install --deployment然后意外git add .然后您看到在vendor/bundle下添加的所有文件都会取消它们,但它们已经进入了git历史记录,因此您必须应用Vi's answer并将 video/parasite-intro.avi更改为vendor/bundle,然后运行他提供的第二个命令。

你可以看到与git count-objects -v的区别,在我的情况下,在应用脚本之前有一个尺寸包:52K,应用后是3.8K。

答案 11 :(得分:1)

值得检查stacktrace.log。它基本上是一个错误日志,用于跟踪失败的提交。我最近发现我的stacktrace.log是65.5GB而我的应用是66.7GB。

答案 12 :(得分:-1)

创建新分支,其中当前提交是初始提交,所有历史记录都消失了,以减少 git 对象和历史记录大小。

注意:请在运行代码前阅读注释。

  1. git checkout --orphan latest_branch
  2. git add -A
  3. git commit -a -m “初始提交消息” #提交更改
  4. git branch -D master #删除主分支
  5. git branch -m master #renaming branch as master
  6. git push -f origin master #pushes 到 master 分支
  7. git gc --aggressive --prune=all # 删除旧文件