Я знаю, как получить дату последней модификации отдельного файла в репозитории git:
git log -1 --format="%ad" -- path/to/file
Есть ли простой и эффективный способ сделать то же самое для всех файлов, находящихся в настоящее время в репозитории?
Простым ответом было бы перебрать каждый файл и отобразить время его модификации, то есть:
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 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
Это небольшая настройка ответа Эндрю М.. (Я не смог прокомментировать его ответ.)
Заключите первое имя файла $ 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
Я ценю решение Эндрю (на основе ls-дерево) работает с голыми репозиториями! (Это не относится к решениям, использующим ls-файлы.)
Для тех из нас, кто использует Windows и PowerShell, ответ Эндрю М с компьютерно-читаемой настройкой:
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
Вот версия ответа Эндрю М в виде рыбной раковины для тех, кто использует рыбу.
git ls-tree -r --name-only HEAD | while read -l filename
printf '%s %s\n' (git log -1 --format="%ai" -- $filename) $filename
end
Я сохраняю это как функцию рыбы для легкого доступа.
Вот еще один ответ:
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
и как таковой может использоваться с голыми репозиториями. | sort
к команде.Обратите внимание, что это неправильно обрабатывает имена файлов с %
персонаж. См. Ниже более подробную команду для правильной обработки всех символов в именах файлов.
Обратите внимание, что эта команда все еще очень медленная, потому что 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 на все процессоры, чтобы команда выглядела как ...TZ=UTC xargs -0n1 -P $(nproc) git...
.
Если вы предпочитаете время коммиттера вместо даты автора, используйте %cd
вместо того %ad
в командной строке выше.