L'oggetto albero segreto semi-segreto di git è affidabile e perché non esiste un nome simbolico per esso?


125

Git ha un albero vuoto ben noto, o almeno una specie di ben noto, il cui SHA1 è:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(puoi vederlo in qualsiasi repository, anche di recente creazione, con git cat-file -te git cat-file -p).

Se lavori sodo e stai molto attento, puoi in qualche modo usare questo albero vuoto per memorizzare una directory che non ha file (vedi risposta a Come aggiungere una directory vuota a un repository git ), anche se non è proprio una grande idea.

È più utile come argomento a git diff-treecui fa uno degli hook di esempio.

Quello che mi chiedo è

  1. quanto è affidabile, ovvero una versione futura di git non avrà un oggetto git numerato 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Perché non esiste un nome simbolico per l'albero vuoto (o ce n'è uno?).

(Un modo rapido e sporco per creare un nome simbolico è quello di inserire SHA1, ad esempio .git/Nulltree. Sfortunatamente devi farlo per ogni repository. Sembra meglio inserire il numero magico negli script, ecc. Ho solo un'avversione generale ai numeri magici.)


3
solo per ricordare l'hash ;-) usa SHA1 ("albero 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 è il carattere NUL)
Thomas

4
@Thomas: il git hash-object -t tree /dev/nullmetodo (dalla risposta di VonC di seguito) ha il vantaggio di non codificare SHA-1, nel caso in cui alcune versioni future di git passino ad SHA-2, ad esempio. (Non tenterò di prevedere quando ciò potrebbe accadere. :-) Sarebbe più facile cambiare Mercurial in SHA-2, poiché hanno lasciato spazio per esso.)
torek

perché hai ragione ma è un buon pezzo di "conoscenza inutile" e può essere utile in ogni caso a chiunque altro ?!
Thomas,

2
@Thomas: sembra che il passaggio all'algoritmo hash potrebbe avvenire prima del previsto . :-)
torek

Parlando di "qualche futura versione di Git", penso che sarete interessati nel mio ultimo (dicembre 2017) di modifica al mio 2012 risposta: stackoverflow.com/revisions/9766506/7
VonC

Risposte:


104

Questa discussione menziona:

Se non ricordi l'albero vuoto sha1, puoi sempre derivarlo con:

git hash-object -t tree /dev/null

Oppure, come propone Ciro Santilli nei commenti :

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

O, come visto qui , da Colin Schimmelfing :

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

Quindi suppongo che sia più sicuro definire una variabile con il risultato di quel comando come il tuo albero sha1 vuoto (invece di fare affidamento su un "valore ben noto").

Nota: Git 2.25.1 (febbraio 2020) propone in commit 9c8a294 :

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

E aggiunge:

Come nota storica, la funzione ora conosciuta come è repo_read_object_file()stata insegnata all'albero vuoto in 346245a1bb ("hard-code l'oggetto albero vuoto", 13-02-2008, Git v1.5.5-rc0 - unione ), e la funzione ora nota come è oid_object_info()stato insegnato l'albero vuoto in c4d9986f5f (" sha1_object_info: esamina cached_objectanche il negozio", 2011-02-07, Git v1.7.4.1).


Noterai che SHA1 apparirà su alcuni repository GitHub quando l'autore vuole che il suo primo commit sia vuoto (vedi post sul blog " Come inizializzo i miei repository 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'

Ti darà:

Albero vuoto SHA1

(Vedi l'albero SHA1?)

Puoi persino rifare la cronologia esistente in cima a quel commit vuoto (vedi " git: come inserire un commit come primo, spostando tutti gli altri? ")

In entrambi i casi, non ti affidi all'esatto valore SHA1 di quell'albero vuoto.
Segui semplicemente una best practice, inizializzando il tuo repository con un primo commit vuoto .


Fare quello:

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"

Ciò genererà un commit con un SHA1 specifico per repository, nome utente, e-mail, data di creazione (il che significa che lo SHA1 del commit stesso sarà ogni volta diverso).
Ma l'albero a cui fa riferimento quel commit sarà 4b825dc642cb6eb9a060e54bf8d69288fbee4904, l'albero vuoto 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

Per mostrare solo l'albero di un commit (visualizza l'albero di commit SHA1):

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

Se quel commit, facendo riferimento a un albero vuoto, è davvero il tuo primo commit, puoi mostrare quell'albero vuoto SHA1 con:

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

(e funziona anche su Windows, con i comandi Gnu On Windows )


Come commentato di seguito , usando git diff <commit> HEAD, questo mostrerà tutti i tuoi file nel ramo corrente HEAD:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Nota: quel valore di albero vuoto è formalmente definito in cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Da Git 2.16 (Q1 2018), è usato in una struttura che non è più legata (solo) a SHA1, come visto in commit eb0ccfd :

Cambia le ricerche di alberi e BLOB vuoti per utilizzare l'astrazione dell'hash

Cambia gli usi empty_tree_oide empty_blob_oidusa l' current_hashastrazione che rappresenta l'attuale algoritmo di hash in uso.

Vedi di più su " Perché Git non usa un SHA più moderno? ": È SHA-2 , dal Git 2.19 (Q3 2018)


Con Git 2.25 (Q1 2020), i test si stanno preparando per una transizione SHA-2 e coinvolge l'albero vuoto.

Vedere commettere fa26d5e , commettere cf02be8 , commettere 38ee26b , commettere 37ab8eb , commettere 0370b35 , commettere 0253e12 , commettere 45e2ef2 , commettere 79b0edc , commettere 840624f , commettere 32a6707 , commettere 440bf91 , commettere 0b408ca , commettere 2eabd38 (28 ottobre 2019), e impegnarsi 1bcef51 , commettono ecde49b (05 ott 2019) di brian m. Carlson ( bk2204) .
(Unito da Junio ​​C Hamano - gitster- in commit 28014c1, 10 nov 2019)

t/oid-info: aggiungi albero vuoto e valori BLOB vuoti

Firmato-fuori-da: brian m. Carlson

Alla fine la suite di test imparerà come eseguire utilizzando un algoritmo diverso da SHA-1. In preparazione a ciò, insegna alla test_oidfamiglia di funzioni come cercare il blob vuoto e i valori dell'albero vuoti in modo che possano essere utilizzati.

Quindi t/oid-info/hash-infoora include:

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" è il nuovo SHA1 " 4b825dc642cb6eb9a060e54bf8d69288fbee4904" albero vuoto.


@torek: ho aggiunto alcuni esempi della prima best practice di commit vuota per illustrare quell'albero vuoto SHA1.
VonC

Bene, uno degli obiettivi è usare l'hash "albero vuoto" come argomento su git diff-treecui sto scrivendo alcuni script. Non esiste alcuna garanzia che sia presente un commit vuoto iniziale nel repository. Quindi mi chiedo solo se un giorno questi script potrebbero finire per rompersi.
Torek,

1
Se passi -wa git hash-object, creerà l'oggetto nel repository su cui è in esecuzione, e questo ricreare l'albero vuoto nel repository su cui stai correndo se dovesse mai andare via in futuro.
javawizard,

Se vuoi andare prima del primo commit usando rebase, puoi usare git rebase --root
GergelyPolonkai

1
O se preferisci la magia delle pipe invece della magia di /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

3

Ho scritto un post sul blog con due modi diversi di trovare l'hash: http://colinschimmelfing.com/blog/gits-empty-tree/

Se dovesse mai cambiare per qualche motivo, potresti usare i due modi seguenti per trovarlo. Tuttavia, mi sentirei abbastanza sicuro usando l'hash negli alias .bashrc, ecc., E non credo che cambierà presto. Per lo meno sarebbe probabilmente una versione importante di git.

I due modi sono:

  1. La risposta sopra: git hash-object -t tree --stdin < /dev/null
  2. Semplicemente inserendo un repository vuoto e quindi eseguendolo git write-treein quel nuovo repository: l'hash verrà generato da git write-tree.

Eseguire il comando con –-stdinmi dà fatal: Cannot open '–-stdin': No such file or directorycon git 2.7.2. Tuttavia, eseguirlo senza --stdincome nella risposta di VonC fornisce il valore hash
sigy

Questa risposta non è molto utile ora che il post sul blog è morto. Quindi perché non approviamo generalmente queste risposte su SO.
Philip Whitehouse,

1
@PhilipWhitehouse il post del blog non è morto, ma in ogni caso ho incluso i due modi nella mia risposta - sono d'accordo che senza includere quei due modi, non sarebbe una buona risposta.
schimmy,

3

Ecco la risposta su come creare commit albero vuoto anche nel caso in cui il repository non sia già vuoto. https://stackoverflow.com/a/14623458/9361507

Ma preferisco "vuoto" per essere tag, ma non un ramo. Il modo semplice è:

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

Perché il tag può puntare direttamente all'albero, senza commit. Ora per ottenere tutti i file nell'albero di lavoro:

git diff --name-only empty

O lo stesso con stat:

git diff --stat empty

Tutti i file come diff:

git diff empty

Controlla gli spazi bianchi in tutti i file:

git diff --check empty

... ma usare il numero magico nella creazione del tag significa semplicemente spazzare sotto il tappeto la vera questione della domanda ( non usare il numero magico SHA-1)
RomainValeri

Non vero. Ho usato il tag per indicare l'oggetto albero-ish. Ormai questo albero-ish è definito da SHA-1, in futuro può essere cambiato, ad esempio, in SHA-256 e così via (con migrazione del repository). Ma il tag sarà lo stesso. :) La caratteristica principale di un tag è di puntare all'oggetto. Un tag può usare SHA-1 internamente o qualcos'altro, è solo questione di interni Git.
Olleg

Capisco quello. Ma se tu (o chiunque legga questo) (o uno script , anche peggio) provi ad applicarlo (la tua prima riga) in un secondo momento, potresti fallire su un nuovo algoritmo di hash, dove sostituire la tua prima riga con un'espressione eseguita (producendo questo hash) continuerebbe ad avere successo.
Romain Valeri

Se lo combini con uno dei metodi per generare automaticamente l'hash dell'albero vuoto, puoi renderlo a prova di futuro (come suggerisce @RomainValeri). Tuttavia, se dipendesse da me, git rev-parseavrebbe nuove bandiere o parole chiave o qualcosa del genere, per produrre (a) l'hash dell'albero vuoto e (b) l'hash null-commit. Entrambi sarebbero utili negli script e proteggerebbero dalle modifiche proposte SHA-256.
Torek,

Okey, cambiato. Ma questo non sarà "un modo più semplice". :)
Olleg
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.