如何在代码库历史记录中查找每个Git提交中的单词数量?

5
这篇文章涉及到单词计数,但我认为它也适用于在存储库中运行任何程序。我正在进行一个写作项目,后来才意识到想要在每次提交后通过编程方式生成单词计数,只针对tex文件。但是,如何获取整个项目的计数?我找不到简单的方法,所以这就是我要问的问题。
我的解决方案是自动化手动检出项目生命周期内每个提交的分支,并运行我的小型shell/sed/perl脚本以获取日期和单词计数:
#!/usr/bin/env perl

use strict;
use warnings;
use 5.014;
use App::gh::Git;
use IPC::System::Simple qw(capture);

my $repo = Git->repository( Directory => '/home/amiri/MyProject/.git' );
my @commits
    = reverse $repo->command( 'rev-list', '--all', '--date', 'short' );

my $command
    = qq{find /home/amiri/MyProject -name "*.tex" | xargs wc -w | grep total | sed 's/[a-zA-Z[:space:]]//g'};

my $command2
    = q{git log | grep "Date:" | sed -n 1p | perl -pi -e "s/^Date:\s+//g" | perl -pi -e "s/2011 -\d+$/UTC 2011/g"};

for my $commit (@commits) {
    $repo->command( "checkout", "-b", "$commit", "$commit" );
    my $count = capture($command);
    my $date  = capture($command2);
    chomp $date;
    say "$date,$count";
    $repo->command( "checkout", "master" );
    $repo->command( 'branch', "-d", $commit );
}

所以,这个方法可以工作,但我总觉得有更好的方式来实现它?它看起来有点不太优雅。


你可以很容易地调整这个回答来实现那个功能。(这不是我对那个问题的回答,它更接近于你的方法,但远不如此好。) - Mat
2个回答

7

如果你想要更容易实现的方法,并且不介意有些次优和笨拙,你可以这样做:

for commit in `git rev-list --all`; do
    git log -n 1 --pretty=%ad $commit
    git archive $commit | tar -x -O | wc -w
done

这比你现有的方法要短得多,我怀疑它也更快,因为它避免了必须将文件检出到磁盘上再次读取以计算单词数。 (要仅限于某些文件,您可以将它们作为附加参数传递给 git archive ,并注意您可以使用 git ls-tree -r --name-only <commit>获取给定提交中所有文件的列表。) git log 行只打印提交日期。 如果您想要更多,请查看 man git-log 以获取您可以执行的操作的描述-基本上有许多占位符,例如作者日期的%ad ,提交主题的%s 等等。 下一行做了这项工作。 git archive 旨在将给定的树打包成tar / zip以进行分发;我们立即解压缩它并计算单词数。(显然,您可以调整输出格式,并替换自己的计数机制以适用于 wc -w 。)
这已经相当快了-在一个带有20MB工作树的repo中,对于每个提交,它在几年前的笔记本电脑上需要大约四分之一秒。
当然,如果您真的非常关心性能,可能绝对最快的方法是,对于每个提交,遍历树,通过blob累加单词计数,并存储每个blob的单词计数,以便您不必重新计算它们。 不过,这需要更多的工作来实现。 伪代码可能如下所示:
word_counts(range)
    for (commit in `git rev-list <range>`)
        sum = 0
        for (blob in second_field_of(`git ls-tree -r commit`))
            if (!counts[blob])
                counts[blob] = word_count(`git cat-file blob`)
             total_count += counts[blob]
         print pretty_format(commit), total_count

 pretty_format(commit)
     return `git log -n 1 --pretty=... commit`

这样做可以避免任何不必要的中间步骤,并通过避免重新读取未更改的文件进一步优化。在小型存储库中,这可能并不是什么大事,但在大型存储库中,它是一个巨大的问题——想象一下一个20MB的存储库,其中平均每次提交都会涉及总大小为20KB的文件。

感谢你的 Bash 循环。我正在寻找一个包含 Git 的答案,而你给出了,Jefromi! - Amiri Barksdale

0
扩展@cascabel的答案,最近我需要对存档中的MS Word文档进行字数统计。花了我一些时间来弄清楚,所以在这里分享一下,可以扩展到任何其他类型的二进制文件并进行计数...
TMPDIR=/tmp/a
mkdir $TMPDIR 2> /dev/null
for commit in `git rev-list --all`; do
    git log -n 1 --pretty=%ad $commit
    git archive $commit | (cd $TMPDIR ; rm *; tar -x '*.docx'; pandoc -t plain * | wc -w ) 2>/dev/null
done

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