Git:显示两个提交之间文件总大小的差异?

89

是否可以显示两个提交之间文件总大小的差异?像这样:

$ git file-size-diff 7f3219 bad418 # I wish this worked :)
-1234 bytes

我尝试过:

$ git diff --patch-with-stat

这显示了在差异中每个二进制文件的文件大小差异,但不适用于文本文件,也不是总文件大小差异。

有什么想法吗?


3
以下是一段三行的bash脚本,可用于获取特定提交的大小 https://dev59.com/DHM_5IYBdhLWcg3wp0-l#23985353 - Stas Dashkovsky
7个回答

110

git cat-file -s 命令可以输出git对象的大小(以字节为单位)。git diff-tree 命令可以告诉您一个树与另一个树之间的差异。

将这些内容组合成一个名为 git-file-size-diff 的脚本,并将其放置在您PATH中的某个位置,这样就可以调用 git file-size-diff <tree-ish> <tree-ish>。我们可以尝试以下操作:

#!/bin/bash
USAGE='[--cached] [<rev-list-options>...]

Show file size changes between two commits or the index and a commit.'

SUBDIRECTORY_OK=1
. "$(git --exec-path)/git-sh-setup"
args=$(git rev-parse --sq "$@")
[ -n "$args" ] || usage
cmd="diff-tree -r"
[[ $args =~ "--cached" ]] && cmd="diff-index"
eval "git $cmd $args" | {
  total=0
  while read A B C D M P
  do
    case $M in
      M) bytes=$(( $(git cat-file -s $D) - $(git cat-file -s $C) )) ;;
      A) bytes=$(git cat-file -s $D) ;;
      D) bytes=-$(git cat-file -s $C) ;;
      *)
        echo >&2 warning: unhandled mode $M in \"$A $B $C $D $M $P\"
        continue
        ;;
    esac
    total=$(( $total + $bytes ))
    printf '%d\t%s\n' $bytes "$P"
  done
  echo total $total
}

实际使用时,它看起来像下面这样:

$ git file-size-diff HEAD~850..HEAD~845
-234   Documentation/RelNotes/1.7.7.txt
112    Documentation/git.txt
-4     GIT-VERSION-GEN
43     builtin/grep.c
42     diff-lib.c
594    git-rebase--interactive.sh
381    t/t3404-rebase-interactive.sh
114    t/test-lib.sh
743    tree-walk.c
28     tree-walk.h
67     unpack-trees.c
28     unpack-trees.h
total 1914

使用git-rev-parse,它应该接受指定提交范围的所有常规方式。
编辑:更新以记录累计总数。请注意,bash在子shell中运行while read语句,因此需要额外的大括号来避免子shell退出时丢失总数。
编辑:通过使用--cached参数调用git diff-index而不是git diff-tree,添加了与另一个树状结构比较索引的支持。例如:
$ git file-size-diff --cached master
-570    Makefile
-134    git-gui.sh
-1  lib/browser.tcl
931 lib/commit.tcl
18  lib/index.tcl
total 244

编辑:将脚本标记为能够在git存储库的子目录中运行。


3
它会执行基本检查,例如在非 Git 代码库目录下运行该命令时生成明智的消息。它还可以帮助抽象出一些平台差异。但这主要是习惯问题。编写 Git 脚本时,首先引入 git-sh-setup 文件。 - patthoyts
感谢这个棒极了的脚本!我一直在寻找一种方法来监控每次提交后大小的增加情况,这对我帮助很大。我创建了一个小的代码片段,仅显示存储库中所有(部分)提交之间的总增量 https://gist.github.com/iamaziz/1019e5a9261132ac2a9a 再次感谢! - Aziz Alto
我该如何使用它?HEAD~850是什么?我能否直接使用提交ID代替? - mr5
2
@mr5 HEAD~850 是指HEAD之前的850个提交。它只是提交的另一种表示法,您可以使用特定的提交ID、标签或任何可以解析为提交的内容。该脚本使用 git rev-parse,因此请参阅 git-rev-parse 文档中的"Specifying Revisions"部分以获取完整详细信息。(https://git-scm.com/docs/git-rev-parse) - patthoyts
我该如何查看文件大小呢?我正在准备一个拉取请求,优化文件输出结构,并希望计算大小减少的百分比。 - Philzen
显示剩余9条评论

29
你可以将输出结果进行管道传送。
git show some-ref:some-path-to-file | wc -c
git show some-other-ref:some-path-to-file | wc -c

比较这两个数字。


11
这很适合快速检查文件在不同版本之间的大小差异。但是如何用它来获取两个提交之间的总文件差异呢?我想看到在两个引用之间整个项目中添加/删除了多少字节。 - Mathias Bynens
3
如果您使用cat-file -s而不是show,则可以跳过| wc -c - neu242
使用@neu242建议的改进,我编写了这个bash函数:gdbytes () { echo "$(git cat-file -s $1:$3) -> $(git cat-file -s $2:$3)" }这使得很容易看到文件大小自上次提交以来的变化,例如:gdbytes @~ @ index.html - webninja
如果跳过 some-ref: 部分,您是否可以获取工作目录中的文件大小? - 40detectives

4

针对matthiaskrgr的回答进行扩展,可以像其他脚本一样使用https://github.com/matthiaskrgr/gitdiffbinstat

gitdiffbinstat.sh HEAD..HEAD~4

我认为它真的很有效,比这里发布的任何其他东西都要快得多。示例输出:

$ gitdiffbinstat.sh HEAD~6..HEAD~7
 HEAD~6..HEAD~7
 704a8b56161d8c69bfaf0c3e6be27a68f27453a6..40a8563d082143d81e622c675de1ea46db706f22
 Recursively getting stat for path "./c/data/gitrepo" from repo root......
 105 files changed in total
  3 text files changed, 16 insertions(+), 16 deletions(-) => [±0 lines]
  102 binary files changed 40374331 b (38 Mb) -> 39000258 b (37 Mb) => [-1374073 b (-1 Mb)]
   0 binary files added, 3 binary files removed, 99 binary files modified => [-3 files]
    0 b  added in new files, 777588 b (759 kb) removed => [-777588 b (-759 kb)]
    file modifications: 39596743 b (37 Mb) -> 39000258 b (37 Mb) => [-596485 b (-582 kb)]
    / ==>  [-1374073 b (-1 Mb)]

输出目录有点奇怪,./c/data... 代表 /c 实际上是文件系统根目录。

你不需要在Matthias的帖子下发表评论 - 你可以建议对其进行编辑,提供他没有提供的这些细节。按照当前标准,他的回答将被视为“仅链接答案”,并将被删除,因此这些细节非常重要。 - Mogsdad
谁可以接受我的答案并将其包含在Matthias中? - guest
如果你想的话,你可以自己提出建议性的编辑。(根据我的经验,这往往会被审阅者拒绝,但在编辑摘要中清晰地解释可能会有所帮助。)但也许我在对你的评论中没有表达清楚……你的回答是一个独立的回答,是对马蒂亚斯旧回答的很好更新。你不需要包含解释你的意思是评论的文本。我已经编辑了答案,以适当地给予马蒂亚斯信用。你不需要再做更多的工作。 - Mogsdad

3

2
有这个的使用示例吗? - AlecRust

2

对于脚本git-file-size-diff的评论:这是patthoyts提出的建议。该脚本非常有用,但我发现了两个问题:

  1. When someone change permissions on the file, git returns a another type in the case statement:

    T) echo >&2 "Skipping change of type"
    continue ;;
    
  2. If a sha-1 value doesn't exist anymore (for some reason), the script crashes. You need to validate the sha before getting the file size:

    $(git cat-file -e $D) if [ "$?" = 1 ]; then continue; fi

完整的 case 语句应该是这样的:
case $M in
      M) $(git cat-file -e $D)
         if [ "$?" = 1 ]; then continue; fi
         $(git cat-file -e $C)
         if [ "$?" = 1 ]; then continue; fi
         bytes=$(( $(git cat-file -s $D) - $(git cat-file -s $C) )) ;;
      A) $(git cat-file -e $D)
         if [ "$?" = 1 ]; then continue; fi
         bytes=$(git cat-file -s $D) ;;
      D) $(git cat-file -e $C)
         if [ "$?" = 1 ]; then continue; fi
         bytes=-$(git cat-file -s $C) ;;
      T) echo >&2 "Skipping change of type"
         continue ;;
      *)
        echo >&2 warning: unhandled mode $M in \"$A $B $C $D $M $P\"
        continue
        ;;
    esac

1
Git核心命令可以使这个过程更加高效,而不是每个blob需要三个后处理命令,只需要总共三个命令即可:
filesizediffs() {
    git diff-tree "$@" \
    | awk '$1":"$2 ~ /:[10]0....:[10]0/ {
            print $3?$3:empty,substr($5,3)
            print $4?$4:empty,substr($5,3)
      }'  FS='[  ]' empty=`git hash-object -w --stdin <&-` \
    | git cat-file --batch-check=$'%(objectsize)\t%(rest)' \
    |  awk '!seen[$2]++ { first[$2]=$1 }
            $1!=first[$2] { print $1-first[$2],$2; total+=$1-first[$2] }
            END { print "total size difference "total }' FS=$'\t' OFS=$'\t'
}
filesizediffs @

在GNU/任何操作系统上。

0
如果你对一个近似答案满意的话,你可以通过以下方式在提交中得到一个简单的数据大小估计:
git archive <COMMIT> | wc -c

报告的大小将是提交中所有数据的字节数加上一些tar元数据。由于tar本身(git archive的默认设置)不进行压缩,所以报告的数字在某种程度上是可比较的。

如果您的目的是找到添加了1GB日志文件的提交,这种方法完全足够。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接