Czy jest jakiś sposób, aby poznać lub uzyskać oryginalne utworzone / zmodyfikowane sygnatury czasowe?
Czy jest jakiś sposób, aby poznać lub uzyskać oryginalne utworzone / zmodyfikowane sygnatury czasowe?
Odpowiedzi:
Uważam, że jedynymi sygnaturami czasowymi zarejestrowanymi w bazie danych Git są autor i znaczniki czasu zatwierdzania. Nie widzę opcji dla Gita, aby zmodyfikować znacznik czasu pliku, aby pasował do ostatniego zatwierdzenia, i ma sens, że nie byłoby to zachowanie domyślne (ponieważ gdyby tak było, Makefiles nie działałby poprawnie).
Możesz napisać skrypt ustawiający datę modyfikacji twoich plików na czas ostatniego zatwierdzenia. Może to wyglądać mniej więcej tak:
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
TAK , metastore lub git-cache-meta mogą przechowywać takie (meta) informacje! Sam Git, bez narzędzi innych firm, nie może. Metastore lub git-cache-meta mogą przechowywać dowolne metadane pliku dla pliku.
Jest to zgodne z projektem, ponieważ metastore lub git-cache-meta są przeznaczone do tego właśnie celu, a także do obsługi narzędzi do tworzenia kopii zapasowych i narzędzi do synchronizacji.
(Przepraszam, tylko trochę zabawne kręcenie odpowiedzi Jakuba)
findjest -printfrozszerzeniem, i jestem prawie pewien, że metastore (jest to projekt C) jest jeszcze więcej pracy, aby przenośny. Całkiem niefortunne. Wrócę tutaj, jeśli dowiem się, że sytuacja się zmieni.
NIE , Git po prostu nie przechowuje takich (meta) informacji , chyba że używasz narzędzi innych firm, takich jak metastore lub git-cache-meta. Jedynym zapisywanym sygnaturą czasową jest data utworzenia poprawki / zmiany (czas autora) i data zatwierdzenia (czas autora).
Jest to zgodne z projektem, ponieważ Git jest systemem kontroli wersji, a nie narzędziem do tworzenia kopii zapasowych ani narzędziem do synchronizacji.
UPDATE : TL; DR: sam git nie oszczędza oryginalnych czasów, ale niektóre rozwiązania omijają to różnymi metodami. git-restore-mtimejest jednym z nich:
https://github.com/MestreLion/git-tools/
Ubuntu / Debian: sudo apt install git-restore-mtime
Fedora / RHEL / CentOS:sudo yum install git-tools
Zobacz moją drugą odpowiedź, aby uzyskać więcej informacji
Pełne zastrzeżenie: jestem autorem git-tools
Ten skrypt w Pythonie może pomóc: dla każdego pliku stosuje się sygnaturę czasową ostatniego zatwierdzenia, w którym plik został zmodyfikowany:
Poniżej znajduje się naprawdę prosta wersja skryptu. W przypadku rzeczywistego użytkowania zdecydowanie sugeruję jedną z bardziej niezawodnych wersji powyżej:
#!/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
Wszystkie wersje analizują pełny dziennik wygenerowany za pomocą jednego git whatchangedpolecenia, co jest setki razy szybsze niż wycinanie dla każdego pliku. Poniżej 4 sekund dla git (24 000 zatwierdzeń, 2500 plików) i mniej niż 1 minuta dla jądra systemu Linux (40 000 plików, 300 000 zatwierdzeń)
$ 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 APICzy mógłbyś powiedzieć nam, która wersja Pythona jest potrzebna? Używam wersji 3.3.3
strw Pythonie 2 jest odpowiednikiem bytestringw Pythonie 3, podczas gdy strw Pythonie 3 jest unicodew Pythonie 2. Czy możesz zgłosić ten problem na github.com/MestreLion/git-tools/issues ?
Już od jakiegoś czasu potykam się ze znacznikami czasu git i plików.
Przetestowałem niektóre z twoich pomysłów i stworzyłem własne, strasznie duże i ciężkie skrypty poprzednika / pamięci RAM, aż znalazłem (na niektórych wiki git) skrypt w Perlu, który robi prawie to, co chciałem. https://git.wiki.kernel.org/index.php/ExampleScripts
Chciałem móc zachować ostatnią modyfikację plików na podstawie dat zatwierdzenia.
Tak więc po pewnym dostosowaniu skrypt jest w stanie zmienić datę utworzenia i modyfikacji pliku 200 tys. Plików w około 2-3 minuty .
#!/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;
Zakładając, że twoje repozytoria nie będą miały ponad 10 000 plików, wykonanie powinno zająć kilka sekund, więc możesz podłączyć go do checkout, pull lub innych haków git basic.
Oto moje rozwiązanie uwzględniające ścieżki zawierające spacje:
#! /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
Zwróć uwagę, że nie zajmuje to czasu na git logzgłoszenie, jest to czas zgłoszony przez system. Jeśli chcesz określić czas od momentu zatwierdzenia plików, użyj git logrozwiązania zamiastdate -r
Natywny git nie ma tej funkcjonalności, ale można to osiągnąć za pomocą skryptów przechwytujących lub narzędzi innych firm.
Próbowałem metastore. Jest bardzo szybki, ale nie podoba mi się potrzeba instalacji, a metadane nie są przechowywane w formacie zwykłego tekstu. git-cache-metato proste narzędzie, które wypróbowałem, ale jest bardzo powolne w przypadku dużych repozytoriów (w przypadku repozytorium z dziesiątkami tysięcy plików aktualizacja pliku metadanych zajmuje kilka minut) i może mieć problemy ze zgodnością między platformami. setgitpermsa inne podejścia również mają swoje wady, których nie lubię.
W końcu stworzyłem skrypt przechwytujący do tego zadania: git-store-meta . Ma bardzo lekką zależność (* nix skorupę, sorti perl, co jest wymagane przez git, a opcjonalnie chown, chgrpi touch) tak że nic dodatkowego muszą być zainstalowane na platformie, które można uruchomić git, pożądanej wydajności (dla transakcji repo z dziesiątków tysięcy plików, aktualizacja pliku metadanych zajmuje mniej niż 10 sekund; chociaż tworzenie trwa dłużej), zapisuje dane w formacie zwykłego tekstu , a metadane do „zapisania” lub „załadowania” można dostosować .
U mnie działa dobrze. Spróbuj tego, jeśli nie jesteś zadowolony z metastore, git-cache-meta i innych podejść.
Mam nadzieję, że docenisz prostotę:
# 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
Dla środowiska Windows napisałem mały (szybki i brudny) plik EXE w Delphi 10.1 Berlin, który gromadzi wszystkie daty plików z drzewa źródłowego do pliku .gitfilattr i może ponownie zastosować je na sprawdzonym drzewie źródłowym.
Oczywiście udostępniam kod w GitHubie:
https://github.com/michaschumann/gitfiledates/blob/master/gitFileDates.dpr
Używam go w moim systemie kompilacji opartym na runnerach GitLab.
W mojej (i innych) interpretacji OP jest pewna niejasność dotycząca tego, czy oznacza to czas zatwierdzenia, czy coś innego, ale zakładając, że oznacza to czas zatwierdzenia, to ten prosty jednolinijkowy będzie działał w systemie Linux (na podstawie fragmentu odpowiedzi od Dietricha Eppa ):
git ls-files | xargs -I{} bash -c 'touch "{}" --date=@$(git log -n1 --pretty=format:%ct -- "{}")'
Ale są bardziej wyrafinowane odpowiedzi (w tym haki git) połączone z komentarzem do oryginalnego pytania Cregox.
--date=@foo
Z narzędziami 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)
W CentOS 7 masz, /usr/share/doc/rsync-*/support/git-set-file-timesaw Debianie (i pochodnych) ten sam skrypt w /usr/share/doc/rsync/scripts/git-set-file-times.gz, oryginał pochodzi od Erica Wonga i jest tutaj https://yhbt.net/git-set-file-times .
Działa szybciej niż inne wymienione tutaj przykłady i może być wygodniejsze, jeśli masz go już w swojej dystrybucji Linuksa.
To moje.
Trochę szybciej niż niektóre inne, ponieważ nie wywołuję „get log” dla każdego znalezionego pliku; zamiast tego wywołanie raz „git log” i przekształcenie tego wyniku w polecenia dotykowe.
Będą przypadki, w których w jednym zatwierdzeniu znajduje się zbyt wiele plików, aby zmieściły się one w pojedynczym buforze poleceń powłoki; uruchom "getconf ARG_MAX", aby zobaczyć maksymalną długość polecenia w bajtach - w mojej instalacji Debiana jest to 2 MB, czyli dużo.
# 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 -
opis według linii:
Jeśli chodzi o szybkość, to 5 sekund 1700 zatwierdzeń dla 6500 plików w 700 katalogach.