我知道如何在git存储库中检索单个文件的最后修改日期:

git log -1 --format="%ad" -- path/to/file


是否有一种简单有效的方法对所有文件进行相同的修改当前在资源库中吗?

#1 楼

一个简单的答案是遍历每个文件并显示其修改时间,即:

git ls-tree -r --name-only HEAD | while read filename; do
  echo "$(git log -1 --format="%ad" -- $filename) $filename"
done


这将产生如下输出:

Fri Dec 23 19:01:01 2011 +0000 Config
Fri Dec 23 19:01:01 2011 +0000 Makefile


显然,您可以控制此操作,因为此时它只是一个bash脚本-因此请随时根据自己的内心需求进行自定义!

评论


我希望有一个选项可以在单次git log中获得组合的输出,但是您的答案比我想使用find的答案要好。我不知道git-ls-tree,它的优点是仅列出存储在存储库中的文件,跳过.git文件夹并忽略了文件。谢谢。

– EricBréchemier
2012年6月23日在8:25

没问题,埃里克;您将遵循与我相同的路线-即查找并忽略.git目录! :)使用git plumbing命令可能有一些选项,但是坦率地说,这很好用。如果您可以找到某种方式一次获取每个文件的信息,那将是最好的方法,但是请记住,git在提交状态而不是单个文件状态下运行。

–安德鲁(Andrew M.)
2012年6月25日下午16:40

如果您想要可排序的时间戳而不是人类可读的日期,则建议使用--format =“%ai”。

–雷神召唤师
2014年5月30日19:45

由于“ HEAD”只是一个引用,因此您可以使用所需的任何引用,可以是标记,分支,提交哈希等。

–安德鲁(Andrew M.)
2014年5月30日23:29

如@ThorSummoner所说,对日期使用%ai格式,然后仅通过管道对结果进行排序:git ls-tree -r --name-only HEAD |在读取文件名时;做echo“ $(git log -1 --format =”%ai“-$ filename)$ filename”;完成|分类

–约翰·亨特
17年8月18日在8:50

#2 楼

此方法也适用于包含空格的文件名:

git ls-files -z | xargs -0 -n1 -I{} -- git log -1 --format="%ai {}" {}


示例输出:

2015-11-03 10:51:16 -0500 .gitignore
2016-03-30 11:50:05 -0400 .htaccess
2015-02-18 12:20:26 -0500 .travis.yml
2016-04-29 09:19:24 +0800 2016-01-13-Atlanta.md
2016-04-29 09:29:10 +0800 2016-03-03-Elmherst.md
2016-04-29 09:41:20 +0800 2016-03-03-Milford.md
2016-04-29 08:15:19 +0800 2016-03-06-Clayton.md
2016-04-29 01:20:01 +0800 2016-03-14-Richmond.md
2016-04-29 09:49:06 +0800 3/8/2016-Clayton.md
2015-08-26 16:19:56 -0400 404.htm
2016-03-31 11:54:19 -0400 _algorithms/acls-bradycardia-algorithm.htm
2015-12-23 17:03:51 -0500 _algorithms/acls-pulseless-arrest-algorithm-asystole.htm
2016-04-11 15:00:42 -0400 _algorithms/acls-pulseless-arrest-algorithm-pea.htm
2016-03-31 11:54:19 -0400 _algorithms/acls-secondary-survey.htm
2016-03-31 11:54:19 -0400 _algorithms/acls-suspected-stroke-algorithm.htm
2016-03-31 11:54:19 -0400 _algorithms/acls-tachycardia-algorithm-stable.htm
...


输出可以通过在末尾添加| sort来按修改时间戳排序:

git ls-files -z | xargs -0 -n1 -I{} -- git log -1 --format="%ai {}" {} | sort


评论


有什么办法让它按修改时间戳排序吗?

–阿梅里奥·巴斯克斯·雷纳(Amelio Vazquez-Reina)
16年6月21日在18:16

@ AmelioVazquez-Reina:只需添加`|排序到命令末尾。

– dotancohen
17-10-18在19:24

这行得通,但是需要很长时间...

–断头台
18年1月24日在15:18

#3 楼

这是Andrew M.答案的一个小调整。 (我无法评论他的回答。)

将第一个$ filename括在双引号中,以支持带有嵌入式空格的文件名。

git ls-tree -r --name-only HEAD | while read filename; do
    echo "$(git log -1 --format="%ad" -- "$filename") $filename"
done


示例输出:

Tue Jun 21 11:38:43 2016 -0600 subdir/this is a filename with spaces.txt


我很欣赏Andrew的解决方案(基于ls-tree)适用于裸仓库! (这不适用于使用ls文件的解决方案。)

评论


您还可以跳过回显:git log -1 --format =“%ad $ filename”

–凯文·莱达(Kevin Lyda)
17-6-27 at 11:02



#4 楼

这是另一个答案:
git ls-tree -r --name-only HEAD -z | TZ=UTC xargs -0n1 -I_ git --no-pager log -1 --date=iso-local --format="%ad _" -- _

更改为先前给出的答案:

正确处理文件名中的空格。
使用ls-tree代替ls-files,因此可以与裸存储库。
始终以ISO 8601之类的格式以零偏移量(UTC)打印所有时间。通过将 | sort附加到命令,还可以对接近夏令时的时间(或来自不同时区的提交)进行正确的排序。
不需要使用子shell,因此性能应尽可能好。

请注意,这不能正确处理带有%字符的文件名。请参见下文,以获取更详细的命令来正确处理文件名中的所有字符。
请注意,由于Git并未真正存储我们要查找的信息,因此该命令仍然非常缓慢。从技术上讲,这会遍历所有文件,从整个项目历史记录中筛选出对任何给定文件的所有更改,进行最新提交并打印其作者时间戳。结果,显示的时间与更改每个文件的最后一次提交相匹配。如果文件在进行原始提交时在磁盘上具有不同的时间戳记,则它永远不会存储在git存储库中的任何位置,因此,如果没有外部数据源就无法恢复它。
如果要设置文件系统每个文件的最后作者提交时间的修改时间,您可以执行以下操作来处理文件名中的特殊字符(添加| bash以自动执行所有发出的命令):
git ls-tree -r --name-only HEAD -z | TZ=UTC xargs -0n1 git --no-pager log -1 --date=iso-local --name-only -z --format="format:%ad" | perl -npe "INIT {$/ = \"\0\"} s@^(.*? .*?) .*?\n(.*)$@$date=$1; $name=$2; $name =~ s/'/'\"'\"'/sg; \"TZ=UTC touch -m --date '$date' '$name';\n\"@se"

即使此命令比上面的命令复杂得多,该命令的性能也应与第一个命令大致相同,因为通过搜索每个文件的最后修改时间(而不是实际设置修改时间)来限制性能。请注意,这会将时间转换为UTC,使用以空分隔的文件,并在设置时间的同时使用UTC时区为文件系统上的每个文件重置正确的时间戳。
如果输出顺序不是很重要,则可以提高性能通过在-P $(nproc)标志中添加xargs以将Git缩放到所有CPU,使该命令看起来像...TZ=UTC xargs -0n1 -P $(nproc) git...一样。
如果您更喜欢提交者时间而不是作者日期,请在上面的命令行中使用%cd而不是%ad

评论


这是最佳答案,因为它可以按日期排序。

–乔纳森·本·阿夫拉罕
20年9月12日19:30在

+1是我很久以来见过的最好的1行shell脚本!

–安德鲁·墨菲(Andrew Murphy)
20-09-30在7:01

#5 楼

对于使用Windows和PowerShell的我们这些人,Andrew M的答案是计算机可读的调整:

git ls-tree -r --name-only HEAD | ForEach-Object { "$(git log -1 --format="%ai" -- "$_")`t$_" }


示例输出:

2019-05-07 12:00:37 -0500   .editorconfig
2016-07-13 14:03:49 -0500   .gitattributes
2019-05-07 12:00:37 -0500   .gitignore
2018-02-03 22:01:17 -0600   .mailmap


#6 楼

这是安德鲁·M(Andrew M)答案的鱼壳版本,适用于使用鱼的人。

git ls-tree -r --name-only HEAD | while read -l filename
    printf '%s %s\n' (git log -1 --format="%ai" -- $filename) $filename
end


我将其存储为鱼函数,以便于使用。

#7 楼

如果要尝试在大型仓库中设置文件修改时间,请查看https://github.com/MestreLion/git-tools它已经是一个软件包。
sudo apt install git-restore-mtime
cd repo
git restore-mtime

它使用git whatschanged而不是git log,这在大型仓库上要快得多