我有一个300 MB的git repo。我目前检出的文件重2 MB,git repo重298 MB。这基本上是一个仅限代码的回购,重量不应超过几MB。
最有可能的是,有人在某个时候偶然犯了一些重文件(视频,巨大的图像等),然后将它们删除......但不是从git中删除,所以我们有无用大文件的历史记录。如何在git历史记录中追踪大文件?有400多个提交,所以一个接一个将是耗时的。
注意:我的问题 不是how to remove the file ,但如何 查找 它首先。
答案 0 :(得分:405)
此shell脚本显示存储库中的所有blob对象,从最小到最大排序。
对于我的样本仓库,它比快100倍比这里找到的其他仓库更快。 在我信赖的Athlon II X4系统上,它处理 Linux内核存储库,其中的560万个对象只需一分钟。
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
macOS用户:由于numfmt
在macOS上不可用,您可以省略最后一行并处理原始字节大小或brew install coreutils
。
要实现进一步过滤,请在sort
行之前插入以下任意一行。
要排除HEAD
中存在的文件,请插入以下行:
| grep -vF --file=<(git ls-tree -r HEAD | awk '{print $3}') \
要仅显示超出指定尺寸的文件(例如,1MiB = 2 20 B),请插入以下行:
| 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
要删除实际文件,请查看this SO question on the topic。
答案 1 :(得分:151)
我在ETH Zurich Department of Physics wiki page(靠近该页末尾)找到了单行解决方案。只需执行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中执行此操作(并处理历史记录重写)。
答案 2 :(得分:113)
我发现这个脚本在过去非常有用,用于在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的提交。
答案 3 :(得分:26)
第1步将所有文件SHA1写入文本文件:
git rev-list --objects --all | sort -k 2 > allfileshas.txt
第2步将Blob从大到小排序并将结果写入文本文件:
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 / size信息:
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 如果您的文件名或路径名包含空格,请尝试步骤3a的此变体。它使用cut
代替awk
来获取所需的列。从第7列到第1行的空格:
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
<强>来源
从Finding and Purging Big Files From Git History
复制了步骤1-3a修改强>
该文章于2017年下半年被删除,但仍可使用an archived copy of it访问Wayback Machine。
答案 4 :(得分:21)
您应该使用BFG Repo-Cleaner。
根据网站:
BFG是一种比git-filter-branch更简单,更快速的替代方案 清理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
答案 5 :(得分:8)
如果您只想要一个大文件列表,那么我想为您提供以下单行(source at renuo):
join -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内容。
答案 6 :(得分:5)
用于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
答案 7 :(得分:4)
如果你在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
答案 8 :(得分:2)
我无法使用最流行的答案,因为--batch-check
命令行切换到Git 1.8.3(我必须使用)不接受任何参数。随后的步骤已经在带有Bash 4.1.2的CentOS 6.5上进行了尝试
在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
要做:
java -jar bfg.jar -bi commits.txt <repo_dir>
问题是关于查找提交,这比查找blob还要艰巨。要知道,请继续阅读。
给出一个提交哈希,一个打印与之关联的所有对象(包括斑点)的哈希的命令是:
git ls-tree -r --full-tree <commit_hash>
因此,如果我们对仓库中的所有提交都提供了此类输出,那么给定一个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-commits.sh
的文件中,则典型的调用如下:
cat blobs.txt | find-commits.sh
如前所述,文件blobs.txt
列出了Blob哈希,每行列出一个。 create_db()
函数将所有提交列表的缓存保存在当前目录的子目录中。
我在一个带有两个Intel®Xeon®CPU E5-2620 2.00GHz处理器的系统上进行的实验的一些统计数据,该操作系统由OS展示为24个虚拟内核:
请注意,脚本是单线程的。因此,一次只能使用一个内核。
答案 9 :(得分:1)
尝试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
答案 10 :(得分:1)
对于 Windows,我编写了 this answer 的 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 或仅显示字节。
可能存在性能优化的潜力,因此如果您有此担忧,请随时进行试验。
要获取所有更改,只需省略 | Select-Object -last $Count
。
要获得更机器可读的版本,只需省略 | Format-Table @{L='Size [kB]';E={$_.Size}}, Path -AutoSize
。
答案 11 :(得分:0)
如何在git历史记录中追踪大文件?
首先分析,验证并选择根本原因。使用git-repo-analysis
提供帮助。
您还可以在BFG Repo-Cleaner生成的详细报告中找到一些值,通过使用其10MiB / s网络吞吐量克隆到Digital Ocean Droplet,可以非常快速地运行。< / p>
答案 12 :(得分:0)
我和其他人一样偶然发现了这一点。但引用的脚本对我来说并不适用。我已经制作了一个比我见过的更混合的,它现在住在这里 - https://gitlab.com/inorton/git-size-calc