Czy półtajny pusty obiekt drzewa gita jest wiarygodny i dlaczego nie ma dla niego symbolicznej nazwy?


125

Git ma dobrze znane lub przynajmniej w pewnym stopniu dobrze znane, puste drzewo, którego SHA1 to:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(możesz to zobaczyć w każdym repozytorium, nawet nowo utworzonym, z git cat-file -ti git cat-file -p).

Jeśli ciężko pracujesz i jesteś bardzo ostrożny, możesz użyć tego pustego drzewa do przechowywania katalogu, który nie zawiera plików (zobacz odpowiedź na temat Jak dodać pusty katalog do repozytorium git ), chociaż nie jest to naprawdę dobry pomysł.

Jest to bardziej przydatne jako jeden argument, do git diff-treektórego służy jeden z przykładowych punktów zaczepienia.

Zastanawiam się,

  1. na ile jest to wiarygodne - tj. czy jakaś przyszła wersja gita nie będzie miała numeru obiektu git 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Dlaczego nie ma symbolicznej nazwy dla pustego drzewa (czy istnieje?).

(Szybkim i brudnym sposobem na stworzenie nazwy symbolicznej jest umieszczenie SHA1 np .git/Nulltree.. Niestety musisz to zrobić dla każdego repozytorium. Wydaje się, że lepiej jest po prostu umieścić magiczną liczbę w skryptach itp. Mam tylko ogólną niechęć do magicznych liczb.)


3
tylko po to, aby zapamiętać skrót ;-) użyj SHA1 ("drzewo 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 to znak NUL)
Thomas

4
@Thomas: git hash-object -t tree /dev/nullmetoda (z odpowiedzi VonC poniżej) ma tę zaletę, że nie zakoduje na stałe SHA-1, na przykład na wypadek, gdyby jakaś przyszła wersja git przełączyła się na SHA-2. (Nie zamierzam przewidywać, kiedy to się stanie. :-) Łatwiej byłoby zmienić Mercurial na SHA-2, ponieważ zostawili dla niego miejsce.)
torek

masz rację, ale jest to dobry fragment „Bezużytecznej wiedzy” i czy może być pomocny w każdym przypadku komukolwiek innemu ?!
Thomas

2
@Thomas: wygląda na to, że zmiana algorytmu skrótu może nastąpić wcześniej niż oczekiwano . :-)
torek

Mówiąc o „jakiejś przyszłej wersji Git”, myślę, że zainteresuje Cię moja najnowsza (z grudnia 2017 r.) Edycja mojej odpowiedzi z 2012 roku: stackoverflow.com/revisions/9766506/7
VonC

Odpowiedzi:


104

Ten wątek wspomina:

Jeśli nie pamiętasz pustego drzewa sha1, zawsze możesz go wyprowadzić za pomocą:

git hash-object -t tree /dev/null

Lub, jak proponuje Ciro Santilli w komentarzach :

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

Lub, jak widać tutaj , od Colina Schimmelfinga :

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

Więc wydaje mi się, że bezpieczniej jest zdefiniować zmienną z wynikiem tego polecenia jako puste drzewo sha1 (zamiast polegać na „dobrze znanej wartości”).

Uwaga: Git 2.25.1 (luty 2020) proponuje w zatwierdzeniu 9c8a294 :

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

I dodaje:

Jako notatka historyczna, funkcja znana obecnie jako repo_read_object_file()była nauczana pustego drzewa w 346245a1bb („ zakoduj na stałe pusty obiekt drzewa”, 2008-02-13, Git v1.5.5-rc0 - merge ), a funkcja znana teraz jak oid_object_info()nauczono pustego drzewa w c4d9986f5f („ sha1_object_info: zbadaj cached_objectrównież sklep”, 2011-02-07, Git v1.7.4.1).


Uwaga, zobaczysz, że SHA1 wyskakuje w niektórych repozytoriach GitHub, gdy autor chce, aby jego pierwsze zatwierdzenie było puste (zobacz post na blogu „ Jak zainicjować moje repozytoria Git ”):

$ 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'

Da tobie:

Puste drzewo SHA1

(Zobacz drzewo SHA1?)

Możesz nawet zmienić bazę swojej istniejącej historii na podstawie tego pustego zatwierdzenia (zobacz „ git: jak wstawić zatwierdzenie jako pierwsze, przesuwając wszystkie pozostałe? ”)

W obu przypadkach nie polegasz na dokładnej wartości SHA1 tego pustego drzewa.
Po prostu postępujesz zgodnie z najlepszą praktyką, inicjując repozytorium pierwszym pustym zatwierdzeniem .


Aby to zrobić:

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"

Spowoduje to wygenerowanie zatwierdzenia z SHA1 specyficznym dla twojego repozytorium, nazwy użytkownika, adresu e-mail, daty utworzenia (co oznacza, że ​​SHA1 samego zatwierdzenia będzie za każdym razem inny).
Ale drzewem, do którego odwołuje się to zatwierdzenie, będzie 4b825dc642cb6eb9a060e54bf8d69288fbee4904puste drzewo SHA1.

git log --pretty=raw

commit 9ed4ff9ac204f20f826ddacc3f85ef7186d6cc14
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904      <====
author VonC <vonc@laposte.net> 1381232247 +0200
committer VonC <vonc@laposte.net> 1381232247 +0200

    initial empty commit

Aby pokazać tylko drzewo zatwierdzenia (wyświetl drzewo zatwierdzeń SHA1):

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

Jeśli to zatwierdzenie, odwołujące się do pustego drzewa, jest rzeczywiście pierwszym zatwierdzeniem, możesz pokazać to puste drzewo SHA1 za pomocą:

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

(i to działa nawet w systemie Windows, z poleceniami Gnu w systemie Windows )


Jak skomentowano poniżej , użycie git diff <commit> HEADspowoduje wyświetlenie całego pliku w bieżącej gałęzi HEAD:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Uwaga: ta pusta wartość drzewa jest formalnie zdefiniowana w cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Od Git 2.16 (Q1 2018) jest używany w strukturze, która nie jest już powiązana z (tylko) SHA1, jak widać w commit eb0ccfd :

Przełącz wyszukiwania pustego drzewa i obiektów blob, aby użyć abstrakcji skrótu

Przełącz zastosowania empty_tree_oidi empty_blob_oidużyj current_hashabstrakcji reprezentującej aktualnie używany algorytm wyznaczania wartości skrótu.

Więcej informacji można znaleźć w artykule „ Dlaczego Git nie używa nowocześniejszego algorytmu SHA? ”: Jest to SHA-2 od wersji Git 2.19 (III kw. 2018 r.)


W Git 2.25 (Q1 2020) testy przygotowują do przejścia na SHA-2 i obejmują puste drzewo.

Zobacz popełnić fa26d5e , popełnić cf02be8 , popełnić 38ee26b , popełnić 37ab8eb , popełnić 0370b35 , popełnić 0253e12 , popełnić 45e2ef2 , popełnić 79b0edc , popełnić 840624f , popełnić 32a6707 , popełnić 440bf91 , popełnić 0b408ca , popełnić 2eabd38 (28 paź 2019) i popełnić 1bcef51 , zobowiązać ecde49b (5 października 2019) autor: brian m. carlson ( bk2204) .
(Scalony przez Junio ​​C Hamano - gitster- w zatwierdzeniu 28014c1, 10 listopada 2019)

t/oid-info: dodaj puste drzewo i puste wartości blob

Podpisał: brian m. Carlson

Zestaw testowy ostatecznie nauczy się, jak działać przy użyciu algorytmu innego niż SHA-1. Przygotowując się do tego, naucz test_oidrodzinę funkcji, jak wyszukiwać wartości pustego obiektu blob i pustego drzewa, aby można było z nich korzystać.

Więc t/oid-info/hash-infoteraz obejmuje:

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

SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321" jest nowym 4b825dc642cb6eb9a060e54bf8d69288fbee4904pustym drzewem SHA1 " ".


@torek: Dodałem kilka przykładów wokół pierwszej najlepszej praktyki dotyczącej pustego zatwierdzania, aby zilustrować to puste drzewo SHA1.
VonC

Cóż, jednym z celów jest użycie skrótu „pustego drzewa” jako argumentu git diff-treew niektórych skryptach, które piszę. Nie ma gwarancji, że w repozytorium znajduje się początkowe puste zatwierdzenie. Zastanawiam się więc, czy te skrypty mogą kiedyś się zepsuć.
torek

1
Jeśli przejdziesz -wdo git hash-object, utworzy obiekt w repozytorium, z którym jest uruchamiany, i to odtworzy puste drzewo w repozytorium, z którym walczysz, gdyby kiedykolwiek zniknęło w przyszłości.
javawizard

Jeśli chcesz przejść przed pierwszym zatwierdzeniem przy użyciu rebase, możesz użyć git rebase
--root

1
Lub jeśli wolisz magię fajek zamiast magii /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3

Napisałem post na blogu z dwoma różnymi sposobami znajdowania skrótu: http://colinschimmelfing.com/blog/gits-empty-tree/

Jeśli z jakiegoś powodu miałoby się to zmienić, możesz skorzystać z dwóch poniższych sposobów, aby go znaleźć. Jednak czułbym się całkiem pewnie, używając skrótu w aliasach .bashrc itp. I nie sądzę, aby to się zmieniło w najbliższym czasie. Przynajmniej będzie to prawdopodobnie główne wydanie gita.

Te dwa sposoby to:

  1. Odpowiedź powyżej: git hash-object -t tree --stdin < /dev/null
  2. Wystarczy zainicjować puste repozytorium, a następnie uruchomić git write-treeto nowe repozytorium - hash zostanie wyprowadzony przez git write-tree.

Uruchomienie polecenia z –-stdindaje mi fatal: Cannot open '–-stdin': No such file or directorygit 2.7.2. Jednak uruchomienie go bez, --stdinjak w odpowiedzi VonC, daje wartość skrótu
sigy

Ta odpowiedź nie jest zbyt przydatna teraz, gdy post na blogu jest martwy. Dlatego generalnie nie akceptujemy tych odpowiedzi w SO.
Philip Whitehouse

1
@PhilipWhitehouse wpis na blogu nie jest martwy, ale w każdym przypadku w mojej odpowiedzi zawarłem dwa sposoby - zgadzam się, że bez uwzględnienia tych dwóch nie byłaby to dobra odpowiedź.
schimmy,

3

Oto odpowiedź, jak utworzyć zatwierdzenie pustego drzewa nawet w przypadku, gdy repozytorium nie jest jeszcze puste. https://stackoverflow.com/a/14623458/9361507

Wolę jednak, aby „pusty” był tagiem, ale nie gałęzią. Prosty sposób to:

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

Ponieważ tag może wskazywać bezpośrednio na drzewo, bez zatwierdzenia. Teraz, aby pobrać wszystkie pliki w drzewie roboczym:

git diff --name-only empty

Lub to samo ze statystyką:

git diff --stat empty

Wszystkie pliki jako różnice:

git diff empty

Sprawdź spacje we wszystkich plikach:

git diff --check empty

... ale używanie magicznej liczby podczas tworzenia tagu to tylko szczotkowanie pod dywanikiem samego pytania ( nie używanie magicznej liczby SHA-1)
RomainValeri

Nie prawda. Użyłem tagu, aby wskazać obiekt drzewa. Do tej pory to drzewo jest zdefiniowane przez SHA-1, w przyszłości można je zmienić na przykład na SHA-256 i tak dalej (z migracją repozytorium). Ale tag będzie taki sam. :) Główną cechą tagu jest wskazywanie obiektu. Znacznik może używać SHA-1 wewnętrznie lub czegoś innego, jest to tylko kwestia wewnętrznych elementów Gita.
Olleg

Rozumiem. Ale jeśli ty (lub ktokolwiek to czyta) (lub skrypt , co gorsza) spróbujesz zastosować to (twoją pierwszą linię) w późniejszym czasie, może to zakończyć się niepowodzeniem w przypadku nowego algorytmu haszującego, w którym zastąpienie pierwszej linii wykonanym wyrażeniem (tworząc ten hash) będzie nadal działać.
RomainValeri

Jeśli połączysz to z jedną z metod automatycznego generowania pustego skrótu drzewa, możesz to zabezpieczyć w przyszłości (jak sugeruje @RomainValeri). Gdyby jednak zależało ode mnie, git rev-parsemiałbym nowe flagi lub słowa kluczowe lub coś w tym kierunku, aby wytworzyć (a) pusty hash drzewa i (b) hash null-commit. Oba byłyby przydatne w skryptach i chroniłyby przed proponowanymi zmianami SHA-256.
torek

Okey, zmienione. Ale to nie będzie „najprostsza droga”. :)
Olleg
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.