如何在git历史中查找/识别大型提交?

313 git

我有一个300 MB的git repo.我目前检出的文件重2 MB,git repo重298 MB.这基本上是一个仅限代码的回购,重量不应超过几MB.

最有可能的是,有人在某个时候偶然犯了一些重文件(视频,巨大的图像等),然后将它们删除......但不是从git中删除,所以我们有无用大文件的历史.如何在git历史记录中追踪大文件?有400多个提交,所以一个接一个将是耗时的.

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

rap*_*sse 551

一个非常快速的贝壳单线

此shell脚本显示存储库中的所有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
Run Code Online (Sandbox Code Playgroud)

当你运行上面的代码时,你会得到很好的人类可读输出,如下所示:

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4
Run Code Online (Sandbox Code Playgroud)

macOS用户:由于numfmt在macOS上不可用,您可以省略最后一行并处理原始字节大小或brew install coreutils.

过滤

要实现进一步过滤,请在行之前sort插入以下任何.

排除存在的文件HEAD,请插入以下行:

| grep -vF --file=<(git ls-tree -r HEAD | awk '{print $3}') \
Run Code Online (Sandbox Code Playgroud)

仅显示超过给定大小的文件(例如1 MiB = 2 20  B),请插入以下行:

| awk '$2 >= 2^20' \
Run Code Online (Sandbox Code Playgroud)

计算机输出

要生成更适合计算机进一步处理的输出,请省略基本脚本的最后两行.他们做所有格式化.这会让你有这样的事情:

...
0d99bb93129939b72069df14af0d0dbda7eb6dba 542455 path/to/some-image.jpg
2ba44098e28f8f66bac5e21210c2774085d2319b 12446815 path/to/hires-image.png
bd1741ddce0d07b72ccf69ed281e09bf8a2d0b2f 65183843 path/to/some-video-1080p.mp4
Run Code Online (Sandbox Code Playgroud)

文件删除

要删除实际文件,请查看有关该主题的SO问题.

  • 要在Mac上使用它,你需要`brew install coreutils`然后用`gcut`替换`cut`和用`gnumfmt`替换`numfmt`. (30认同)
  • 这不仅仅是我的赞成!特别感谢您提供计算机和人类可读输出. (12认同)
  • 这个答案似乎打印对象 ID 和文件名,而不是添加它们的提交,对吗?正如问题所问,如何找到必须删除的提交? (5认同)
  • 这非常快速且易于使用! (3认同)
  • 让我再次强调 - 这比我见过的所有其他列表都快得多。 (3认同)
  • 这是一个很棒的git别名:)`git large`有人吗? (3认同)
  • @Sridhar-Sarnobat 你怎么看?火箭速度相当快,对吧? (2认同)
  • 我建议使用 `objectsize:disk` 而不是 `objectsize`。 (2认同)
  • 非常感谢.在MacOs上为我工作(使用自制'coreutils'包,使用'gcut','gnumfmt'代替'cut'和'numfmt') (2认同)
  • 当我运行“基本脚本”时,我只会收到错误“错误:选项'批处理检查'没有任何价值”。 (2认同)
  • 我想知道它如何列出 Git LFS 管理的文件。因此,我创建了[一个包含两个大文件的存储库](https://github.com/brandizzi/big),在启用 Git LFS 之前添加/提交了“wrong.iso”和“xubuntu-18.04.2-desktop-amd64” .iso`,使用 Git LFS 添加并提交。(顺便说一句,它们是同一个文件)。此脚本适用于 LFS 之前添加的脚本:`177485aecd84 1,4GiB error.iso`。对于 LFS 之后添加的,结果是:“c381232ed0de 135B xubuntu-18.04.2-desktop-amd64.iso”。所以 **LFS 文件没有以完整大小列出**(无论如何,这是我想要的行为。) (2认同)

sko*_*ima 156

我在苏黎世联邦理工学院维基页面(靠近该页末尾)找到了一个单线解决方案.只需做一个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}')"
Run Code Online (Sandbox Code Playgroud)

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

现在还有一个更加懒惰的解决方案,GitExtensions现在有一个插件可以在UI中执行此操作(并处理历史记录重写).

GitExtensions'查找大文件'对话框

  • grep:a70783fca9bfbec1ade1519a41b6cc4ee36faea0:没有这样的文件或目录 (10认同)
  • 寻找GitExtensions就像找到金罐和彩虹的尽头 - 谢谢! (9认同)
  • 如果你想获得单个最大的文件(即使用tail -1),那个单行只能工作.Newlines阻碍了任何更大的事情.你可以使用sed转换换行符,这样grep就会很好用:``git rev-list --objects --all | grep -E`git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | 尾-10 | awk'{print $ 1}'| sed':a; N; $!ba; s/\n/|/g'``` (8认同)
  • 是否还有一个扩展名来打印文件的大小? (3认同)

Mar*_*air 126

我发现这个脚本在过去非常有用,用于在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 ', '
Run Code Online (Sandbox Code Playgroud)

这将为您提供blob的对象名称(SHA1sum),然后您可以使用如下脚本:

...找到指向每个blob的提交.

  • 这个答案真的很有帮助,因为它把我送到了上面的帖子.虽然帖子的脚本有效,但我觉得它很慢.所以我重写了它,现在它在大型存储库上的速度明显加快了.看看:https://gist.github.com/nk9/b150542ef72abc7974cb (24认同)
  • 请在您的答案中包含完整的说明,而不仅仅是异地链接; 当stubbisms.wordpress.com不可避免地下降时,我们该怎么办? (7认同)

fri*_*mle 27

步骤1将所有文件SHA1写入文本文件:

git rev-list --objects --all | sort -k 2 > allfileshas.txt
Run Code Online (Sandbox Code Playgroud)

步骤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
Run Code Online (Sandbox Code Playgroud)

步骤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;
Run Code Online (Sandbox Code Playgroud)

步骤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;
Run Code Online (Sandbox Code Playgroud)

现在,您可以查看文件bigtosmall.txt,以确定要从Git历史记录中删除哪些文件.

步骤4执行删除(请注意,此部分很慢,因为它将检查历史记录中有关您标识的文件的数据的每个提交):

git filter-branch --tree-filter 'rm -f myLargeFile.log' HEAD
Run Code Online (Sandbox Code Playgroud)

资源

步骤1-3a是从Git History中查找和清除大文件中复制的

编辑

该文章于2017年下半年被删除,但仍可使用Wayback Machine访问其存档副本.

  • 一个班轮做同样的事情:`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` (6认同)
  • @ nickolay和bash` $'\ t'`应该给你一个标签.`echo -n $'\ t'| xxd -ps` - >`09` (2认同)

War*_*ine 20

你应该使用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
Run Code Online (Sandbox Code Playgroud)

  • 这并没有告诉您如何列出所有最大的文件. (25认同)
  • 因为没有直接解决OP而被投票. (6认同)
  • 这个问题是你不能只是看到什么是大文件而不实际删除它们.如果没有干运行,我只是简单地列出大文件,我感觉不舒服. (5认同)
  • BFG Repo-Cleaner非常好.它快速闪电,工作非常可靠. (4认同)
  • 从 2020 年开始,我会避免使用 bfg。它只接受文件基名(“foo.out”)而不接受路径,因此您无法对其进行有意义的限制。它没有 -dryrun 选项。最后一次提交是 2015 年。本质上它已经死了。投了反对票(抱歉)。 (4认同)

sch*_*jos 8

如果你只想要一个大文件列表,那么我想为你提供以下单行(来源于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
Run Code Online (Sandbox Code Playgroud)

谁的输出将是:

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
Run Code Online (Sandbox Code Playgroud)

列表中的最后一个条目指向git历史记录中的最大文件.

您可以使用此输出来确保您不会删除历史记录中您需要的BFG内容.

  • 惊人的!!但是,您应该注意,在运行此命令之前,您需要使用 --mirror 选项克隆存储库。 (2认同)

Jul*_*arz 7

如果您使用的是 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
Run Code Online (Sandbox Code Playgroud)


Sve*_*enS 7

对于 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
}
Run Code Online (Sandbox Code Playgroud)

您可能需要根据自己的情况微调是显示 kB 还是 MB 还是仅显示字节。

可能有性能优化的潜力,所以如果您担心的话,请随意尝试。

要获取所有更改,只需省略| Select-Object -last $Count
要获得机器可读的版本,只需省略| Format-Table @{L='Size [kB]';E={$_.Size}}, Path -AutoSize.


Voj*_*tek 6

尝试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
Run Code Online (Sandbox Code Playgroud)


Aar*_*ron 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
Run Code Online (Sandbox Code Playgroud)