2011-08-29 9 views
103

Los valores hash SHA1 almacenados en los objetos de árbol (devuelto por git ls-tree) no coinciden con los valores hash SHA1 del contenido del archivo (tal como lo devuelve sha1sum)¿Cómo calcula git hashes de archivos?

$ git cat-file blob 4716ca912495c805b94a88ef6dc3fb4aff46bf3c | sha1sum 
de20247992af0f949ae8df4fa9a37e4a03d7063e - 

¿Cómo hash de archivo git de cómputo? ¿Comprime el contenido antes de calcular el hash?

+11

Consulte [asignación de Git SHA1 sin Git] (http://stackoverflow.com/questions/552659/assigning-git-sha1s-without-git) – sanmai

+1

Para obtener más información, también consulte http://progit.org/book /ch9-2.html – netvope

+5

El enlace de netvope parece estar muerto ahora. Creo que esta es la nueva ubicación: http://git-scm.com/book/es/Git-Internals-Git-Objects que es §9.2 de http://git-scm.com/book – Rhubbarb

Respuesta

97

Git prefijos el objeto con "blob", seguido por la longitud (como un número entero legible), seguido por un carácter NUL

$ echo -e 'blob 14\0Hello, World!' | shasum 8ab686eafeb1f44702738c8b0f24f2567c36da6d

Fuente: http://alblue.bandlem.com/2011/08/git-tip-of-week-objects.html

+1

También vale la pena mencionar que reemplaza "\ r \ n" con "\ n", pero deja aisladas las "\ r" s. – user420667

+3

^corrección al comentario anterior: * a veces * git hace el reemplazo anterior, dependiendo de la configuración de eol/autocrlf. – user420667

+1

También puede comparar esto con la salida de ['echo 'Hello, World!' | git hash-object --stdin'] (https://git-scm.com/docs/git-hash-object#_options). Opcionalmente puede especificar '--no-filters' para asegurarse de que no ocurra ninguna conversión crlf, o especifique' --path = somethi.ng' para permitir que git use el filtro especificado a través de 'gitattributes' (también @ user420667). Y '-w' para realmente enviar el blob a' .git/objects' (si _are_ en un repositorio de git). –

29

Solo estoy expandiendo la respuesta por @Leif Gruenwoldt y detallando lo que está en el reference provisto por @Leif Gruenwoldt

hágalo usted mismo ..

  • Paso 1. Crear un documento de texto vacío (nombre no importa) en su repositorio
  • Paso 2. Etapa y confirma el documento
  • Paso 3. Identifique el hash del blob ejecutando git ls-tree HEAD
  • Paso 4. Encuentre el hash del blob para ser e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
  • Paso 5. Snap fuera de su sorpresa y leer a continuación

¿Cómo calcular GIT sus cometer hashes

Commit Hash (SHA1) = SHA1("blob " + <size_of_file> + "\0" + <contents_of_file>) 

El texto blob⎵ es un prefijo constante y \0 también es constante y es el NULL personaje. El <size_of_file> y <contents_of_file> varían según el archivo.

¡Y eso es todo!

¡Pero espera!, ¿ha notado que el <filename> no es un parámetro utilizado para el cálculo hash? Dos archivos podrían tener el mismo hash si sus contenidos son indiferentes a la fecha y hora en que fueron creados y su nombre. Esta es una de las razones por las que Git maneja movimientos y cambia el nombre mejor que otros sistemas de control de versiones.

hágalo usted mismo (Ext)

  • Paso 6. Crear otro archivo vacío con una diferente filename en el mismo directorio
  • Paso 7. Comparar los hashes de los dos archivos.

Nota:

El enlace no menciona cómo se hash del objeto tree. No estoy seguro del algoritmo y parámetros Sin embargo, desde mi observación es probable que calcula un hash basado en todos los blobs y trees (sus valores hash probablemente) que contiene

+0

' SHA1 ("blob" + '- ¿hay espacio adicional entre blob y tamaño? ¿Es el tamaño decimal? ¿Tiene el prefijo cero? – osgx

+1

@osgx Sí. La referencia y mi prueba lo confirman. He corregido la respuesta. El tamaño parece ser el número de bytes como un entero sin prefijo. –

10

git hash-object es una forma rápida de verificar su método de prueba:

s='abc' 
printf "$s" | git hash-object --stdin 
printf "blob $(printf "$s" | wc -c)\0$s" | sha1sum 

salida:

f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f 
f2ba8f84ab5c1bce84a7b441cb1959cfc7093b7f - 

donde sha1sum está en Coreutils GNU.

+0

Como se mencionó en un anterior respuesta, la longitud debería calcularse como '$ (printf" \ 0 $ s "| wc -c)'. Observe el carácter vacío agregado. Es decir, si la cadena es 'abc' con el carácter vacío adicional delante del length daría 4, no 3. Entonces los resultados con sha1sum coinciden con git hash-object. –

+0

@mike pero ¿por qué mi método funcionó cuando lo probé? –

+0

Tienes razón, no coinciden. Parece que hay un efecto secundario pernicioso por usar printf en lugar de echo -e aquí. Cuando aplica git hash-object a un archivo que contiene la cadena 'abc' obtienes 8baef1b ... f903, que es lo que obtienes al usar echo -e en lugar de printf. Siempre que echo -e agregue una nueva línea al final de una cadena, parece que para hacer coincidir el comportamiento con printf puede hacer lo mismo (es decir, s = "$ s \ n"). –

2

Basado en Leif Gruenwoldt respuesta, aquí es un sustituto función Shell para git hash-object:

git-hash-object() { # substitute when the `git` command is not available 
    local type=blob 
    [ "$1" = "-t" ] && shift && type=$1 && shift 
    # depending on eol/autocrlf settings, you may want to substitute CRLFs by LFs 
    # by using `perl -pe 's/\r$//g'` instead of `cat` in the next 2 commands 
    local size=$(cat $1 | wc -c | sed 's/ .*$//') 
    (echo -en "$type $size\0"; cat "$1") | sha1sum | sed 's/ .*$//' 
} 

prueba:

$ echo 'Hello, World!' > test.txt 
$ git hash-object test.txt 
8ab686eafeb1f44702738c8b0f24f2567c36da6d 
$ git-hash-object test.txt 
8ab686eafeb1f44702738c8b0f24f2567c36da6d 
2

que necesitaba esto para algunas pruebas de unidad en Python 3 así que pensé' Déjalo aquí.

def git_blob_hash(data): 
    if isinstance(data, str): 
     data = data.encode() 
    data = b'blob ' + str(len(data)).encode() + b'\0' + data 
    h = hashlib.sha1() 
    h.update(data) 
    return h.hexdigest() 

me quedo con \n finales de línea en todas partes, pero en algunas circunstancias Git también podría ser changing your line endings antes de calcular este hash lo que puede tener un .replace('\r\n', '\n') allí también.