L'arbre vide semi-secret de git est-il fiable et pourquoi n'y a-t-il pas de nom symbolique pour lui?

125

Git a un arbre vide bien connu, ou du moins bien connu, dont SHA1 est:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(vous pouvez le voir dans n'importe quel dépôt, même nouvellement créé, avec git cat-file -tet git cat-file -p).

Si vous travaillez dur et que vous faites très attention, vous pouvez en quelque sorte utiliser cette arborescence vide pour stocker un répertoire qui n'a pas de fichiers (voir la réponse à Comment ajouter un répertoire vide à un dépôt git ), bien que ce ne soit pas vraiment une bonne idée.

C'est plus utile en tant qu'argument à git diff-tree, lequel des exemples de hooks fait.

Ce que je me demande c'est,

  1. dans quelle mesure est-ce fiable - c'est-à-dire, une future version de git n'aura-t-elle pas d'objet git numéroté 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. Pourquoi n'y a-t-il pas de nom symbolique pour l'arbre vide (ou y en a-t-il un?).

(Un moyen rapide et sale de créer un nom symbolique est de mettre le SHA1, par exemple .git/Nulltree. Malheureusement, vous devez le faire pour chaque dépôt. Il semble préférable de simplement mettre le nombre magique dans les scripts, etc. J'ai juste une aversion générale aux nombres magiques.)

Torek
la source
3
juste pour se souvenir du hachage ;-) utiliser SHA1 ("tree 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 est le caractère NUL)
Thomas
4
@Thomas: la git hash-object -t tree /dev/nullméthode (d'après la réponse de VonC ci-dessous) a l'avantage de ne pas coder en dur SHA-1, au cas où une future version de git passerait à SHA-2 par exemple. (Je ne vais pas essayer de prédire quand cela pourrait arriver. :-) Il serait plus facile de passer de Mercurial à SHA-2, car ils ont laissé de la place pour cela.)
torek le
de cause vous avez raison, mais c'est un bon morceau de "connaissance inutile" et peut-il être utile dans tous les cas à quelqu'un d'autre?!
Thomas le
2
@Thomas: il semble que le changement d'algorithme de hachage pourrait se produire plus tôt que prévu . :-)
torek
En parlant de "une future version de Git", je pense que vous serez intéressé par ma dernière modification (décembre 2017) à ma réponse de 2012: stackoverflow.com/revisions/9766506/7
VonC

Réponses:

104

Ce fil mentionne:

Si vous ne vous souvenez pas de l'arbre vide sha1, vous pouvez toujours le dériver avec:

git hash-object -t tree /dev/null

Ou, comme le propose Ciro Santilli dans les commentaires :

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

Ou, comme on le voit ici , de Colin Schimmelfing :

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

Donc je suppose qu'il est plus sûr de définir une variable avec le résultat de cette commande comme votre arbre sha1 vide (au lieu de compter sur une "valeur bien connue").

Remarque: Git 2.25.1 (février 2020) propose dans le commit 9c8a294 :

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

Et ajoute:

Comme note historique, la fonction maintenant connue sous le nom de repo_read_object_file()l'arbre vide a été enseignée dans 346245a1bb ("coder en dur l'objet arbre vide", 2008-02-13, Git v1.5.5-rc0 - merge ), et la fonction maintenant connue comme oid_object_info()on l'a appris l'arbre vide dans c4d9986f5f (" sha1_object_info: examine cached_objectstore too", 07/02/2011, Git v1.7.4.1).


Remarque, vous verrez que SHA1 apparaît sur un dépôt GitHub lorsque l'auteur veut que son premier commit soit vide (voir l'article de blog " Comment j'initialise mes dépôts 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'

Te donnera:

Arbre vide SHA1

(Voir l'arbre SHA1?)

Vous pouvez même rebaser votre historique existant en plus de ce commit vide (voir " git: comment insérer un commit comme premier, en décalant tous les autres? ")

Dans les deux cas, vous ne vous fiez pas à la valeur SHA1 exacte de cet arbre vide.
Vous suivez simplement une bonne pratique, initialisant votre repo avec un premier commit vide .


Pour faire ça:

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"

Cela générera un commit avec un SHA1 spécifique à votre repo, nom d'utilisateur, email, date de création (ce qui signifie que le SHA1 du commit lui-même sera différent à chaque fois).
Mais l'arborescence référencée par ce commit sera 4b825dc642cb6eb9a060e54bf8d69288fbee4904, l'arborescence vide SHA1.

git log --pretty=raw

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

    initial empty commit

Pour afficher uniquement l'arborescence d'un commit (afficher l'arbre de commit SHA1):

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

Si ce commit, référençant un arbre vide, est bien votre premier commit, vous pouvez montrer cet arbre vide SHA1 avec:

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

(et cela fonctionne même sous Windows, avec les commandes Gnu On Windows )


Comme commenté ci - dessous , en utilisant git diff <commit> HEAD, cela affichera tous vos fichiers dans la branche actuelle HEAD:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Remarque: cette valeur d'arbre vide est formellement définie dans cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Depuis Git 2.16 (Q1 2018), il est utilisé dans une structure qui n'est plus liée (uniquement) à SHA1, comme on le voit dans commit eb0ccfd :

Basculer vers l'arborescence vide et les recherches d'objets blob pour utiliser l'abstraction de hachage

Changez les utilisations de empty_tree_oidet empty_blob_oidutilisez l' current_hashabstraction qui représente l'algorithme de hachage en cours d'utilisation.

Pour en savoir plus, consultez " Pourquoi Git n'utilise-t-il pas un SHA plus moderne? ": C'est SHA-2 , depuis Git 2.19 (Q3 2018)


Avec Git 2.25 (Q1 2020), les tests se préparent à une transition SHA-2 et impliquent l'arbre vide.

Voir commettre fa26d5e , engager cf02be8 , engager 38ee26b , engager 37ab8eb , engager 0370b35 , engager 0253e12 , engager 45e2ef2 , engager 79b0edc , engager 840624f , engager 32a6707 , engager 440bf91 , engager 0b408ca , commettre 2eabd38 (28 octobre 2019), et engager 1bcef51 , commettre ecde49b (05 octobre 2019) par brian m. carlson ( bk2204) .
(Fusionné par Junio ​​C Hamano - gitster- dans commit 28014c1, 10 novembre 2019)

t/oid-info: ajouter une arborescence vide et des valeurs d'objets blob vides

Signé par: Brian M. Carlson

La suite de tests apprendra éventuellement à s'exécuter en utilisant un algorithme autre que SHA-1. Pour préparer cela, apprenez à la test_oidfamille de fonctions à rechercher les valeurs d'objets blob et d'arborescence vides afin de pouvoir les utiliser.

t/oid-info/hash-infoComprend donc maintenant:

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

Le SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321" est le nouvel 4b825dc642cb6eb9a060e54bf8d69288fbee4904arbre vide SHA1 " ".

VonC
la source
@torek: J'ai ajouté quelques exemples autour de la première meilleure pratique de commit vide pour illustrer cet arbre vide SHA1.
VonC
Eh bien, l'un des objectifs est d'utiliser le hachage «arbre vide» comme argument git diff-treedans certains scripts que j'écris. Il n'y a aucune garantie qu'il existe un commit initial vide dans le dépôt. Je me demande donc si ces scripts pourraient finir par se rompre un jour.
torek
1
Si vous passez -wà git hash-object, il créera l'objet dans le référentiel sur lequel il est exécuté, et cela recréera l'arborescence vide dans le référentiel sur lequel vous courrez s'il devait disparaître à l'avenir.
javawizard
Si vous voulez aller avant le premier commit en utilisant rebase, vous pouvez utiliser git rebase --root
GergelyPolonkai
1
Ou si vous préférez la magie des tuyaux au lieu de la magie de /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
3

J'ai rédigé un article de blog avec deux façons différentes de trouver le hachage: http://colinschimmelfing.com/blog/gits-empty-tree/

S'il devait changer pour une raison quelconque, vous pouvez utiliser les deux méthodes ci-dessous pour le trouver. Cependant, je me sentirais assez confiant en utilisant le hachage dans les alias .bashrc, etc., et je ne pense pas que cela changera de si tôt. À tout le moins, ce serait probablement une version majeure de git.

Les deux moyens sont:

  1. La réponse ci-dessus: git hash-object -t tree --stdin < /dev/null
  2. Il suffit de lancer un dépôt vide puis de l'exécuter git write-treedans ce nouveau dépôt - le hachage sera généré par git write-tree.
schimmy
la source
Lancer la commande avec –-stdinme donne fatal: Cannot open '–-stdin': No such file or directorygit 2.7.2. Cependant, l'exécuter sans --stdincomme dans la réponse de VonC donne la valeur de hachage
sigy
Cette réponse n'est pas très utile maintenant que le blog est mort. D'où pourquoi nous n'approuvons généralement pas ces réponses sur SO.
Philip Whitehouse
1
@PhilipWhitehouse, le billet de blog n'est pas mort, mais dans tous les cas, j'ai inclus les deux façons dans ma réponse - je suis d'accord que sans inclure ces deux façons, ce ne serait pas une bonne réponse.
schimmy
3

Voici la réponse sur la façon de créer un commit d'arbre vide même dans le cas où le référentiel n'est pas déjà vide. https://stackoverflow.com/a/14623458/9361507

Mais je préfère "vide" pour être tag, mais pas une branche. Un moyen simple est:

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

Parce que la balise peut pointer directement vers tree-ish, sans validation. Maintenant, pour obtenir tous les fichiers dans l'arborescence de travail:

git diff --name-only empty

Ou la même chose avec stat:

git diff --stat empty

Tous les fichiers comme diff:

git diff empty

Vérifiez les espaces dans tous les fichiers:

git diff --check empty
Olleg
la source
... mais utiliser le nombre magique dans la création de votre tag, c'est simplement brosser sous le tapis la question même ( sans utiliser le numéro magique SHA-1)
RomainValeri
Pas vrai. J'ai utilisé une balise pour pointer vers l'objet tree-ish. À présent, cet arbre est défini par SHA-1, à l'avenir il peut être changé, par exemple, en SHA-256 et ainsi de suite (avec la migration du référentiel). Mais le tag sera le même. :) La principale caractéristique d'une balise est de pointer vers l'objet. Un tag peut utiliser SHA-1 en interne ou quelque chose d'autre, c'est une question de Git interne uniquement.
Olleg
Je comprends ça. Mais si vous (ou quiconque lisant ceci) (ou un script , pire encore) essayez de l'appliquer (votre première ligne) ultérieurement, cela pourrait échouer sur un nouvel algorithme de hachage, où le remplacement de votre première ligne par une expression exécutée (produisant ce hachage) continuerait à réussir.
RomainValeri
Si vous combinez cela avec l'une des méthodes de génération automatique du hachage d'arbre vide, vous pouvez le pérenniser (comme le suggère @RomainValeri). Cependant, si cela ne tenait qu'à moi, git rev-parsej'aurais de nouveaux indicateurs ou mots clés ou quelque chose du genre, pour produire (a) le hachage d'arbre vide et (b) le hachage null-commit. Ces deux éléments seraient utiles dans les scripts et protégeraient contre les modifications proposées de SHA-256.
torek
Okey, changé. Mais ce ne sera pas "la manière la plus simple". :)
Olleg