如何获得整个git历史中每个文件的大小?

如何获得整个git历史中每个文件的大小?,git,Git,我想从我的git存储库中删除大文件。但是,我想具体说明一下,所以我想查看存储库所有历史记录中的所有文件大小 我创建了以下bash脚本,但它似乎效率很低,可能缺少历史上某个地方删除的文件: git log --pretty=tformat:%H | while read hash; do git show --stat --name-only $hash | grep -P '^(?:(?!commit|Author:|Date:|Merge:| ).)*$' | while read

我想从我的git存储库中删除大文件。但是,我想具体说明一下,所以我想查看存储库所有历史记录中的所有文件大小

我创建了以下bash脚本,但它似乎效率很低,可能缺少历史上某个地方删除的文件:

git log --pretty=tformat:%H | while read hash; do
   git show --stat --name-only $hash | grep -P '^(?:(?!commit|Author:|Date:|Merge:|   ).)*$' | while read filename; do
      if [ ! -z "$filename" ]; then
          git show "$hash:$filename" | wc -c | while read filesize; do
             if [ $(echo "$filesize > 100000" | bc) -eq 1 ]; then
                printf "%-40s %11s %s\n" "$hash" "$filesize" "$filename"
             fi
          done
      fi
   done
done

关于更好的方法有什么建议吗?

git ls文件将为您提供所有文件的列表。如果您传递
--debug
选项,它将以以下格式输出附加数据:

path/filename.ext
  ctime: ${timestamp}:0
  mtime: ${timestamp}:0
  dev: 16777220 ino: 62244153
  uid: 1912685926   gid: 80
  size: ${bytes}    flags: 0

然后,您可以分析
size
值的结果,并将其与您设置的最大值进行比较。

实际上,您已经完成了大部分工作

git log --pretty=tformat:%H
这应该是
git rev list
,例如
git rev list HEAD
git rev list--all
。您可能需要添加
--topo order--reverse
,原因我们稍后将讨论

 | while read hash; do
   git show --stat --name-only $hash
您可能只想在散列上使用
gitls-tree
,而不是
gitshow--stat
。使用递归的
gitls树
可以找到给定提交中的每个树和blob,以及相应的路径名

这些树可能没什么意思,所以我们可能会掉下来成一团。请注意,顺便提一下,
gitls-tree
将对一些有问题的文件名进行编码,除非您使用
-z
(但这会使读取项目变得更困难;bash可以做到,而普通sh不能做到)

使用
git ls tree
我们可以将其替换为:

git ls tree-r$hash |而读取模式类型为objhash path;执行

然后我们将跳过任何类型不是blob的内容:

[$type==blob]| |继续

  if [ ! -z "$filename" ]; then
我们根本不需要这个

      git show "$hash:$filename" | wc -c | while read filesize; do
         if [ $(echo "$filesize > 100000" | bc) -eq 1 ]; then
            printf "%-40s %11s %s\n" "$hash" "$filesize" "$filename"
         fi
我不清楚为什么会有一个
while read filesize
循环,或者复杂的测试。在任何情况下,获取blob对象大小的简单方法是使用
git cat file-s$objhash
,并且可以轻松测试
[$blobsize-gt 100000]
,例如:

    blobsize=$(git cat-file -s $objhash)
    if [ $blobsize -gt 100000 ]; then
       echo "$hash contains $filename size $blobsize"
    fi
但是,通过放弃git show而选择git ls tree-r,我们可以在每次提交中看到每个文件的每个副本,而不是在第一次提交时只看到一次。例如,如果commit
f00f1e
添加了大文件
bigfile
,并且它在commit
baafba6
中保持不变,我们将看到这两次。使用
git show--stat
运行
git diff
的一个变体,将每个提交与其父级进行比较,这样,如果我们以前看到过该文件,就可以忽略它

轻微的缺陷(或者可能不是缺陷)是,如果文件返回,我们将“重新看到”。例如,如果这个大文件在第三次提交时被删除,在第四次提交时被恢复,我们将看到它两次

这是我们可能需要的
--拓扑顺序--反向
。如果我们使用它,我们将在其子级之前获得所有父级提交。然后,我们可以保存每个已诊断的对象哈希,并禁止重复诊断。在这里,一种具有关联数组(哈希表)的优秀编程语言非常方便,但我们可以在普通bash中使用包含以前显示的对象哈希的文件或目录来实现这一点:

#! /bin/sh

# get temporary file to hold viewed object hashes
TF=$(mktemp)
trap "rm -f $TF" 0 1 2 3 15

BIG=100000  # files up to (and including?) this size are not-big

git rev-list --all --topo-order --reverse |
while read commithash; do
    git ls-tree -r $commithash |
    while read mode type objhash path; do
        [ $type == blob ] || continue      # only look at files
        blobsize=$(git cat-file -s $objhash)
        [ $blobsize -lt $BIG ] && continue # or -le
        # found a big file - have we seen it yet?
        grep $objhash $TF >/dev/null && continue
        echo "$blobsize byte file added at commit $commithash as $path"
        echo $objhash >> $TF # don't print again under any path name
    done
done
请注意,由于我们现在通过散列ID记住大文件,因此即使它们以另一个名称重新出现(例如,get
git mv
ed,或者被删除然后以相同或另一个名称重新出现),我们也不会重新宣布它们

如果您喜欢使用
git show
使用的diff调用方法,我们可以使用该方法而不是散列保存临时文件,但仍然可以通过使用适当的管道命令(即
git diff tree
)避免笨拙地将提交消息变大。虽然不再需要--topoorder,但使用--topoorder(作为一般规则)可能仍然是明智的。因此,这给出:

BIG=100000 # just as before

git rev-list --all --topo-order | while read commithash; do
    git diff-tree -r --name-only --diff-filter=AMT $commithash |
        tail -n +2 | while read path; do
            objsize=$(git cat-file -s "$commithash:$path")
            [ $objsize -lt $BIG ] && continue
            echo "$blobsize byte file added at commit $commithash as $path"
        done
done
git-diff-tree
需要
-r
递归工作(与
git-ls-tree
相同),需要
--name only
只打印文件名,需要
--diff-filter=AMT
只打印添加、修改或类型更改的文件名(从符号链接到文件,反之亦然)。令人讨厌的是,
git diff tree
再次打印提交ID作为第一行。我们可以使用
--no-commit-ID
来抑制ID,但是我们会得到一个空行,因此我们最好使用
tail-n+2
跳过第一行

脚本的其余部分与您的相同,只是我们使用
git cat file-s
以简单的方式获得对象的大小,并直接使用
[
/
测试程序对其进行测试

注意,对于合并提交,
git-diff-tree
(如
git-show
)使用组合的差异,仅显示合并结果中与任何一个父级不匹配的文件。这应该可以,因为如果文件
巨大
在合并结果中为4GB,但与两次合并提交中的一次提交中为4GB的文件
巨大
相同,我们将在将其添加到该提交时看到它,而不是在t中看到它他合并了自己

(如果不需要,可以将
-m
添加到
git diff-tree
命令中。但是,您需要删除
尾部-n+2
,并输入
--no commit id
,它在
-m
下的行为不同。git中的这种特殊行为有些烦人,尽管它在默认情况下是有意义的输出格式,类似于
git log--raw

(注意:上述代码未经测试-在上次重读时发现并修复了
$hash
$commithash
的差异。)

也就是说,对于历史上的每次提交,在提交id之后显示所有已更改但未删除的文件,将列表重新格式化为

sha:path sha path

对于每个和馈送到--批量检查大小的一次性提取。

git ls files
读取索引,因此只包括存储在索引中的文件,而不包括
git log --name-only --diff-filter=d --all --pretty=format:%H \
| awk '/^$/{c=""}!c{c=$1;next}{print c":"$0,c,$0}' \
| git cat-file --batch-check=$'%(rest)\t%(objectsize)'
sha:path sha path