我想列出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个不同。)此外,它仍会打印不再需要的旧提交。
问题。我错过了什么吗?有快速方便的方法吗?当我们提交更多数量的提交时,最好不仅能够立即使用,还能在将来工作?
答案 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