¿Cuál es el formatting interno de un object de tree git?

¿Cuál es el formatting del contenido de un object de tree git?

El contenido de un object blob es blob [size of string] NUL [string] , pero ¿qué es para un object de tree?

El formatting de un object de tree:

 tree [content size]\0[Entries having references to other trees and blobs] 

El formatting de cada input que tiene references a otros treees y blobs:

 [mode] [file/folder name]\0[SHA-1 of referencing blob or tree] 

Escribí un guión desinflando los objects del tree. Emite de la siguiente manera:

 tree 192\0 40000 octopus-admin\0 a84943494657751ce187be401d6bf59ef7a2583c 40000 octopus-deployment\0 14f589a30cf4bd0ce2d7103aa7186abe0167427f 40000 octopus-product\0 ec559319a263bc7b476e5f01dd2578f255d734fd 100644 pom.xml\0 97e5b6b292d248869780d7b0c65834bfb645e32a 40000 src\0 6e63db37acba41266493ba8fb68c76f83f1bc9dd 

El número 1 como primer carácter de un modo muestra que es una reference a un blob / file. El ejemplo anterior, pom.xml es un blob y los otros son treees.

Tenga en count que agregué nuevas líneas y espacios después de \0 por el bien de la printing bonita. Normalmente, todo el contenido no tiene nuevas líneas. También convertí 20 bytes (es decir, el SHA-1 de reference de blobs y treees) en una cadena hexadecimal para visualizar mejor.

Intento elaborar un poco más sobre @lemiorhan respuesta, por medio de un informe de testing.

Crear un informe de testing

Crea un proyecto de testing en una carpeta vacía:

 $ echo ciao > file1 $ mkdir folder1 $ echo hello > folder1/file2 $ echo hola > folder1/file3 

Es decir:

 $ find -type f ./file1 ./folder1/file2 ./folder1/file3 

Crea el repository local de Git:

 $ git init $ git add . $ git write-tree 0b6e66b04bc1448ca594f143a91ec458667f420e 

El último command devuelve el hash del tree de nivel superior.

Lee el contenido de un tree

Para imprimir el contenido de un tree en formatting de lectura humana, use:

 $ git ls-tree 0b6e66 100644 blob 887ae9333d92a1d72400c210546e28baa1050e44 file1 040000 tree ab39965d17996be2116fe508faaf9269e903c85b folder1 

En este caso, 0b6e66 son los primeros seis caracteres del tree superior. Puedes hacer lo mismo con la folder1 .

Para get el mismo contenido pero en formatting sin formatting, use:

 $ git cat-file tree 0b6e66 100644 file1 ▒z▒3=▒▒▒$ ▒►Tn(▒▒♣D40000 folder1 ▒9▒]▒k▒◄o▒▒▒i▒♥▒[% 

El contenido es similar al almacenado físicamente como un file en formatting comprimido, pero falla la cadena inicial:

 tree [content size]\0 

Para get el contenido real, necesitamos descomprimir el file que almacena el object de tree c1f4bf . El file que queremos es – dado el formatting de ruta 2/38 -:

 .git/objects/0b/6e66b04bc1448ca594f143a91ec458667f420e 

Este file está comprimido con zlib, por lo tanto, obtenemos su contenido con:

 $ openssl zlib -d -in .git/objects/0b/6e66b04bc1448ca594f143a91ec458667f420e tree 67 100644 file1 ▒z▒3=▒▒▒$ ▒►Tn(▒▒♣D40000 folder1 ▒9▒]▒k▒◄o▒▒▒i▒♥▒[% 

Aprendemos que el tamaño del contenido del tree es 67.

Tenga en count que, dado que el terminal no está hecho para imprimir binarys, podría comerse una parte de la cadena o mostrar otro comportamiento extraño. En este caso, realice los commands anteriores con | od -c | od -c o use la solución manual en la siguiente sección.

Genere manualmente el contenido del object del tree

Para comprender el process de generación de treees podemos generarlo nosotros mismos a partir de su contenido legible por humanos, por ejemplo, para el tree superior:

 $ git ls-tree 0b6e66 100644 blob 887ae9333d92a1d72400c210546e28baa1050e44 file1 040000 tree ab39965d17996be2116fe508faaf9269e903c85b folder1 

Cada object hash ASCII SHA-1 se convierte y se almacena en formatting binary. Si lo que necesita es solo una versión binaria de los hashes ASCII, puede hacerlo con:

 $ echo -e "$(echo ASCIIHASH | sed -e 's/../\\x&/g')" 

Por lo tanto, el blob 887ae9333d92a1d72400c210546e28baa1050e44 se convierte en

 $ echo -e "$(echo 887ae9333d92a1d72400c210546e28baa1050e44 | sed -e 's/../\\x&/g')" ▒z▒3=▒▒▒$ ▒►Tn(▒▒♣D 

Si queremos crear todo el object de tree, aquí hay un awk one-liner:

 $ git ls-tree 0b6e66 | awk -b 'function bsha(asha)\ {patsplit(asha, x, /../); h=""; for(j in x) h=h sprintf("%c", strtonum("0x" x[j])); return(h)}\ {t=t sprintf("%d %s\0%s", $1, $4, bsha($3))} END {printf("tree %s\0%s", length(t), t)}' tree 67 100644 file1 ▒z▒3=▒▒▒$ ▒►Tn(▒▒♣D40000 folder1 ▒9▒]▒k▒◄o▒▒▒i▒♥▒[% 

La function bsha convierte los algorithms hash SHA-1 ASCII en binarys. El contenido del tree se coloca primero en la variable t y luego su longitud se calcula e imprime en la sección END{...} .

Como se observó anteriormente, la console no es muy adecuada para imprimir binarys, por lo que es posible que deseemos replacelos por su equivalente en formatting \x## :

 $ git ls-tree 0b6e66 | awk -b 'function bsha(asha)\ {patsplit(asha, x, /../); h=""; for(j in x) h=h sprintf("%s", "\\x" x[j]); return(h)}\ {t=t sprintf("%d %s\0%s", $1, $4, bsha($3))} END {printf("tree %s\0%s", length(t), t)}' tree 187 100644 file1 \x88\x7a\xe9\x33\x3d\x92\xa1\xd7\x24\x00\xc2\x10\x54\x6e\x28\xba\xa1\x05\x0e\x4440000 folder1 \xab\x39\x96\x5d\x17\x99\x6b\xe2\x11\x6f\xe5\x08\xfa\xaf\x92\x69\xe9\x03\xc8\x5b% 

El resultado debe ser un buen compromiso para comprender la estructura del contenido del tree. Compare el resultado anterior con la estructura de contenido general del tree

 tree [content size]\0[Object Entries] 

donde cada input de object es como:

 [mode] [Object name]\0[SHA-1 in binary format] 

Los modos son un subset de modos de sistema de files UNIX. Ver el manual Tree Objects on Git para más detalles.

Necesitamos asegurarnos de que los resultados sean consistentes. Con este fin, podríamos comparar la sum de comprobación del tree generado por awk con la sum de comprobación del tree almacenado de Git.

En cuanto a este último:

 $ openssl zlib -d -in .git/objects/0b/6e66b04bc1448ca594f143a91ec458667f420e | shasum 0b6e66b04bc1448ca594f143a91ec458667f420e *- 

En cuanto al tree hecho en casa:

 $ git ls-tree 0b6e66 | awk -b 'function bsha(asha)\ {patsplit(asha, x, /../); h=""; for(j in x) h=h sprintf("%c", strtonum("0x" x[j])); return(h)}\ {t=t sprintf("%d %s\0%s", $1, $4, bsha($3))} END {printf("tree %s\0%s", length(t), t)}' | shasum 0b6e66b04bc1448ca594f143a91ec458667f420e *- 

La sum de comprobación es la misma.

Calcula la sum de comprobación del object de tree

La manera más o less oficial de getlo es:

 $ git ls-tree 0b6e66 | git mktree 0b6e66b04bc1448ca594f143a91ec458667f420e 

Para calcularlo manualmente, necesitamos canalizar el contenido del tree generado por el script en el command shasum . En realidad, ya hemos hecho esto anteriormente (para comparar el contenido generado y almacenado). El resultado fue:

 0b6e66b04bc1448ca594f143a91ec458667f420e *- 

y es lo mismo que con git mktree .

Objetos embalados

Puede encontrar que, para su repository, no puede encontrar los files .git/objects/XX/XXX... almacenando los objects Git. Esto sucede porque algunos o todos los objects "sueltos" se han empaquetado en uno o más files .git\objects\pack\*.pack .

Para descomprimir el repository, primero mueva los files del package fuera de su position original, luego git-desempackage los objects.

 $ mkdir .git/pcache $ mv .git/objects/pack/*.pack .git/pcache/ $ git unpack-objects < .git/pcache/*.pack 

Para volver a embalar cuando haya terminado con los experimentos:

 $ git gc 

Expresado como un patrón tipo BNF, un tree git contiene datos de la forma

 (?<tree> tree (?&SP) (?&decimal) \0 (?&entry)+ ) (?<entry> (?&octal) (?&SP) (?&strnull) (?&sha1bytes) ) (?<strnull> [^\0]+ \0) (?<sha1bytes> (?s: .{20})) (?<decimal> [0-9]+) (?<octal> [0-7]+) (?<SP> \x20) 

Es decir, un tree git comienza con un encabezado de

  1. el tree cadena literal
  2. ESPACIO ( es decir, el byte 0x20 )
  3. Longitud decimal codificada en ASCII de los contenidos no comprimidos

Después de un terminador NUL ( es decir , el byte 0x00 ), el tree contiene una o más inputs del formulario

  1. Modo octal codificado en ASCII
  2. ESPACIO
  3. nombre
  4. NUL
  5. Hash SHA1 codificado como 20 bytes sin firmar

Git luego alimenta los datos del tree al desinflado de zlib para un almacenamiento compacto.

Restring que los blobs de git son anónimos. Los treees Git asocian nombres con hashes SHA1 de otro contenido que pueden ser blobs, otros treees, etc.

Para demostrarlo, considere el tree asociado con la label v2.7.2 de git, que es posible que desee explorar en GitHub .

 $ git rev-parse v2.7.2^{tree} 802b6758c0c27ae910f40e1b4862cb72a71eee9f 

El código siguiente requiere que el object tree esté en formatting "suelto". No sé de una manera de extraer un solo object sin formatting de un file de package, así que primero ejecuté git unpack-objects en los files del package desde mi clon a un nuevo repository. Tenga en count que esto expandió un directory .git que comenzó alnetworkingedor de 90 MB para resultar de aproximadamente 1,8 GB.

ACTUALIZACIÓN: Gracias a max630 por mostrar cómo desempaquetar un solo object .

 #! /usr/bin/env perl use strict; use warnings; use subs qw/ git_tree_contents_pattern read_raw_tree_object /; use Compress::Zlib; my $treeobj = read_raw_tree_object; my $git_tree_contents = git_tree_contents_pattern; die "$0: invalid tree" unless $treeobj =~ /^$git_tree_contents\z/; die "$0: unexpected header" unless $treeobj =~ s/^(tree [0-9]+)\0//; print $1, "\n"; # eg, 100644 SP .gitattributes \0 sha1-bytes while ($treeobj) { # /s is important so . matches any byte! if ($treeobj =~ s/^([0-7]+) (.+?)\0(.{20})//s) { my($mode,$name,$bytes) = (oct($1),$2,$3); printf "%06o %s %s\t%s\n", $mode, ($mode == 040000 ? "tree" : "blob"), unpack("H*", $bytes), $name; } else { die "$0: unexpected tree entry"; } } sub git_tree_contents_pattern { qr/ (?(DEFINE) (?<tree> tree (?&SP) (?&decimal) \0 (?&entry)+ ) (?<entry> (?&octal) (?&SP) (?&strnull) (?&sha1bytes) ) (?<strnull> [^\0]+ \0) (?<sha1bytes> (?s: .{20})) (?<decimal> [0-9]+) (?<octal> [0-7]+) (?<SP> \x20) ) (?&tree) /x; } sub read_raw_tree_object { # $ git rev-parse v2.7.2^{tree} # 802b6758c0c27ae910f40e1b4862cb72a71eee9f # # NOTE: extracted using git unpack-objects my $tree = ".git/objects/80/2b6758c0c27ae910f40e1b4862cb72a71eee9f"; open my $fh, "<", $tree or die "$0: open $tree: $!"; binmode $fh or die "$0: binmode: $!"; local $/; my $treeobj = uncompress <$fh>; die "$0: uncompress failed" unless defined $treeobj; $treeobj } 

Mira el git ls-tree nuestro pobre hombre en acción. La salida es idéntica, excepto que produce el marcador y la longitud del tree .

  $ diff -u <(cd ~ / src / git; git ls-tree 802b6758c0) <(../ rawtree)
 --- / dev / fd / 63 2016-03-09 14: 41: 37.011791393 -0600
 +++ / dev / fd / 62 2016-03-09 14: 41: 37.011791393 -0600
 @@ -1,3 +1,4 @@
 + tree 15530
  100644 blob 5e98806c6cc246acef5f539ae191710a0c06ad3f .gitattributes
  100644 blob 1c2f8321386f89ef8c03d11159c97a0f194c4423 .gitignore
  100644 blob e5b4126bec557db55924b7b60ed70349626ea2c4 .mailmap 

@lemiorhan respuesta es correcta, pero se pierde pequeños detalles importantes. El formatting del tree es:

 [mode] [file/folder name]\0[SHA-1 of referencing blob or tree] 

Pero lo importante es que [SHA-1 of referencing blob or tree] está en forma binaria, no en hexadecimal. Este es un fragment de Python para analizar el object de tree en inputs:

 entries = [ line[0:2]+(line[2].encode('hex'),) for line in re.findall('(\d+) (.*?)\0(.{20})', body, re.MULTILINE) ] 

Como se sugirió, Pro Git explica bien la estructura. Para mostrar un tree bastante impreso, use:

 git cat-file -p 4c975c5f5945564eae86d1e933192c4a9096bfe5 

para mostrar el mismo tree en su forma original, pero sin comprimir, use:

 git cat-file tree 4c975c5f5945564eae86d1e933192c4a9096bfe5 

La estructura es esencialmente la misma, con hashes almacenados como nombres de file binarys y terminados en nulo.