我有一个300MB的git仓库。当前检出文件的总大小为2MB,剩余git仓库的总大小为298MB。这基本上是一个仅包含代码的仓库,不应该超过几MB。
我怀疑有人意外提交了一些大文件(视频、图片等),然后将它们删除……但没有从git中删除,因此历史记录仍包含无用的大文件。如何在git历史中找到这些大文件?由于有400多个提交,逐个查找不太实际。
注意:我的问题不涉及如何删除文件,而是如何首先找到它。
这个命令行脚本会显示仓库中的所有 blob 对象,并按照从最小到最大的顺序进行排序。
对于我的示例仓库来说,它的运行速度比这里找到的其他脚本快了100倍。
在我的可靠的 Athlon II X4 系统上,它可以在一分钟多一点的时间内处理包含560万个对象的Linux 内核仓库。
git rev-list --objects --all |
git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' |
sed -n 's/^blob //p' |
sort --numeric-sort --key=2 |
cut -c 1-12,41- |
$(command -v gnumfmt || echo numfmt) --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
...
0d99bb931299 530KiB path/to/some-image.jpg
2ba44098e28f 12MiB path/to/hires-image.png
bd1741ddce0d 63MiB path/to/some-video-1080p.mp4
grep -vF --file=<(git ls-tree -r HEAD | awk '{print $3}') |
awk '$2 >= 2^20' |
为了生成适合计算机进一步处理的输出,可以省略基本脚本的最后两行。这些行负责格式化。这样你就会得到类似下面的内容:
...
0d99bb93129939b72069df14af0d0dbda7eb6dba 542455 path/to/some-image.jpg
2ba44098e28f8f66bac5e21210c2774085d2319b 12446815 path/to/hires-image.png
bd1741ddce0d07b72ccf69ed281e09bf8a2d0b2f 65183843 path/to/some-video-1080p.mp4
关于实际的文件删除,请查看这个关于该主题的Stack Overflow问题。
这个脚本显示的是每个文件在工作目录中的大小。如果你想查看一个文件在未签出状态下占用多少空间,可以使用%(objectsize:disk)
代替%(objectsize)
。然而,请注意这个度量指标也有其局限性,正如文档中所提到的。
有时候仅仅列出大文件的列表还不足以找出问题所在。例如,你可能无法发现包含大量小文件的目录或分支。
所以,如果这里的脚本对你来说不够好(而且你有一个相当新的git版本),可以尝试一下git-filter-repo --analyze
或者git rev-list --disk-usage
(示例)。brew install coreutils
,然后将 cut
替换为 gcut
,将 numfmt
替换为 gnumfmt
。 - Nick Sweetingobjectsize:disk
而不是 objectsize
。 - Victor Yarema我在ETH Zurich物理系维基页面上找到了一种单行解决方案(页面接近末尾)。只需运行git gc
即可删除过时的垃圾,然后……
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中完成这项操作(并且也处理历史重写)。
git rev-list --objects --all | grep -E `git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}' | sed ':a;N;$!ba;s/\n/|/g'`
- Throctukes我曾经发现这个脚本非常有用,可以用来查找git仓库中的大文件(包括不明显的):
#!/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的提交。
第一步:将所有文件的SHA1值写入文本文件:
git rev-list --objects --all | sort -k 2 > allfileshas.txt
步骤二:将 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
第3步a:将两个文本文件合并,以获取文件名/ 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;
步骤3b 如果您的文件名或路径名包含空格,请尝试Step 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历史记录中删除哪些文件。
步骤4执行删除操作(请注意,此部分速度较慢,因为它将检查历史记录中的每个提交,以获取有关您确定的文件的数据):
git filter-branch --tree-filter 'rm -f myLargeFile.log' HEAD
来源
步骤1-3a是从从Git历史记录中查找和清除大文件中复制的。
编辑
git gc && join -e ERROR -a 2 -j 1 -o 2.1,2.3,1.2 --check-order <( git rev-list --objects --all | sort -k 1 ) <( git verify-pack -v .git/objects/pack/pack-*.idx | gawk '( NF == 5 && $2 == "blob" ){print}' | sort -k1 ) | sort -k2gr
- Iwan Aucampjoin -t' ' -e ERROR -a 2 -j 1 -o 2.1,2.3,1.2 --check-order <( git rev-list --objects --all | sed 's/[[:space:]]/\t/' | sort -k 1 ) <( git verify-pack -v .git/objects/pack/pack-*.idx | gawk '( NF == 5 && $2 == "blob" ){print}' | sort -k1 | sed 's/[[:space:]]\+/\t/g' ) | sort -k2gr | less
。请注意,在 join -t'
后必须使用 CTRL+V <TAB> 输入实际的 TAB 字符,参见 http://geekbraindump.blogspot.ru/2009/04/unix-join-with-tabs.html。 - Nickolay$'\t'
应该会给你一个制表符。echo -n $'\t' | xxd -ps
-> 09
- Iwan Aucamp您应该使用BFG Repo-Cleaner。
根据该网站:
BFG是一个更简单,更快速的选项,可用于清除Git存储库历史记录中的不良数据:
- 删除超大文件
- 删除密码、凭据和其他私有数据
减小存储库大小的传统过程是:
git clone --mirror git://example.com/some-big-repo.git
java -jar bfg.jar --strip-biggest-blobs 500 some-big-repo.git
cd some-big-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push
--strip-biggest-blobs 500
是什么意思? - 2540625join -o "1.1 1.2 2.3" <(git rev-list --objects --all | sort) <(git verify-pack -v objects/pack/*.idx | sort -k3 -n | tail -5 | sort) | sort -k3 -n
输出将为:
commit file name size in bytes
72e1e6d20... db/players.sql 818314
ea20b964a... app/assets/images/background_final2.png 6739212
f8344b9b5... data_test/pg_xlog/000000010000000000000001 1625545
1ecc2395c... data_development/pg_xlog/000000010000000000000001 16777216
bc83d216d... app/assets/images/background_1forfinal.psd 95533848
列表中的最后一项指向您的git历史记录中最大的文件。
您可以使用此输出来确保在使用BFG时,不会删除您在历史记录中需要的内容。
请注意,您需要使用--mirror
克隆存储库才能使其正常工作。
1.1、1.2、2.3
这些数字是用来做什么的? - ympostorjoin
命令只取文件路径中由空格分隔的第一个“单词”。 - villapx$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
对于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}}, Path -AutoSize
。
尝试使用git ls-files | xargs du -hs --threshold=1M
命令。
我们在CI流水线中使用以下命令,如果在git存储库中发现任何大文件,则会停止运行:
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
针对 Windows Git 的 Powershell 解决方案,查找最大的文件:
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