我有一个300mb的git回购。我目前签出的文件的总大小是2 MB,其余的git回购的总大小是298 MB。这基本上是一个只有代码的回购,不应该超过几MB。
我怀疑有人不小心提交了一些大文件(视频、图像等),然后删除了它们……但不是从git,所以历史仍然包含无用的大文件。如何在git历史中找到大文件?有400多个提交,所以一个接一个的提交是不实际的。
注意:我的问题不是关于如何删除文件,而是如何在第一时间找到它。
我有一个300mb的git回购。我目前签出的文件的总大小是2 MB,其余的git回购的总大小是298 MB。这基本上是一个只有代码的回购,不应该超过几MB。
我怀疑有人不小心提交了一些大文件(视频、图像等),然后删除了它们……但不是从git,所以历史仍然包含无用的大文件。如何在git历史中找到大文件?有400多个提交,所以一个接一个的提交是不实际的。
注意:我的问题不是关于如何删除文件,而是如何在第一时间找到它。
当前回答
我无法使用最流行的答案,因为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%。
注意,脚本是单线程的。因此,在任何时候只能使用一个核心。
其他回答
Powershell解决方案的windows git,找到最大的文件:
git ls-tree -r -t -l --full-name HEAD | Where-Object {
$_ -match '(.+)\s+(.+)\s+(.+)\s+(\d+)\s+(.*)'
} | ForEach-Object {
New-Object -Type PSObject -Property @{
'col1' = $matches[1]
'col2' = $matches[2]
'col3' = $matches[3]
'Size' = [int]$matches[4]
'path' = $matches[5]
}
} | sort -Property Size -Top 10 -Descending
对于Windows,我写了一个Powershell版本的答案:
function Get-BiggestBlobs {
param ([Parameter(Mandatory)][String]$RepoFolder, [int]$Count = 10)
Write-Host ("{0} biggest files:" -f $Count)
git -C $RepoFolder rev-list --objects --all | git -C $RepoFolder cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | ForEach-Object {
$Element = $_.Trim() -Split '\s+'
$ItemType = $Element[0]
if ($ItemType -eq 'blob') {
New-Object -TypeName PSCustomObject -Property @{
ObjectName = $Element[1]
Size = [int]([int]$Element[2] / 1kB)
Path = $Element[3]
}
}
} | Sort-Object Size | Select-Object -last $Count | Format-Table ObjectName, @{L='Size [kB]';E={$_.Size}}, Path -AutoSize
}
您可能需要根据自己的情况微调它是显示kB还是MB,还是仅显示Bytes。
可能存在性能优化的潜力,因此,如果您担心这一点,可以自由地进行试验。
要获得所有更改,只需省略| Select-Object -last $Count。 要得到一个更便于机器阅读的版本,只需省略| Format-Table @{L='Size [kB]';E={$_。Size}},路径-AutoSize。
步骤1将所有sha1文件写入文本文件。
git rev-list --objects --all | sort -k 2 > allfileshas.txt
步骤2将blobs从大到小排序,并将结果写入文本文件:
git gc && git verify-pack -v .git/objects/pack/pack-*.idx | egrep "^\w+ blob\W+[0-9]+ [0-9]+ [0-9]+$" | sort -k 3 -n -r > bigobjects.txt
步骤3a结合两个文本文件,得到文件名/sha1/大小信息:
for SHA in `cut -f 1 -d\ < bigobjects.txt`; do
echo $(grep $SHA bigobjects.txt) $(grep $SHA allfileshas.txt) | awk '{print $1,$3,$7}' >> bigtosmall.txt
done;
如果您的文件名或路径名包含空格,请尝试步骤3a的这种变化。它使用cut而不是awk来获得从第7列到行尾的所需列包括空格:
for SHA in `cut -f 1 -d\ < bigobjects.txt`; do
echo $(grep $SHA bigobjects.txt) $(grep $SHA allfileshas.txt) | cut -d ' ' -f'1,3,7-' >> bigtosmall.txt
done;
现在可以查看bigtosmall.txt文件,以决定要从Git历史记录中删除哪些文件。
要执行删除(注意这部分很慢,因为它将检查历史记录中的每个提交,以获取关于您标识的文件的数据):
git filter-branch --tree-filter 'rm -f myLargeFile.log' HEAD
源
步骤1-3a是从Git历史记录中查找和清除大文件复制的
EDIT
这篇文章在2017年下半年的某个时候被删除了,但它的存档副本仍然可以使用时光倒流机访问。
如果你在Windows上,下面是一个PowerShell脚本,它将打印存储库中最大的10个文件:
$revision_objects = git rev-list --objects --all;
$files = $revision_objects.Split() | Where-Object {$_.Length -gt 0 -and $(Test-Path -Path $_ -PathType Leaf) };
$files | Get-Item -Force | select fullname, length | sort -Descending -Property Length | select -First 10
我偶然发现这个的原因和其他人一样。但是引用的脚本并不适合我。我做了一个更像是我见过的那些的混合体,它现在生活在这里- https://gitlab.com/inorton/git-size-calc