Чи є спосіб дізнатися або отримати оригінальні мітки часу створення / модифікації?
Чи є спосіб дізнатися або отримати оригінальні мітки часу створення / модифікації?
Відповіді:
Я вважаю, що єдиними позначками часу, записаними в базі даних Git, є позначки часу автора та коміту. Я не бачу можливості для Git змінити позначку часу файлу відповідно до останнього коміту, і має сенс, що це не буде поведінкою за замовчуванням (бо якби це було, Makefiles не працювали б належним чином).
Ви можете написати сценарій, щоб встановити дату модифікації ваших файлів до часу останнього коміту. Це може виглядати приблизно так:
IFS="
"
for FILE in $(git ls-files)
do
TIME=$(git log --pretty=format:%cd -n 1 --date=iso -- "$FILE")
TIME=$(date -j -f '%Y-%m-%d %H:%M:%S %z' "$TIME" +%Y%m%d%H%M.%S)
touch -m -t "$TIME" "$FILE"
done
ТАК , metastore або git-cache-meta можуть зберігати таку (мета-) інформацію! Git сам по собі, без сторонніх інструментів, не може. Metastore або git-cache-meta можуть зберігати будь-які метадані файлу для файлу.
Це за задумом, оскільки метастор або git-cache-meta призначені саме для цієї мети, а також підтримують утиліти резервного копіювання та засоби синхронізації.
(Вибачте лише трохи веселого розкручування відповіді Якуба)
find«s -printfрозширення, і я майже впевнений , що metastore (будучи C проект) ще більше роботи , щоб зробити портативний. Зовсім прикро. Я відправлю сюди повідомлення, якщо з’ясую, що ситуація змінилася.
НІ , Git просто не зберігає таку (мета-) інформацію , якщо ви не використовуєте сторонні інструменти, такі як metastore або git-cache-meta. Єдиною міткою часу, яка зберігається, є час виправлення / зміни часу (час автора) і час коміту (час комітера).
Це за задумом, оскільки Git - це система контролю версій, а не утиліта резервного копіювання чи інструмент синхронізації.
ОНОВЛЕННЯ : TL; DR: сам git не економить початковий час, але деякі рішення обходять це різними методами.git-restore-mtimeє одним з них:
https://github.com/MestreLion/git-tools/
Ubuntu / Debian: sudo apt install git-restore-mtime
Fedora / RHEL / CentOS:sudo yum install git-tools
Дивіться мою іншу відповідь для отримання більш докладної інформації
Повна відмова: Я автор git-tools
Цей сценарій python може допомогти: для кожного файлу застосовується позначка часу останнього коміту, де файл було змінено:
Нижче - справді гола версія сценарію. Для фактичного використання я настійно рекомендую одну з найбільш надійних версій вище:
#!/usr/bin/env python
# Bare-bones version. Current dir must be top-level of work tree.
# Usage: git-restore-mtime-bare [pathspecs...]
# By default update all files
# Example: to only update only the README and files in ./doc:
# git-restore-mtime-bare README doc
import subprocess, shlex
import sys, os.path
filelist = set()
for path in (sys.argv[1:] or [os.path.curdir]):
if os.path.isfile(path) or os.path.islink(path):
filelist.add(os.path.relpath(path))
elif os.path.isdir(path):
for root, subdirs, files in os.walk(path):
if '.git' in subdirs:
subdirs.remove('.git')
for file in files:
filelist.add(os.path.relpath(os.path.join(root, file)))
mtime = 0
gitobj = subprocess.Popen(shlex.split('git whatchanged --pretty=%at'),
stdout=subprocess.PIPE)
for line in gitobj.stdout:
line = line.strip()
if not line: continue
if line.startswith(':'):
file = line.split('\t')[-1]
if file in filelist:
filelist.remove(file)
#print mtime, file
os.utime(file, (mtime, mtime))
else:
mtime = long(line)
# All files done?
if not filelist:
break
Усі версії аналізують повний журнал, згенерований однією git whatchangedкомандою, що в сотні разів швидше, ніж лопінг для кожного файлу. Менше 4 секунд для git (24 000 комітів, 2500 файлів) і менше 1 хвилини для ядра Linux (40 000 файлів, 300 000 комітів)
$ python ./git-restore-mtime Traceback (most recent call last): File "./git-restore-mtime", line 122, in <module> 'git rev-parse --show-toplevel --git-dir')).split('\n')[:2] TypeError: Type str doesn't support the buffer APIНе могли б ви сказати нам, яка версія Python потрібна? Я використовую 3.3.3
strв Python 2 еквівалент bytestringPython 3, тоді як strу Python 3 - unicodePython 2. Чи можете ви повідомити про цю проблему на github.com/MestreLion/git-tools/issues ?
Я вже деякий час стикаюся з мітками часу git і file.
Перевірив деякі ваші ідеї та створив власні страшенно величезні та важкі сценарії попередників / оперативної пам'яті, доки я не знайшов (на деяких git wiki) сценарій у perl, який робить майже те, що я хотів. https://git.wiki.kernel.org/index.php/ExampleScripts
І я хотів, щоб мати можливість зберегти останню модифікацію файлів на основі дат комітів.
Отже, після деякої корекції сценарій може змінити дату створення та модифікації 200 тис . Файлів приблизно за 2-3 хв .
#!/usr/bin/perl
my %attributions;
my $remaining = 0;
open IN, "git ls-tree -r --full-name HEAD |" or die;
while (<IN>) {
if (/^\S+\s+blob \S+\s+(\S+)$/) {
$attributions{$1} = -1;
}
}
close IN;
$remaining = (keys %attributions) + 1;
print "Number of files: $remaining\n";
open IN, "git log -r --root --raw --no-abbrev --date=raw --pretty=format:%h~%cd~ |" or die;
while (<IN>) {
if (/^([^:~]+)~([^~]+)~$/) {
($commit, $date) = ($1, $2);
} elsif (/^:\S+\s+1\S+\s+\S+\s+\S+\s+\S\s+(.*)$/) {
if ($attributions{$1} == -1) {
$attributions{$1} = "$date";
$remaining--;
utime $date, $date, $1;
if ($remaining % 1000 == 0) {
print "$remaining\n";
}
if ($remaining <= 0) {
break;
}
}
}
}
close IN;
Якщо припустити, що у ваших сховищах не буде файлів більше 10 тис., Це може зайняти секунди, щоб ви могли підключити його до каси, тягнути або інших основних хуків git.
Ось моє рішення, яке враховує шляхи, що містять пробіли:
#! /bin/bash
IFS=$'\n'
list_of_files=($(git ls-files | sort))
unset IFS
for file in "${list_of_files[@]}"; do
file_name=$(echo $file)
## When you collect the timestamps:
TIME=$(date -r "$file_name" -Ins)
## When you want to recover back the timestamps:
touch -m -d $TIME "$file_name"
done
Зверніть увагу, що це не займає час, який git logзвітує, це час, про який повідомляє система. Якщо вам потрібен час з моменту, коли файли були записані, використовуйте git logрішення замістьdate -r
Рідний git не має функціональних можливостей, але його можна досягти за допомогою скриптових скриптів або сторонніх інструментів.
Я пробував metastore. Це дуже швидко, але мені не подобається необхідність встановлення та те, що метадані не зберігаються у форматі простого тексту. git-cache-metaце простий інструмент, який я спробував, але надзвичайно повільний для великих репозитаріїв (для репозиторію з десятками тисяч файлів оновлення файлу метаданих займає хвилини) і може мати проблеми з сумісністю між платформами.setgitpermsта інші підходи також мають свої недоліки, які мені не подобаються.
Нарешті я створив скрипт для цієї роботи: git-store-meta . Він має дуже легку залежність (* nix shell,, sortі perl, що вимагається git, і, за бажанням chown, chgrpі touch), так що для платформи, яка може запускати git, не потрібно встановлювати нічого додаткового, бажана продуктивність (для репо з десятками тисяч файлів, оновлення файлу метаданих займає <10 секунд; хоча і довше для створення), зберігає дані у форматі звичайного тексту , а які метадані, які потрібно «зберегти» чи «завантажити», можна налаштувати .
Це добре в мене спрацювало. Спробуйте це, якщо вас не влаштовують metastore, git-cache-meta та інші підходи.
Сподіваюся, ви оціните простоту:
# getcheckin - Retrieve the last committed checkin date and time for
# each of the files in the git project. After a "pull"
# of the project, you can update the timestamp on the
# pulled files to match that date/time. There are many
# that believe that this is not a good idea, but
# I found it useful to get the right source file dates
#
# NOTE: This script produces commands suitable for
# piping into BASH or other shell
# License: Creative Commons Attribution 3.0 United States
# (CC by 3.0 US)
##########
# walk back to the project parent or the relative pathnames don't make
# sense
##########
while [ ! -d ./.git ]
do
cd ..
done
echo "cd $(pwd)"
##########
# Note that the date format is ISO so that touch will work
##########
git ls-tree -r --full-tree HEAD |\
sed -e "s/.*\t//" | while read filename; do
echo "touch --date=\"$(git log -1 --date=iso --format="%ad" -- "$filename")\" -m $filename"
done
Для середовища Windows я написав невеликий (швидкий і брудний) EXE в Delphi 10.1 Berlin, який збирає всі дати файлів у вихідному дереві у файл .gitfilattr і може застосувати їх у перевіреному нашому дереві джерела знову.
Звичайно, я ділюсь кодом у GitHub:
https://github.com/michaschumann/gitfiledates/blob/master/gitFileDates.dpr
Я використовую його у своїй системі збірки на базі бігунів GitLab.
У моїй (та інших) інтерпретації OP є певна неоднозначність щодо того, чи означає це час коміту чи щось інше, але припускаючи, що це означає час коміту, тоді цей простий одношаровий лайнер буде працювати в Linux (на основі фрагмента відповіді Дітріха Еппа ):
git ls-files | xargs -I{} bash -c 'touch "{}" --date=@$(git log -n1 --pretty=format:%ct -- "{}")'
Але є ще складніші відповіді (включаючи git hooks), пов’язані з коментаря до оригінального запитання cregox.
--date=@foo
За допомогою інструментів GNU.
s=$(git ls-files | wc -l);
git ls-files -z |
xargs -0 -I{} -n1 bash -c \
"git log --date=format:%Y%m%d%H%M.%S '--pretty=format:touch -m -t %cd \"{}\"%n' -n1 -- {}"|
pv -l -s$s |
parallel -n1 -j8
967 0:00:05 [ 171 /s] [=====================================> ] 16%
.
$ git --version ; xargs --version | sed 1q ; ls --version | sed 1q;
parallel --version | sed 1q; pv --version | sed 1q; sh --version | sed 1q
git version 2.13.0
xargs (GNU findutils) 4.6.0
ls (GNU coreutils) 8.25
GNU parallel 20150522
pv 1.6.0 - Copyright 2015 Andrew Wood <andrew.wood@ivarch.com>
GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
У CentOS 7 у вас /usr/share/doc/rsync-*/support/git-set-file-timesі в Debian (та похідних) однаковий сценарій /usr/share/doc/rsync/scripts/git-set-file-times.gz, оригінал від Еріка Вонга і тут https://yhbt.net/git-set-file-times .
Це працює швидше, ніж інші згадані тут приклади, і вам може бути зручніше мати його вже у своєму дистрибутиві Linux.
Ось моя.
Трохи швидше, ніж деякі інші, оскільки я не називаю "отримати журнал" для кожного знайденого файлу; натомість один раз викликає 'git log' і трансформує цей результат у команди дотику.
Будуть випадки, коли в одному коміті занадто багато файлів із переліком, щоб вміститися в один буфер команд оболонки; запустіть "getconf ARG_MAX", щоб побачити максимальну довжину команди в байтах - на моїй установці debian це 2 Мб, що достатньо.
# set file last modification time to last commit of file
git log --reverse --date=iso --name-only | \
grep -vE "^(commit |Merge:|Author:| |^$)" | \
grep -B 1 "^[^D][^a][^t][^e][^:][^ ]" | \
grep -v "^\-\-" | \
sed "s|^\(.*\)$|\"\1\"|;s|^\"Date: *\(.*\)\"$|~touch -c -m -d'\1'|" | \
tr '~\n' '\n ' | \
sh -
опис за рядком:
Що стосується швидкості, це 5 секунд 1700 комітів для 6500 файлів у 700 каталогах.