Gibt es eine Möglichkeit, die ursprünglichen erstellten / geänderten Zeitstempel zu kennen oder zu erhalten?
Gibt es eine Möglichkeit, die ursprünglichen erstellten / geänderten Zeitstempel zu kennen oder zu erhalten?
Antworten:
Ich glaube, dass die einzigen in der Git-Datenbank aufgezeichneten Zeitstempel der Autor sind und Zeitstempel festschreiben. Ich sehe keine Option für Git, um den Zeitstempel der Datei so zu ändern, dass er mit dem letzten Commit übereinstimmt, und es ist sinnvoll, dass dies nicht das Standardverhalten ist (da Makefiles sonst nicht richtig funktionieren würden).
Sie können ein Skript schreiben, um das Änderungsdatum Ihrer Dateien auf den Zeitpunkt des letzten Commits festzulegen. Es könnte ungefähr so aussehen:
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
JA , Metastore oder Git-Cache-Meta können solche (Meta-) Informationen speichern! Git allein kann es ohne Tools von Drittanbietern nicht. Metastore oder Git-Cache-Meta können beliebige Dateimetadaten für eine Datei speichern.
Dies ist beabsichtigt, da Metastore oder Git-Cache-Meta genau für diesen Zweck vorgesehen sind und Backup-Dienstprogramme und Synchronisierungstools unterstützen.
(Tut mir leid, nur ein bisschen Spaß an Jakubs Antwort)
findder -printfErweiterung von GNU , und ich bin mir fast sicher, dass Metastore (ein C-Projekt) noch mehr Arbeit ist, um portabel zu machen. Sehr unglücklich. Ich werde hier zurück posten, wenn ich herausfinde, dass sich diese Situation ändert.
NEIN , Git speichert solche (Meta-) Informationen einfach nicht , es sei denn, Sie verwenden Tools von Drittanbietern wie Metastore oder Git-Cache-Meta. Der einzige Zeitstempel, der gespeichert wird, ist die Zeit, zu der Patch / Änderung erstellt wurde (Autorenzeit), und die Zeit, zu der das Commit erstellt wurde (Committer-Zeit).
Dies ist beabsichtigt, da Git ein Versionskontrollsystem ist, kein Sicherungsdienstprogramm oder Synchronisationstool.
UPDATE : TL; DR: git selbst speichert keine Originalzeiten, aber einige Lösungen umgehen dies durch verschiedene Methoden. git-restore-mtimeIst einer von ihnen:
https://github.com/MestreLion/git-tools/
Ubuntu / Debian: sudo apt install git-restore-mtime
Fedora / RHEL / CentOS:sudo yum install git-tools
Siehe meine andere Antwort für weitere Details
Vollständiger Haftungsausschluss: Ich bin der Autor von git-tools
Dieses Python-Skript kann hilfreich sein: Für jede Datei wird der Zeitstempel des letzten Commits angewendet, bei dem die Datei geändert wurde:
Unten finden Sie eine wirklich nackte Version des Skripts. Für den tatsächlichen Gebrauch empfehle ich dringend eine der robusteren Versionen oben:
#!/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
Alle Versionen analysieren das vollständige Protokoll, das von einem einzelnen erstellt wurde git whatchanged Befehl Dies ist hunderte Male schneller als das Lopping für jede Datei. Unter 4 Sekunden für Git (24.000 Commits, 2.500 Dateien) und weniger als 1 Minute für Linux-Kernel (40.000 Dateien, 300.000 Commits)
$ 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 APIWürde es Ihnen etwas ausmachen, uns zu sagen, welche Version von Python benötigt wird? Ich benutze 3.3.3
strIn Python 2 entspricht dies bytestringPython 3, während strPython 3 unicodePython 2 entspricht. Können Sie dieses Problem bitte unter github.com/MestreLion/git-tools/issues melden ?
Ich habe schon seit einiger Zeit mit Git- und Datei-Zeitstempeln gekämpft.
Habe einige deiner Ideen getestet und meine eigenen schrecklich großen und vorgänger- / ramlastigen Skripte erstellt, bis ich (in einem Git-Wiki) ein Skript in Perl gefunden habe, das fast das tut, was ich wollte. https://git.wiki.kernel.org/index.php/ExampleScripts
Und ich wollte in der Lage sein, die letzte Änderung von Dateien basierend auf den Festschreibungsdaten beizubehalten.
Nach einigen Anpassungen kann das Skript das Erstellungs- und Änderungsdatum von 200.000 Dateien in ca. 2-3 Minuten ändern .
#!/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;
Angenommen, Ihre Repositorys verfügen nicht über mehr als 10.000 Dateien. Die Ausführung sollte Sekunden dauern, damit Sie sie an die Kasse, den Pull oder andere grundlegende Git-Hooks anschließen können.
Hier ist meine Lösung, die Pfade berücksichtigt, die Leerzeichen enthalten:
#! /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
Beachten Sie, dass dies nicht die Zeit in git logAnspruch nimmt, die gemeldet wird, sondern die vom System gemeldete Zeit. Wenn Sie die Zeit seit dem Festschreiben der Dateien benötigen, verwenden Sie git logstattdessen die Lösungdate -r
Native Git verfügt nicht über die Funktionalität, kann jedoch durch Hook-Skripte oder Tools von Drittanbietern erreicht werden.
Ich habe es versucht metastore. Es ist sehr schnell, aber ich mag die Notwendigkeit der Installation nicht und dass Metadaten nicht im Nur-Text-Format gespeichert werden. git-cache-metaist ein einfaches Tool, das ich ausprobiert habe, aber es ist für große Repos extrem langsam (für ein Repo mit Zehntausenden von Dateien dauert das Aktualisieren der Metadatendatei Minuten) und kann plattformübergreifende Kompatibilitätsprobleme aufweisen.setgitpermsund andere Ansätze haben auch ihre Mängel, die ich nicht mag.
Endlich habe ich ein Hook-Skript für diesen Job erstellt: git-store-meta . Es hat eine sehr leichte Abhängigkeit (* nix shell ,, sortundperl , die von git und optional benötigt wird chown, chgrpund touch), so dass für eine Plattform, auf der git ausgeführt werden kann, keine zusätzliche Leistung installiert werden muss, was für ein Repo mit Zehntausenden wünschenswert ist Bei Dateien dauert das Aktualisieren der Metadatendatei <10 Sekunden (obwohl das Erstellen länger dauert), das Speichern von Daten im Nur-Text-Format und das Anpassen der zu "speichern" oder "geladenen" Metadaten ist anpassbar .
Es hat gut für mich funktioniert. Versuchen Sie dies, wenn Sie mit Metastore, Git-Cache-Meta und anderen Ansätzen nicht zufrieden sind.
Ich hoffe, Sie schätzen die Einfachheit:
# 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
Für die Windows-Umgebung habe ich in Delphi 10.1 Berlin eine kleine (schnelle und schmutzige) EXE-Datei geschrieben, die alle Dateidaten im Quellbaum in der Datei .gitfilattr sammelt und sie erneut auf den überprüften Quellbaum anwenden kann.
Natürlich teile ich den Code in GitHub:
https://github.com/michaschumann/gitfiledates/blob/master/gitFileDates.dpr
Ich verwende es in meinem Build-System, das auf GitLab-Läufern basiert.
In meiner (und der anderer) Interpretation des OP gibt es einige Unklarheiten darüber, ob dies die Festschreibungszeit oder etwas anderes bedeutet. Unter der Annahme, dass dies Festschreibungszeit bedeutet, funktioniert dieser einfache Einzeiler unter Linux (basierend auf dem Antwortausschnitt von Dietrich Epp ):
git ls-files | xargs -I{} bash -c 'touch "{}" --date=@$(git log -n1 --pretty=format:%ct -- "{}")'
Es gibt jedoch differenziertere Antworten (einschließlich Git-Hooks), die von einem Kommentar zur ursprünglichen Frage von Cregox verknüpft sind.
--date=@foo
Mit GNU-Tools.
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)
In CentOS 7 haben Sie /usr/share/doc/rsync-*/support/git-set-file-timesund in Debian (und Derivaten) das gleiche Skript /usr/share/doc/rsync/scripts/git-set-file-times.gz, das Original stammt von Eric Wong und ist hier https://yhbt.net/git-set-file-times .
Es funktioniert schneller als andere hier erwähnte Beispiele, und es ist möglicherweise praktischer, es bereits in Ihrer Linux-Distribution zu haben.
Hier ist meins.
Ein bisschen schneller als einige andere, da ich nicht für jede gefundene Datei 'get log' aufrufe; Rufen Sie stattdessen einmal 'git log' auf und wandeln Sie diese Ausgabe in Touch-Befehle um.
Es wird Fälle geben, in denen zu viele aufgelistete Dateien in einem Commit vorhanden sind, um in einen einzelnen Shell-Befehlspuffer zu passen. Führen Sie "getconf ARG_MAX" aus, um die maximale Länge eines Befehls in Byte anzuzeigen. Bei meiner Debian-Installation sind es 2 MB, was ausreichend ist.
# 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 -
Beschreibung nach Zeile:
In Bezug auf die Geschwindigkeit werden 5 Sekunden 1700 für 6500 Dateien in 700 Verzeichnissen festgeschrieben.