Ist das halbgeheime leere Baumobjekt von git zuverlässig und warum gibt es keinen symbolischen Namen dafür?

125

Git hat einen bekannten oder zumindest bekannten leeren Baum, dessen SHA1 lautet:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(Sie können dies in jedem Repo sehen, auch in einem neu erstellten, mit git cat-file -tund git cat-file -p).

Wenn Sie hart arbeiten und sehr vorsichtig sind, können Sie diesen leeren Baum verwenden, um ein Verzeichnis zu speichern, das keine Dateien enthält (siehe Antwort auf Wie füge ich einem Git-Repository ein leeres Verzeichnis hinzu ), obwohl dies keine gute Idee ist.

Es ist nützlicher als ein Argument dafür git diff-tree, was einer der Beispiel-Hooks tut.

Was ich mich frage ist,

  1. Wie zuverlässig ist dies - dh wird in einer zukünftigen Version von Git kein Git-Objekt nummeriert sein 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Warum gibt es keinen symbolischen Namen für den leeren Baum (oder gibt es einen?).

(Eine schnelle und schmutzige Möglichkeit, einen symbolischen Namen zu erstellen, besteht darin, den SHA1 einzugeben, z .git/Nulltree. B .. Leider müssen Sie dies für jedes Repo tun. Scheint besser, nur die magische Zahl in Skripten usw. einzufügen. Ich habe nur eine allgemeine Abneigung zu magischen Zahlen.)

torek
quelle
3
Nur um sich an den Hash zu erinnern ;-) Verwenden Sie SHA1 ("Baum 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 ist NUL-Zeichen)
Thomas
4
@Thomas: Die git hash-object -t tree /dev/nullMethode (aus der Antwort von VonC unten) hat den Vorteil, dass SHA-1 nicht fest codiert wird, falls eine zukünftige Version von git beispielsweise auf SHA-2 umschaltet. (Ich werde nicht versuchen vorherzusagen, wann das passieren könnte. :-) Es wäre einfacher, Mercurial auf SHA-2 umzustellen, da sie Platz dafür gelassen haben.)
torek
Natürlich hast du recht, aber es ist ein gutes Stück "nutzloses Wissen" und kann es auf jeden Fall für andere hilfreich sein?!
Thomas
2
@Thomas: Es sieht so aus, als ob die Umstellung des Hash-Algorithmus früher als erwartet erfolgen könnte . :-)
Torek
Apropos "eine zukünftige Version von Git", ich denke, Sie werden an meiner neuesten (Dez. 2017) Bearbeitung meiner Antwort von 2012 interessiert sein
VonC

Antworten:

104

Dieser Thread erwähnt:

Wenn Sie sich nicht an den leeren Baum sha1 erinnern, können Sie ihn jederzeit ableiten mit:

git hash-object -t tree /dev/null

Oder, wie Ciro Santilli in den Kommentaren vorschlägt :

printf '' | git hash-object --stdin -t tree

Oder, wie hier zu sehen , von Colin Schimmelfing :

git hash-object -t tree --stdin < /dev/null

Ich denke, es ist sicherer, eine Variable mit dem Ergebnis dieses Befehls als Ihren leeren sha1-Baum zu definieren (anstatt sich auf einen "bekannten Wert" zu verlassen).

Hinweis: Git 2.25.1 (Februar 2020) schlägt in Commit 9c8a294 vor :

empty_tree=$(git mktree </dev/null)
# Windows:
git mktree <NUL

Und fügt hinzu:

Als historische Anmerkung wurde der Funktion, die jetzt als repo_read_object_file()leerer Baum bekannt ist, der leere Baum in 346245a1bb (" Hardcode des leeren Baumobjekts", 13.02.2008, Git v1.5.5-rc0 - Zusammenführen ) und der jetzt bekannten Funktion beigebracht wie oid_object_info()der leere Baum in c4d9986f5f gelehrt wurde (" sha1_object_info: cached_objectStore auch untersuchen", 2011-02-07, Git v1.7.4.1).


Beachten Sie, dass SHA1 auf einem GitHub-Repo angezeigt wird, wenn der Autor möchte, dass sein erstes Commit leer ist (siehe Blog-Beitrag " Wie ich meine Git-Repositorys initialisiere "):

$ GIT_AUTHOR_DATE="Thu, 01 Jan 1970 00:00:00 +0000" GIT_COMMITTER_DATE="Thu, 01 Jan 1970 00:00:00 +0000" git commit --allow-empty -m 'Initial commit'

Werde dir geben:

Leerer Baum SHA1

(Siehe den Baum SHA1?)

Sie können Ihren vorhandenen Verlauf sogar zusätzlich zu diesem leeren Commit neu erstellen (siehe " git: Wie füge ich ein Commit als erstes ein und verschiebe alle anderen? ").

In beiden Fällen verlassen Sie sich nicht auf den genauen SHA1-Wert dieses leeren Baums.
Sie folgen einfach einer bewährten Methode und initialisieren Ihr Repo mit einem ersten leeren Commit .


Das zu tun:

git init my_new_repo
cd my_new_repo
git config user.name username
git config user.email email@com

git commit --allow-empty -m "initial empty commit"

Dadurch wird ein Commit mit einem SHA1 generiert, der für Ihr Repo, Ihren Benutzernamen, Ihre E-Mail-Adresse und Ihr Erstellungsdatum spezifisch ist (was bedeutet, dass der SHA1 des Commits selbst jedes Mal anders ist).
Der Baum, auf den durch dieses Commit verwiesen wird, 4b825dc642cb6eb9a060e54bf8d69288fbee4904ist jedoch der leere Baum SHA1.

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <[email protected]> 1381232247 +0200
committer VonC <[email protected]> 1381232247 +0200

    initial empty commit

So zeigen Sie nur den Baum eines Commits an (zeigen Sie den Commit-Baum SHA1 an):

git show --pretty=format:%T 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
4b825dc642cb6eb9a060e54bf8d69288fbee4904

Wenn dieses Commit, das auf einen leeren Baum verweist, tatsächlich Ihr erstes Commit ist, können Sie diesen leeren Baum SHA1 mit folgendem Befehl anzeigen:

git log --pretty=format:%h --reverse | head -1 | xargs git show --pretty=format:%T
4b825dc642cb6eb9a060e54bf8d69288fbee4904

(und das funktioniert sogar unter Windows mit Gnu On Windows- Befehlen)


Wie unten kommentiert , git diff <commit> HEADzeigt dies mit Ihre gesamte Datei im aktuellen Zweig HEAD an:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Hinweis: Dieser leere Baumwert ist formal in definiert cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Seit Git 2.16 (Q1 2018) wird es in einer Struktur verwendet, die nicht mehr an (nur) SHA1 gebunden ist, wie in commit eb0ccfd zu sehen :

Wechseln Sie zwischen leeren Baum- und Blob-Lookups, um die Hash-Abstraktion zu verwenden

Wechseln Sie die Verwendung von empty_tree_oidund empty_blob_oid, um die current_hashAbstraktion zu verwenden, die den aktuell verwendeten Hash-Algorithmus darstellt.

Weitere Informationen finden Sie unter " Warum verwendet Git kein moderneres SHA? ": Es ist SHA-2 seit Git 2.19 (Q3 2018).


Mit Git 2.25 (Q1 2020) bereiten sich Tests auf einen SHA-2-Übergang vor und beziehen den leeren Baum mit ein.

Sehen Sie verpflichten fa26d5e , begehen cf02be8 , begehen 38ee26b , begehen 37ab8eb , begehen 0370b35 , begehen 0253e12 , begehen 45e2ef2 , begehen 79b0edc , begehen 840624f , begehen 32a6707 , begehen 440bf91 , begehen 0b408ca , begehen 2eabd38 (28. Oktober 2019), und verpflichten 1bcef51 , verpflichten ecde49b ( 05.10.2019 ) von brian m. Carlson ( bk2204) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 28014c1, 10. November 2019)

t/oid-info: Füge leere Baum- und leere Blob-Werte hinzu

Unterzeichnet von: brian m. Carlson

Die Testsuite wird schließlich lernen, wie man mit einem anderen Algorithmus als SHA-1 läuft. Bringen Sie der test_oidFunktionsfamilie in Vorbereitung bei, wie Sie die Werte für leere Blobs und leere Bäume nachschlagen, damit sie verwendet werden können.

Also t/oid-info/hash-infojetzt beinhaltet:

rawsz sha1:20
rawsz sha256:32

hexsz sha1:40
hexsz sha256:64

zero sha1:0000000000000000000000000000000000000000
zero sha256:0000000000000000000000000000000000000000000000000000000000000000

algo sha1:sha1
algo sha256:sha256

empty_blob sha1:e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
empty_blob sha256:473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813

empty_tree sha1:4b825dc642cb6eb9a060e54bf8d69288fbee4904
empty_tree sha256:6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321

Der SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321" ist der neue 4b825dc642cb6eb9a060e54bf8d69288fbee4904leere SHA1 " " -Baum.

VonC
quelle
@torek: Ich habe einige Beispiele für die erste Best Practice für leeres Commit hinzugefügt, um diesen leeren Baum SHA1 zu veranschaulichen.
VonC
Nun, eines der Ziele ist es, den Hash "leerer Baum" als Argument für git diff-treeeinige Skripte zu verwenden, die ich schreibe. Es gibt keine Garantie dafür, dass das Repo ein anfängliches leeres Commit enthält. Ich frage mich nur, ob diese Skripte eines Tages kaputt gehen könnten.
Torek
1
Wenn Sie -wan übergeben git hash-object, wird das Objekt in dem Repository erstellt, für das es ausgeführt wird, und dadurch wird der leere Baum in dem Repository neu erstellt, für das Sie ausgeführt werden, falls es in Zukunft jemals verschwinden sollte.
Javawizard
Wenn Sie vor dem ersten Commit mit Rebase gehen möchten, können Sie Git Rebase --root
GergelyPolonkai
1
Oder wenn Sie die Magie der Pfeifen anstelle der Magie von /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
3

Ich habe einen Blog-Beitrag mit zwei verschiedenen Methoden zum Auffinden des Hash geschrieben: http://colinschimmelfing.com/blog/gits-empty-tree/

Wenn es sich aus irgendeinem Grund jemals ändern sollte, können Sie die beiden folgenden Möglichkeiten verwenden, um es zu finden. Ich würde mich jedoch ziemlich sicher fühlen , wenn ich den Hash in .bashrc-Aliasnamen usw. verwende, und ich glaube nicht, dass sich dies bald ändern wird. Zumindest wäre es wahrscheinlich eine Hauptversion von Git.

Die zwei Möglichkeiten sind:

  1. Die Antwort oben: git hash-object -t tree --stdin < /dev/null
  2. Einfach ein leeres Repo einleiten und dann git write-treein diesem neuen Repo ausführen - der Hash wird von git write-tree ausgegeben.
schimmy
quelle
Wenn –-stdinich den Befehl mit fatal: Cannot open '–-stdin': No such file or directoryausführe, bekomme ich git 2.7.2. --stdinWenn Sie es jedoch ohne wie in VonCs Antwort ausführen, erhalten Sie den Hash-Wert
Sigy
Diese Antwort ist nicht sehr nützlich, da der Blog-Beitrag tot ist. Daher sind wir mit diesen Antworten auf SO im Allgemeinen nicht einverstanden.
Philip Whitehouse
1
@PhilipWhitehouse Der Blog-Beitrag ist nicht tot, aber in jedem Fall habe ich die beiden Möglichkeiten in meine Antwort aufgenommen - ich stimme zu, dass es ohne diese beiden Möglichkeiten keine gute Antwort wäre.
schimmy
3

Hier finden Sie die Antwort zum Erstellen eines leeren Baum-Commits, auch wenn das Repository noch nicht leer ist. https://stackoverflow.com/a/14623458/9361507

Aber ich bevorzuge "leer" als Tag, aber keinen Zweig. Einfacher Weg ist:

git tag empty $(git hash-object -t tree /dev/null)

Weil das Tag ohne Commit direkt auf Tree-ish verweisen kann. Um nun alle Dateien im Arbeitsbaum abzurufen:

git diff --name-only empty

Oder das gleiche mit stat:

git diff --stat empty

Alle Dateien als diff:

git diff empty

Überprüfen Sie Leerzeichen in allen Dateien:

git diff --check empty
Olleg
quelle
... aber die Verwendung der magischen Nummer in Ihrer Tag-Erstellung ist nur das Streichen unter den Teppich ( ohne die magische Nummer SHA-1)
RomainValeri
Nicht wahr. Ich habe tag verwendet, um auf das baumartige Objekt zu zeigen. Mittlerweile wird diese Baumstruktur von SHA-1 definiert. In Zukunft kann sie beispielsweise in SHA-256 usw. geändert werden (mit Repository-Migration). Aber das Tag wird das gleiche sein. :) Das Hauptmerkmal eines Tags besteht darin, auf das Objekt zu zeigen. Ein Tag kann SHA-1 intern oder etwas anderes verwenden, es handelt sich nur um Git-Interna.
Olleg
Ich verstehe das. Wenn Sie (oder jemand, der dies liest) (oder ein Skript , noch schlimmer) versuchen, es (Ihre erste Zeile) zu einem späteren Zeitpunkt anzuwenden, kann dies bei einem neuen Hash-Algorithmus fehlschlagen, bei dem Ihre erste Zeile durch einen ausgeführten Ausdruck ersetzt wird (Produzieren) Dieser Hash würde weiterhin Erfolg haben.
RomainValeri
Wenn Sie dies mit einer der Methoden zum automatischen Generieren des leeren Baum-Hash kombinieren, können Sie dies zukunftssicher machen (wie @RomainValeri vorschlägt). Wenn es jedoch nach mir git rev-parseginge , hätte ich neue Flags oder Schlüsselwörter oder ähnliches, um (a) den leeren Baum-Hash und (b) den Null-Commit-Hash zu erzeugen. Beides wäre in Skripten nützlich und würde vor den vorgeschlagenen SHA-256-Änderungen schützen.
Torek
Okey, verändert. Dies wird jedoch kein "einfachster Weg" sein. :)
Olleg