我有一个300mb的git回购。我目前签出的文件的总大小是2 MB,其余的git回购的总大小是298 MB。这基本上是一个只有代码的回购,不应该超过几MB。

我怀疑有人不小心提交了一些大文件(视频、图像等),然后删除了它们……但不是从git,所以历史仍然包含无用的大文件。如何在git历史中找到大文件?有400多个提交,所以一个接一个的提交是不实际的。

注意:我的问题不是关于如何删除文件,而是如何在第一时间找到它。


当前回答

试试git ls-files | xargs du -hs——threshold=1M。

我们在CI管道中使用下面的命令,如果它在git repo中发现任何大文件,它就会停止:

test $(git ls-files | xargs du -hs --threshold=1M 2>/dev/null | tee /dev/stderr | wc -l) -gt 0 && { echo; echo "Aborting due to big files in the git repository."; exit 1; } || true

其他回答

像这样使用git-filter-repo的——analyze特性:

$ cd my-repo-folder
$ git-filter-repo --analyze
$ less .git/filter-repo/analysis/path-all-sizes.txt

我发现这个脚本在过去在git存储库中查找大型(和不明显的)对象非常有用:

http://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/


#!/bin/bash
#set -x 
 
# Shows you the largest objects in your repo's pack file.
# Written for osx.
#
# @see https://stubbisms.wordpress.com/2009/07/10/git-script-to-show-largest-pack-objects-and-trim-your-waist-line/
# @author Antony Stubbs
 
# set the internal field separator to line break, so that we can iterate easily over the verify-pack output
IFS=$'\n';
 
# list all objects including their size, sort by size, take top 10
objects=`git verify-pack -v .git/objects/pack/pack-*.idx | grep -v chain | sort -k3nr | head`
 
echo "All sizes are in kB's. The pack column is the size of the object, compressed, inside the pack file."
 
output="size,pack,SHA,location"
allObjects=`git rev-list --all --objects`
for y in $objects
do
    # extract the size in bytes
    size=$((`echo $y | cut -f 5 -d ' '`/1024))
    # extract the compressed size in bytes
    compressedSize=$((`echo $y | cut -f 6 -d ' '`/1024))
    # extract the SHA
    sha=`echo $y | cut -f 1 -d ' '`
    # find the objects location in the repository tree
    other=`echo "${allObjects}" | grep $sha`
    #lineBreak=`echo -e "\n"`
    output="${output}\n${size},${compressedSize},${other}"
done
 
echo -e $output | column -t -s ', '

这将给你blob的对象名称(SHA1sum),然后你可以使用这样的脚本:

哪个提交有这个斑点?

…来查找指向这些blob的提交。

我无法使用最流行的答案,因为Git 1.8.3(我必须使用)的——batch-check命令行开关不接受任何参数。下面的步骤已经在CentOS 6.5和Bash 4.1.2上进行了尝试

关键概念

在Git中,术语blob表示文件的内容。请注意,提交可能会更改文件或路径名的内容。因此,根据提交的不同,同一个文件可以引用不同的blob。在一次提交中,某个文件可能是目录层次结构中的最大文件,而在另一次提交中则不是。因此,寻找大提交而不是大文件的问题将问题置于正确的角度。

对于没有耐心的人

按大小降序打印blob列表的命令是:

git cat-file --batch-check < <(git rev-list --all --objects  | \
awk '{print $1}')  | grep blob  | sort -n -r -k 3

样例输出:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e blob 305971200
7c357f2c2a7b33f939f9b7125b155adbd7890be2 blob 289163620

要删除这样的斑点,使用BFG Repo Cleaner,如在其他答案中提到的。给定一个只包含blob哈希值的文件blobs.txt,例如:

3a51a45e12d4aedcad53d3a0d4cf42079c62958e
7c357f2c2a7b33f939f9b7125b155adbd7890be2

Do:

java -jar bfg.jar -bi blobs.txt <repo_dir>

这个问题是关于查找提交的,这比查找blob要复杂得多。要知道,请继续往下读。

进一步的工作

给定一个提交哈希值,打印与之相关的所有对象(包括blob)的哈希值的命令是:

git ls-tree -r --full-tree <commit_hash>

所以,如果我们在repo中所有提交都有这样的输出,那么给定一个blob哈希,一堆提交就是那些匹配任何输出的。这个想法被编码在下面的脚本中:

#!/bin/bash
DB_DIR='trees-db'

find_commit() {
    cd ${DB_DIR}
    for f in *; do
        if grep -q $1 ${f}; then
            echo ${f}
        fi
    done
    cd - > /dev/null
}

create_db() {
    local tfile='/tmp/commits.txt'
    mkdir -p ${DB_DIR} && cd ${DB_DIR}
    git rev-list --all > ${tfile}

    while read commit_hash; do
        if [[ ! -e ${commit_hash} ]]; then
            git ls-tree -r --full-tree ${commit_hash} > ${commit_hash}
        fi
    done < ${tfile}
    cd - > /dev/null
    rm -f ${tfile}
}

create_db

while read id; do
    find_commit ${id};
done

如果内容保存在一个名为find-commit .sh的文件中,那么典型的调用将如下所示:

cat blobs.txt | find-commits.sh

和前面一样,文件blobs.txt列出了blob哈希值,每行一个。create_db()函数将所有提交清单的缓存保存在当前目录的子目录中。

我在一个系统上做了一些实验,这个系统有两个Intel(R) Xeon(R) CPU E5-2620 2.00GHz处理器,由操作系统提供24个虚拟核:

在repo中提交的总数=近11000 文件创建速度= 126个文件/秒。该脚本每次提交创建一个文件。这只在第一次创建缓存时发生。 缓存创建开销= 87秒。 平均搜索速度= 522次提交/秒。缓存优化使运行时间减少了80%。

注意,脚本是单线程的。因此,在任何时候只能使用一个核心。

我在苏黎世联邦理工学院物理系的维基页面上找到了一个简单的解决方案(接近该页的末尾)。只要做个git垃圾收集,把垃圾清除掉,然后

git rev-list --objects --all \
  | grep "$(git verify-pack -v .git/objects/pack/*.idx \
           | sort -k 3 -n \
           | tail -10 \
           | awk '{print$1}')"

将为您提供存储库中最大的10个文件。

现在还有一个更懒的解决方案,GitExtensions现在有一个插件,可以在UI中做到这一点(以及处理历史重写)。

我偶然发现这个的原因和其他人一样。但是引用的脚本并不适合我。我做了一个更像是我见过的那些的混合体,它现在生活在这里- https://gitlab.com/inorton/git-size-calc