Eric Bréchemier Asked: 2012-06-23 11:41:19 +0800 CST2012-06-23 11:41:19 +0800 CST 2012-06-23 11:41:19 +0800 CST 如何检索 Git 存储库中所有文件的最后修改日期 772 我知道如何检索 Git 存储库中单个文件的最后修改日期: git log -1 --format="%ad" -- path/to/file 是否有一种简单有效的方法可以对存储库中当前存在的所有文件执行相同的操作? git 7 个回答 Voted Best Answer Andrew M. 2012-06-23T12:10:17+08:002012-06-23T12:10:17+08:00 一个简单的答案是遍历每个文件并显示其修改时间,即: 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 脚本——所以请随意自定义您的内容! William Entriken 2016-05-04T07:35:34+08:002016-05-04T07:35:34+08:00 这种方法也适用于包含空格的文件名: 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 Mikko Rantalainen 2020-08-30T02:52:37+08:002020-08-30T02:52:37+08:00 这是另一种方式: git ls-tree -r --name-only HEAD -z | TZ=UTC xargs -0n1 -I_ git --no-pager log -1 --date=iso-local --format="%ad _" -- _ 对先前给出的答案的更改: 正确处理文件名中的空格。 uses ls-treeinstead of ls-filesand as such 可以与裸存储库一起使用。 始终以类似 ISO 8601 的格式打印零偏移量 (UTC)。 | sort通过附加到命令,这也允许对接近夏令时更改(或来自不同时区的提交)的时间进行正确排序。 不需要使用子外壳,因此性能应该尽可能好。 请注意,这不能正确处理带有%字符的文件名。有关正确处理文件名中所有字符的更详尽的命令,请参见下文。 请注意,此命令仍然非常慢,因为 Git 并未真正存储我们正在查找的信息。从技术上讲,这会遍历所有文件,过滤整个项目历史中对任何给定文件的所有更改,获取最新提交并打印其作者时间戳。结果,显示的时间与更改每个文件的最后一次提交相匹配。如果文件在进行原始提交时在磁盘上具有不同的时间戳,则它不会存储在 Git 存储库中的任何位置,因此如果没有外部数据源,它永远无法恢复。 该脚本发出的时间戳只是一个与提交时间匹配的模拟版本,而不是文件的真实时间戳,因为 Git 不将文件时间戳视为数据。这是因为 Git 的这一部分是由 Linus Torvalds 设计的,他坚信磁盘上的文件时间戳应该匹配它在磁盘上被修改的时间,而不是文件在历史上被修改时在其他人磁盘上的时间戳. Git 只为已提交的提交存储一个时间戳,并为提交包含在 DAG 中的那一刻存储另一个时间戳。如果提交作者和将提交应用于版本历史记录的人是两个不同的人,这可能会有所不同,这在 Linux 内核开发中经常发生。 如果你想将文件系统修改时间设置为每个文件的最后作者提交时间,你可以做这样的事情来处理文件名中的特殊字符(添加| 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)Gitxargs扩展到所有 CPU,使命令看起来像...TZ=UTC xargs -0n1 -P $(nproc) git...。 如果您更喜欢提交者时间而不是作者日期,请在上面的命令行中使用%cdinstead of 。%ad Kevin G. 2016-06-22T10:15:19+08:002016-06-22T10:15:19+08:00 这是Andrew M.'s answer的一个小调整。(我无法评论他的回答。) 将第一个 $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-files的解决方案并非如此。) Andrew Murphy 2020-10-01T10:22:33+08:002020-10-01T10:22:33+08:00 如果您尝试在大型存储库上设置文件修改时间,请查看Git 工具。已经是一个包了。 sudo apt install git-restore-mtime cd repo git restore-mtime 它使用git whatschangedrather than git log,这在大型存储库上要快得多。 James Skemp 2019-10-18T07:39:57+08:002019-10-18T07:39:57+08:00 对于我们这些使用 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 Kevin Cherepski 2018-03-10T08:28:34+08:002018-03-10T08:28:34+08:00 对于那些使用 Fish 的人,这是Andrew M 的答案的Fish shell 版本。 git ls-tree -r --name-only HEAD | while read -l filename printf '%s %s\n' (git log -1 --format="%ai" -- $filename) $filename end 我将其存储为 Fish 函数以便于访问。
一个简单的答案是遍历每个文件并显示其修改时间,即:
这将产生如下输出:
显然,您可以控制它,因为此时它只是一个 bash 脚本——所以请随意自定义您的内容!
这种方法也适用于包含空格的文件名:
示例输出:
输出可以通过添加
| sort
到末尾按修改时间戳排序:这是另一种方式:
对先前给出的答案的更改:
ls-tree
instead ofls-files
and as such 可以与裸存储库一起使用。| sort
通过附加到命令,这也允许对接近夏令时更改(或来自不同时区的提交)的时间进行正确排序。请注意,这不能正确处理带有
%
字符的文件名。有关正确处理文件名中所有字符的更详尽的命令,请参见下文。请注意,此命令仍然非常慢,因为 Git 并未真正存储我们正在查找的信息。从技术上讲,这会遍历所有文件,过滤整个项目历史中对任何给定文件的所有更改,获取最新提交并打印其作者时间戳。结果,显示的时间与更改每个文件的最后一次提交相匹配。如果文件在进行原始提交时在磁盘上具有不同的时间戳,则它不会存储在 Git 存储库中的任何位置,因此如果没有外部数据源,它永远无法恢复。
该脚本发出的时间戳只是一个与提交时间匹配的模拟版本,而不是文件的真实时间戳,因为 Git 不将文件时间戳视为数据。这是因为 Git 的这一部分是由 Linus Torvalds 设计的,他坚信磁盘上的文件时间戳应该匹配它在磁盘上被修改的时间,而不是文件在历史上被修改时在其他人磁盘上的时间戳. Git 只为已提交的提交存储一个时间戳,并为提交包含在 DAG 中的那一刻存储另一个时间戳。如果提交作者和将提交应用于版本历史记录的人是两个不同的人,这可能会有所不同,这在 Linux 内核开发中经常发生。
如果你想将文件系统修改时间设置为每个文件的最后作者提交时间,你可以做这样的事情来处理文件名中的特殊字符(添加
| bash
自动执行所有发出的命令):尽管这比上面的命令复杂得多,但该命令的性能应该与第一个命令大致相同,因为性能受到搜索每个文件的最后修改时间而不是实际设置修改时间的限制。请注意,这会将时间转换为 UTC,使用空分隔文件并在设置时间时使用 UTC 时区为文件系统上的每个文件重置正确的时间戳。
如果输出顺序不是很重要,您可以通过添加标志来提高此命令的性能,以将
-P $(nproc)
Gitxargs
扩展到所有 CPU,使命令看起来像...TZ=UTC xargs -0n1 -P $(nproc) git...
。如果您更喜欢提交者时间而不是作者日期,请在上面的命令行中使用
%cd
instead of 。%ad
这是Andrew M.'s answer的一个小调整。(我无法评论他的回答。)
将第一个 $filename 括在双引号中,以支持带有嵌入空格的文件名。
示例输出:
我很欣赏 Andrew 的解决方案(基于ls-tree)适用于裸存储库!(使用ls-files的解决方案并非如此。)
如果您尝试在大型存储库上设置文件修改时间,请查看Git 工具。已经是一个包了。
它使用
git whatschanged
rather thangit log
,这在大型存储库上要快得多。对于我们这些使用 Windows 和 PowerShell 的人,Andrew M 的答案是计算机可读的调整:
示例输出:
对于那些使用 Fish 的人,这是Andrew M 的答案的Fish shell 版本。
我将其存储为 Fish 函数以便于访问。