¿Es confiable el objeto de árbol vacío semisecreto de git, y por qué no hay un nombre simbólico para él?

125

Git tiene un árbol vacío bien conocido, o al menos un poco conocido, cuyo SHA1 es:

4b825dc642cb6eb9a060e54bf8d69288fbee4904

(puede ver esto en cualquier repositorio, incluso uno recién creado, con git cat-file -ty git cat-file -p).

Si trabaja duro y tiene mucho cuidado, puede usar este árbol vacío para almacenar un directorio que no tiene archivos (consulte la respuesta a Cómo agregar un directorio vacío a un repositorio git ), aunque en realidad no es una gran idea.

Es más útil como un argumento para git diff-tree, cuál de los ganchos de muestra lo hace.

Lo que me pregunto es

  1. ¿Qué tan confiable es esto, es decir, alguna versión futura de git no tendrá un objeto git numerado 4b825dc642cb6eb9a060e54bf8d69288fbee4904?
  2. ¿Por qué no hay un nombre simbólico para el árbol vacío (o hay uno?).

(Una manera rápida y sucia de crear un nombre simbólico es poner el SHA1, por ejemplo,. .git/NulltreeDesafortunadamente, tiene que hacer esto para cada repositorio. Parece mejor simplemente poner el número mágico en los guiones, etc. Simplemente tengo una aversión general a números mágicos)

torek
fuente
3
solo para recordar el hash ;-) use SHA1 ("árbol 0 \ 0") = 4b825dc642cb6eb9a060e54bf8d69288fbee4904 (\ 0 es el carácter NUL)
Thomas
44
@Thomas: el git hash-object -t tree /dev/nullmétodo (de la respuesta de VonC a continuación) tiene la ventaja de no codificar SHA-1, en caso de que alguna versión futura de git cambie a SHA-2, por ejemplo. (No voy a intentar predecir cuándo podría suceder eso :-) Sería más fácil cambiar Mercurial a SHA-2, ya que dejaron espacio para ello.)
torek
porque tienes razón, pero es un buen "conocimiento inútil" y ¿puede ser útil en cualquier caso para alguien más?
Thomas
2
@Thomas: parece que el cambio de algoritmo hash podría ocurrir antes de lo esperado . :-)
torek
Hablando de "alguna versión futura de Git", creo que le interesará mi última edición (diciembre de 2017) a mi respuesta de 2012: stackoverflow.com/revisions/9766506/7
VonC

Respuestas:

104

Este hilo menciona:

Si no recuerda el árbol vacío sha1, siempre puede derivarlo con:

git hash-object -t tree /dev/null

O, como Ciro Santilli propone en los comentarios :

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

O, como se ve aquí , de Colin Schimmelfing :

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

Así que supongo que es más seguro definir una variable con el resultado de ese comando como su árbol sha1 vacío (en lugar de confiar en un "valor bien conocido").

Nota: Git 2.25.1 (febrero de 2020) propone en commit 9c8a294 :

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

Y agrega:

Como nota histórica, la función ahora conocida como repo_read_object_file()se le enseñó el árbol vacío en 346245a1bb ("codificar el objeto del árbol vacío", 13/02/2008, Git v1.5.5-rc0 - fusionar ), y la función ahora conocida como oid_object_info()se le enseñó el árbol vacío en c4d9986f5f (" sha1_object_info: examine cached_objectstore también", 2011-02-07, Git v1.7.4.1).


Tenga en cuenta que verá que SHA1 aparece en algún repositorio de GitHub cuando el autor quiere que su primer commit esté vacío (vea la publicación del blog " Cómo inicializo mis repositorios de 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 regalaré:

Árbol vacío SHA1

(¿Ves el árbol SHA1?)

Incluso puede modificar su historial existente sobre esa confirmación vacía (consulte " git: ¿cómo insertar una confirmación como la primera, cambiando todas las demás? ")

En ambos casos, no confía en el valor SHA1 exacto de ese árbol vacío.
Simplemente siga una práctica recomendada, inicializando su repositorio con una primera confirmación vacía .


Para hacer eso:

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"

Eso generará un commit con un SHA1 específico para su repositorio, nombre de usuario, correo electrónico, fecha de creación (lo que significa que el SHA1 del commit en sí será diferente cada vez).
Pero el árbol al que hace referencia esa confirmación será 4b825dc642cb6eb9a060e54bf8d69288fbee4904el árbol vacío SHA1.

git log --pretty=raw

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

    initial empty commit

Para mostrar solo el árbol de una confirmación (mostrar el árbol de confirmación SHA1):

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

Si esa confirmación, haciendo referencia a un árbol vacío, es su primera confirmación, puede mostrar ese árbol vacío SHA1 con:

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

(y eso incluso funciona en Windows, con comandos Gnu en Windows )


Como se comenta a continuación , usando git diff <commit> HEAD, esto mostrará todo su archivo en la rama HEAD actual:

git diff --name-only 4b825dc642cb6eb9a060e54bf8d69288fbee4904 HEAD

Nota: ese valor de árbol vacío se define formalmente en cache.h.

#define EMPTY_TREE_SHA1_HEX \
    "4b825dc642cb6eb9a060e54bf8d69288fbee4904"

Desde Git 2.16 (Q1 2018), se usa en una estructura que ya no está vinculada a (solo) SHA1, como se ve en commit eb0ccfd :

Cambie las búsquedas de árbol y blob vacías para usar abstracción hash

Cambie los usos de empty_tree_oidy empty_blob_oidpara usar la current_hashabstracción que representa el algoritmo hash actual en uso.

Vea más en " ¿Por qué Git no usa SHA más moderno? ": Es SHA-2 , ya que Git 2.19 (Q3 2018)


Con Git 2.25 (Q1 2020), las pruebas se están preparando para una transición SHA-2 y están involucrando el árbol vacío.

Ver cometer fa26d5e , comprometerse cf02be8 , comprometerse 38ee26b , comprometerse 37ab8eb , comprometerse 0370b35 , comprometerse 0253e12 , comprometerse 45e2ef2 , comprometerse 79b0edc , comprometerse 840624f , comprometerse 32a6707 , comprometerse 440bf91 , comprometerse 0b408ca , comprometerse 2eabd38 (28 de octubre 2019), y comprometerse 1bcef51 , cometen ecde49b (05 oct 2019) por brian m. Carlson ( bk2204) .
(Fusionada por Junio ​​C Hamano - gitster- en commit 28014c110 nov 2019)

t/oid-info: agrega valores de árbol vacío y blob vacío

Firmado por: brian m. carlson

Eventualmente, el paquete de prueba aprenderá a ejecutar utilizando un algoritmo distinto de SHA-1. En preparación para esto, enseñe a la test_oidfamilia de funciones cómo buscar los valores de blob y árbol vacíos para que puedan usarse.

Entonces t/oid-info/hash-infoahora incluye:

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

El SHA2 " 6ef19b41225c5369f1c104d45d8d85efa9b057b53b14b4b9b939dd74decc5321" es el nuevo 4b825dc642cb6eb9a060e54bf8d69288fbee4904árbol vacío SHA1 " ".

VonC
fuente
@torek: He agregado algunos ejemplos sobre la primera práctica de confirmación vacía para ilustrar ese árbol vacío SHA1.
VonC
Bueno, uno de los objetivos es utilizar el hash "árbol vacío" como argumento git diff-treeen algunos scripts que estoy escribiendo. No hay garantía de que haya una confirmación inicial vacía en el repositorio. Así que me pregunto si estos scripts podrían terminar rompiéndose algún día.
torek
1
Si pasa -wa git hash-object, creará el objeto en el repositorio contra el que se ejecuta, y eso recrearía el árbol vacío en el repositorio contra el que se está ejecutando si alguna vez desapareciera en el futuro.
javawizard
Si quieres ir antes del primer commit usando rebase, puedes usar git rebase --root
GergelyPolonkai
1
O si prefieres la magia de las pipas en lugar de la magia de /dev/null: printf '' | git hash-object --stdin -t tree:)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
3

Escribí una publicación de blog con dos formas diferentes de encontrar el hash: http://colinschimmelfing.com/blog/gits-empty-tree/

Si alguna vez cambiara por alguna razón, podría usar las dos formas siguientes para encontrarlo. Sin embargo, me sentiría bastante seguro usando el hash en los alias .bashrc, etc., y no creo que cambie pronto. Por lo menos, probablemente sería una gran versión de git.

Las dos formas son:

  1. La respuesta anterior: git hash-object -t tree --stdin < /dev/null
  2. Simplemente iniciando un repositorio vacío y luego ejecutándose git write-treeen ese nuevo repositorio: el hash será generado por git write-tree.
schimmy
fuente
Ejecutar el comando con –-stdinme da fatal: Cannot open '–-stdin': No such file or directorycon git 2.7.2. Sin embargo, ejecutarlo sin --stdincomo en la respuesta de VonC da el valor hash
sigy
Esta respuesta no es muy útil ahora que la publicación del blog está muerta. Por lo tanto, por qué generalmente no aprobamos estas respuestas en SO.
Philip Whitehouse el
1
@PhilipWhitehouse la publicación del blog no está muerta, pero en cualquier caso incluí las dos formas en mi respuesta: estoy de acuerdo en que sin incluir esas dos formas, no sería una buena respuesta.
schimmy
3

Aquí está la respuesta sobre cómo crear una confirmación de árbol vacía, incluso en el caso de que el repositorio aún no esté vacío. https://stackoverflow.com/a/14623458/9361507

Pero prefiero "vacío" para ser etiqueta, pero no una rama. La forma simple es:

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

Porque la etiqueta puede apuntar a tree-ish directamente, sin confirmación. Ahora para obtener todos los archivos en el árbol de trabajo:

git diff --name-only empty

O lo mismo con stat:

git diff --stat empty

Todos los archivos como diff:

git diff empty

Verifique los espacios en blanco en todos los archivos:

git diff --check empty
Olleg
fuente
... pero usar el número mágico en la creación de su etiqueta es solo rozar debajo de la alfombra el tema de la pregunta ( no usar el número mágico SHA-1)
RomainValeri
No es verdad. Usé la etiqueta para señalar el objeto tree-ish. Por ahora, este árbol-ish está definido por SHA-1, en el futuro puede cambiarse, por ejemplo, a SHA-256 y así sucesivamente (con migración de repositorio). Pero la etiqueta será la misma. :) La característica principal de una etiqueta es apuntar al objeto. Una etiqueta puede usar SHA-1 internamente o alguna otra cosa, solo se trata de aspectos internos de Git.
Olleg
Lo entiendo. Pero si usted (o alguien que lee esto) (o un script , aún peor) intenta aplicarlo (su primera línea) en un momento posterior, podría fallar en un nuevo algoritmo hash, donde reemplazar su primera línea con una expresión ejecutada (produciendo este hash) seguiría teniendo éxito.
RomainValeri
Si combina esto con uno de los métodos para generar el hash de árbol vacío automáticamente, puede prepararlo para el futuro (como sugiere @RomainValeri). Sin embargo, si fuera por mí, git rev-parsetendría nuevas banderas o palabras clave o algo por el estilo, para producir (a) el hash de árbol vacío y (b) el hash de confirmación nula. Ambos serían útiles en scripts y protegerían contra los cambios propuestos SHA-256.
torek
Okey, cambiado. Pero esto no será "una forma más simple". :)
Olleg