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

Git 快速列出大量文件的上次提交日期,git,Git,我想列出git存储库中大量文件的上次提交日期 为了具体起见,让我们假设我想要获取特定子目录中所有*.txt文件的最后提交日期。存储库中总共有上万个文件,相关的*.txt文件的数量大约有几百个。存储库中已有数千次提交 我尝试了三种不同的方法 解决方案1。根据git日志给出一个答案。但是,如果我尝试这样做,速度会非常慢: find . -name '*.txt' | xargs -n1 git log --format=format:%ai -n1 --all -- '{}' 在我的测试

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

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

我尝试了三种不同的方法


解决方案1。根据
git日志给出一个答案。但是,如果我尝试这样做,速度会非常慢:

find . -name '*.txt' |
    xargs -n1 git log --format=format:%ai -n1 --all -- '{}'
在我的测试用例中,它花费了几分钟——对于我来说太慢了


解决方案2。类似的方法会快得多,不到一秒钟:

git log --format=format:%ai --name-only .
但是,接下来我必须编写一个脚本来对输出进行后期处理。此外,上面的命令打印出了很多不需要的信息:不相关的文件和旧的提交


解决方案3。我也尝试了类似的方法,以消除不相关的文件:

git log --format=format:%ai --name-only `find . -name '*.txt'`
然而,这比解决方案2慢。(在运行时间上有一个系数3的差异。)此外,它仍然打印不再需要的旧提交


问题。我遗漏了什么吗?有没有快捷方便的方法?当我们有更多的提交时,最好是现在和将来都能使用的东西?

试试这个

在git中,每个提交都引用一个树对象,该树对象具有指向每个文件状态的指针(这些文件是blob对象)

因此,您要做的是编写一个程序,它从您感兴趣的所有文件的列表开始,从
HEAD
对象开始(通过
git rev parse HEAD
获得SHA1 commit)。它检查该树中是否有任何“感兴趣的文件”被修改(该树是从git cat file commit[SHA1]
的“tree”属性获取的)-注意,您必须下降到每个目录的子树。如果它们被修改(意味着与“上一次”修订版中的SHA1哈希不同),它会从兴趣集中删除每一个这样的哈希,并打印适当的信息。然后,它继续到当前树的每个父级。这将一直持续到兴趣集为空为止

如果您想要最大的速度,您将使用gitcapi。如果您不想要这么快的速度,可以使用
git cat file tree[SHA1 hash]
(或者更简单的是,
git ls tree[SHA1 hash][files]
),它将执行绝对最小的工作量来读取特定的树对象(它是管道层的一部分)

这在未来能否继续发挥作用还值得怀疑,但如果向前兼容是一个更大的问题,您可以从
git cat file
提升一个级别,但正如您已经发现的那样,
git log
相对较慢,因为它是瓷器的一部分,而不是管道的一部分


有关git对象模型如何工作的非常好的资源,请参阅。

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

我用perl编写了一些脚本来修改访问时间,经过一些修改后,这是一个版本,可以打印您所要的内容:

#/usr/bin/perl
my$commit=$ARGV[0];
$commit='HEAD',除非$commit;
#git是访问时间和文件的列表
my@logbook=`git whatchanged--pretty=%ai$commit`;
我看到的百分比;
我的$timestamp;
我的$filename;
foreach(@logbook){
下一个if/^$/;#跳过emtpy行
如果(/^:/){
下一个除非是/.txt$/;
chomp($filename=(split/\t/)[1]);
下一步如果$SEED{$filename};
打印“$timestamp$filename\n”;
$seen{$filename}=1;
}否则{
chomp($timestamp=$);
}
}

我使用了
git whatchanged
而不是
git log
,以方便地使用以
开头的非时间行格式:
,因此我可以轻松地将带有文件的行与上次修改时间分开。

我在这里参加聚会有点晚,但这使用了OP的#2中的调用,并在awk中进行后处理。(就我的使用而言,我不需要查看截至当前日期已被删除的文件,因此还有存在性检查。)


下面是一个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
}

你试过用grep代替find吗?例如,
git log--format=format:%ai--name only | grep“*\.txt”
@deiga:这不正是上面的“解决方案2”吗,即获取所有内容的日志并进行一些后处理吗?类似:
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
}