快速列出大量文件的上次提交日期

时间:2012-02-23 17:04:37

标签: git

我想列出git存储库中大量文件的最后提交日期

为了具体起见,我们假设我想获取特定子目录中所有*.txt个文件的最后提交日期。存储库中总共有成千上万的文件,相关*.txt文件的数量在几百个大小的范围内。存储库中已有数千个提交。

我尝试了三种不同的方法。


解决方案1。 This question根据git log给出一个答案。但是,如果我尝试做这样的事情,那就很慢了:

find . -name '*.txt' |
    xargs -n1 git log --format=format:%ai -n1 --all -- '{}'

在我的测试案例中,花了几分钟 - 对我来说太慢了。


解决方案2。这样的事情会更快 ,不到一秒钟:

git log --format=format:%ai --name-only .

但是,我必须编写一个后处理输出的脚本。此外,上面的命令打印出 lot 从不需要的信息:无关文件和旧提交。


解决方案3。我也试过这样的事情,以便摆脱不相关的文件:

git log --format=format:%ai --name-only `find . -name '*.txt'`

然而,事实证明比解决方案2慢。(运行时间有3个不同。)此外,它仍会打印不再需要的旧提交。


问题。我错过了什么吗?有快速方便的方法吗?当我们提交更多数量的提交时,最好不仅能够立即使用,还能在将来工作?

4 个答案:

答案 0 :(得分:7)

试试这个。

在git中,每个提交引用一个树对象,它指向每个文件的状态(文件是 blob对象)。

所以,你要做的是编写一个程序,该程序首先列出你感兴趣的所有文件,然后从HEAD对象开始(通过{{1}获得的SHA1提交})。它检查是否在该树中修改了任何“感兴趣的文件”(树从git rev-parse HEAD的“树”属性获得) - 注意,您将必须下降到每个目录的子树。如果它们被修改(意味着与“上一个”修订版中的SHA1哈希值不同),会从兴趣集中删除每个哈希值并打印相应的信息。然后它继续到当前树的每个父节点。这一直持续到感兴趣的组合为空。

如果你想要最大速度,你将使用git C API。如果您不希望 速度很快,可以使用git cat-file commit [SHA1](或更简单,git cat-file tree [SHA1 hash]),这将执行绝对最少的工作量来读取一个特定的树对象(它是管道层的一部分)。

这将在未来继续发挥作用是值得怀疑的,但如果前向比较是一个更大的问题,你可以从git ls-tree [SHA1 hash] [files]升级 - 但正如你已经发现的那样,git cat-file是比较慢,因为它是瓷器的一部分,而不是管道。

请参阅here,了解git对象模型的工作原理。

答案 1 :(得分:1)

我还认为您的解决方案#2是最快的,您可以找到几个使用此方法设置访问时间的脚本。避免打印较旧访问时间的方法是使用例如哈希。

我在perl中编写了一些脚本来修改访问时间,经过一些修改后,这个版本应该打印出你想要的内容:

#!/usr/bin/perl
my $commit = $ARGV[0];

$commit = 'HEAD' unless $commit;

# git a list of access times and files
my @logbook = `git whatchanged --pretty=%ai $commit`;

my %seen;
my $timestamp;
my $filename;
foreach (@logbook) {
    next if /^$/; # skip emtpy lines
    if (/^:/) {
        next unless /.txt$/;
        chomp ($filename = (split /\t/)[1]);
        next if $seen{$filename};
        print "$timestamp $filename\n";
        $seen{$filename} = 1;
    } else {
        chomp ($timestamp = $_);
    }
}

我使用git whatchanged代替git log来使用以:开头的非时间线的方便格式,因此我可以轻松地将这些行与上次修改时间的文件分开。

答案 2 :(得分:1)

这是Powershell函数

function Get-GitRevisionDates($Path='.', $Ext='.md')
{
    [array] $log = git --no-pager log --format=format:%ai --name-only $Path

    $date_re = "^\d{4}-\d\d-\d\d \d\d:\d\d:\d\d .\d{4}$"
    [array] $dates = $log | Select-String $date_re | select LineNumber, Line

    $files = $log -notmatch "^$date_re$" | ? { $_.EndsWith($Ext) } | sort -unique

    $res = @()
    foreach ($file in $files) {
        $iFile = $log.IndexOf($file) + 1
        $fDate = $dates | ? LineNumber -lt $iFile | select -Last 1
        $res += [PSCustomObject]@{ File = $file; Date = $fDate.Line }
    }

    $res | sort Date -Desc
}

答案 3 :(得分:0)

我在这里参加派对有点晚了,但here's a little Bash script使用OP#2中的调用,并在awk中进行后处理。 (对于我的使用,我不需要查看截至当前日期已被删除的文件,因此也存在检查。)

#!/bin/bash
(
    git ls-files | sed 's/^/+ /'
    git log --format=format:"~ %aI" --name-only .
) | gawk '
/^~/ {date=$2;}
/^+/ {extant[$2] = 1;}
/^[^~+]/ {dates[$1] = date;}
END { for (file in dates) if(extant[file]) print(dates[file], file); }
' | sort