如何在存储库的历史记录中的每个git提交中查找字数?

时间:2012-02-12 02:51:34

标签: git perl sed commit

这是关于字数的,但我想它也是关​​于在存储库中的所有git提交中运行任何程序。我正在做一个写作项目,并且很晚才意识到我想在每次提交后以编程方式生成单词count。仅适用于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 );
}

所以,这有效,但我不禁觉得有更好的方法吗?看起来有点icky。

2 个答案:

答案 0 :(得分:4)

如果你想要更容易实现的东西,并且不介意有点不理想和kludgy,你可以这样做:

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的工作树进行回购。

当然,如果你真的非常关心性能,那么绝对最快的方法可能是,对于每次提交,遍历树,将字数统计在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。

答案 1 :(得分: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