Full Stack

Esplorare il database di Git con cat-file

Autore

Manuel Ricci

Da che abbiamo inizializzato per la prima volta la .git directory, di acqua sotto i ponti ne è passata e ora possiamo completare la nostra esplorazione.

Prima di aprire la directory di git e vedere tante lettere e numeri incomprensibili, facciamo il giro largo ed esploriamo il database con i comandi opportuni.

Partiamo con il recuperare l’ultima commit, cosa che possiamo fare con git log in questo modo:

1$ git log --pretty=format:'%h %d [%ad] %s - %an' --graph --date=short --max-count=1

L'output sarà simile a:

1$ * 8230e0b  (HEAD -> master) [2023-06-12] Aggiunto gitignore e creata cartella - Manuel Ricci

Il vostro differirà dato che sicuramente non avrete il mio hash, forse la branch ha un nome diverso, potreste aver fatto la commit in un altro momento, il messaggio potrebbe essere diverso e non siete me (a meno che non siate il calciatore della Juve Stabia, mio omonimo. Ciao se mi stai leggendo).

Il nostro unico interesse in tutto ciò comunque è l’ID della commit, il primo valore dopo l’asterisco.

L’hash possiamo usarlo per eseguire i prossimi due comandi:

1$ git cat-file -t <ID>

e

1$ git cat-file -p <ID>

Il primo ci restituirà il tipo di oggetto che ha l’ID che gli passeremo. In questo caso commit.

Il secondo comando invece ci mostra il contenuto dell’oggetto. Nel mio caso:

1tree 51cf5835e99b0302c840c659b5d82c503d4588df
2parent 4a0bc0708137164160934b8c33fd2901d52f53ee
3author Manuel Ricci <manuel@webtea.it> 1686562386 +0200
4committer Manuel Ricci <manuel@webtea.it> 1686562386 +0200
5
6Aggiunto gitignore e creata cartella

Cos’è tutta sta roba? Partendo dal basso:

  • Il messaggio di commit
  • Chi ha fatto la commit
  • L’autore della modifica
  • parent può fare riferimento a due cose:
    • il commit precedente a quello appena inserito (se rifate git log vedrete che è lo stesso della commit precedente)
    • nel caso di merge senza fast-forward ci sono due parents
  • tree fa riferimento ad un oggetto interno di Git usato per contenere lo snapshot del commit.

Arrivati a questo punto però ci sono un po’ di chiarimenti da fare, partendo proprio da tree.

Cos’è un tree in Git?

La parola tree in informatica è sovrautilizzata e Git non fa eccezione. Per cui quando si parla di tree in Git, si fa riferimento a più cose, tre per la precisione:

  • working tree (o work-tree) fa riferimento alla nostra working directory.
  • tree object fa riferimento alla struttura dati usata da Git per memorizzare cartelle e sottocartelle (sub-tree)
  • internal tree object usato da Git per contenere lo snapshot del commit.

Tenete sempre a mente queste tre differenze e la vostra vita sarà molto più semplice.

Cosa fare il comando cat-file?

Abbiamo usato un comando senza farci una lezione apposita, ma tranquilli che non vi lascio a secco.

Il comando cat-file fornisce informazioni sul contenuto, sul tipo e sulle dimensioni degli oggetti presenti in repository.

Ricordate che gli oggetti possibili sono tre: blob, tree (ora sapete quale) e commit.

I flag a disposizione sono:

  • -t (per vedere il tipo di oggetto)
  • -s (dimensioni dell’oggetto in bytes)
  • -e (nessun output, a meno che l’oggetto non sia malformato)
  • -p (mostra il contenuto dell’oggetto)

Per maggiori dettagli circa cat-file vi incito a prendere visione, come sempre, della documentazione, che ne sa sicuramente più di me.

Tornando alla nostra esplorazione

Con l’ultimo comando eseguito abbiamo trovato l’hash del tree (oggetto interno di Git):

1tree 51cf5835e99b0302c840c659b5d82c503d4588df

Questo hash possiamo usarlo per vedere il contenuto della commit:

1100644 blob 4c59c3eb9cebc8ba2be18de311a98760d7d60ff5    .gitignore
2040000 tree 5655e76406ad6724109e5fc21347824d95a51064    cartella
3100644 blob 76866113600672556f2bb35f1dbbfa5bf44f68bc    ciao.txt
4100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    ipsum.txt
5100644 blob c1ca2821286a1a0d4a1fbdc335770130bdb59590    lorem.txt

Come sempre differirà da ciò che vedete voi.

Come mai ci sono tutti i file e non solo quelli oggetto della commit? Ricordate sempre che per questioni di performance, Git fa uno snapshot di tutti i file e non solo quelli modificati così che sia più veloce nelle sue operazioni senza andare a ricostruire i file in base alle differenze accumulate tra un commit e l’altro.

Nell’output vediamo anche il terzo tree menzionato qualche paragrafo fa dove indica la directory chiamata “cartella”.

Se volessi vedere il contenuto della cartella?

1$ git cat-file -p 5655e76

Il cui output sarà:

1100644 blob 887ae9333d92a1d72400c210546e28baa1050e44    hello.txt

E se volessi vedere il contenuto del file?

1$ git cat-file -p 887ae93

Semmai vi stesse domandando cosa sia quel 100644 sappiate che si tratta dei permessi. 100644 indica un file, 040000 le directory mentre 120000 fa riferimento ad un link simbolico.

E la .git directory?

Come detto all'inizio, da che abbiamo inizializzato per la prima volta la .git directory, di acqua sotto i ponti ne è passata.

Per prima cosa andiamo a vedere nella .git directory la cartella logs. Al suo interno troveremo il file HEAD e la directory refs. Quest’ultima ci farà vedere i log per branch, mentre HEAD se lo spiamo con il comando cat ci mostrerà l’output che vediamo con git log, chiaramente senza possibilità di formattazione.

10000000000000000000000000000000000000000 937db31b62418ff5018b7bf79ba2c84a70bf3c7a Manuel Ricci <manuel@webtea.it> 1686135546 +0200     commit (initial): Prima commit
2937db31b62418ff5018b7bf79ba2c84a70bf3c7a 14b02a5a742ef49d9b82e0305c16f16c555c4e61 Manuel Ricci <manuel@webtea.it> 1686135598 +0200     commit: Seconda commit
314b02a5a742ef49d9b82e0305c16f16c555c4e61 fa09924e9052d4c2778716a5c2e050e4dd2128a1 Manuel Ricci <manuel@webtea.it> 1686135693 +0200     commit: Terza commit
4fa09924e9052d4c2778716a5c2e050e4dd2128a1 99bb4c6ac28d74553774e6f4e237daa6b6e2d823 Manuel Ricci <manuel@webtea.it> 1686136120 +0200     commit: Aggiunto lo spagnolo
599bb4c6ac28d74553774e6f4e237daa6b6e2d823 72b536b9f427784949a2a08d092f7531afd3b0c1 Manuel Ricci <manuel@webtea.it> 1686136282 +0200     commit: Aggiunta reference a lorem.txt
672b536b9f427784949a2a08d092f7531afd3b0c1 bdc5f468cc1b06f45582d2ad992e99a61af1703e Manuel Ricci <manuel@webtea.it> 1686136316 +0200     commit (amend): Aggiunta reference a lorem.txt
7bdc5f468cc1b06f45582d2ad992e99a61af1703e fbcfe0b85eae76fe08136cb7c6dee382c4057846 Manuel Ricci <manuel@webtea.it> 1686136999 +0200     commit: Questo è il mio oggetto e deve arrivare più o meno fino a qui
8fbcfe0b85eae76fe08136cb7c6dee382c4057846 4a0bc0708137164160934b8c33fd2901d52f53ee Manuel Ricci <manuel@webtea.it> 1686562017 +0200     commit: Modificato contenuto e creato nuovo file
94a0bc0708137164160934b8c33fd2901d52f53ee 8230e0bbaacf838d23a43620f8b4fd88428e3bda Manuel Ricci <manuel@webtea.it> 1686562386 +0200     commit: Aggiunto gitignore e creata cartella

Il log presenta:

  • hash della commit genitore (aka quella precedente), quella iniziale vedete che ha 40 zeri
  • hash della commit
  • autore
  • email dell’autore
  • data in formato Unix epoch (secondi dal 1° gennaio 1970) con timezone (+0200 = CEST)
  • oggetto
  • messaggio

Tornando nella git directory (cd .. per risalire alla directory precedente) ed entrando nella directory objects e facendo un ls -l vedremo una lista di coppie di caratteri:

1drwxr-xr-x  2 manuel manuel 4096 Jun  7 13:11 05
2drwxr-xr-x  2 manuel manuel 4096 Jun  7 12:59 14
3drwxr-xr-x  2 manuel manuel 4096 Jun  7 12:58 21
4drwxr-xr-x  2 manuel manuel 4096 Jun  7 13:01 33
5drwxr-xr-x  2 manuel manuel 4096 Jun  7 13:11 43
6drwxr-xr-x  2 manuel manuel 4096 Jun 12 11:26 4a
7drwxr-xr-x  2 manuel manuel 4096 Jun 12 11:32 4c
8drwxr-xr-x  2 manuel manuel 4096 Jun 12 11:33 51
9drwxr-xr-x  2 manuel manuel 4096 Jun 12 11:33 56
10drwxr-xr-x  2 manuel manuel 4096 Jun 12 11:26 58
11drwxr-xr-x  2 manuel manuel 4096 Jun  7 19:37 72
12drwxr-xr-x  2 manuel manuel 4096 Jun  7 13:11 76
13drwxr-xr-x  2 manuel manuel 4096 Jun 12 11:33 82
14drwxr-xr-x  2 manuel manuel 4096 Jun  7 13:09 88

Output parziale della mia.

Sono tutte directory (lo si può capire dalla d davanti ai permessi a sinistra). Quei caratteri non sono casuali (cioè sì, ma hanno un significato). La mia ultima commit ha come hash 8230e0bbaacf838d23a43620f8b4fd88428e3bda le prime due cifre corrispondono proprio a quella directory. Se entro troverò un file 30e0bbaacf838d23a43620f8b4fd88428e3bda (la parte dopo 82) e se provo a fare un bel cat del file?

1x��MJ1
2        �a�=E���M�iAD��if,̏�
3                            ^_����]<��u� ��ɦ*���3��Ґ0H�(�K㞃0R��s_�O�zĊMp��i�)���b�B��P�w+����}_>�y�_Cd����_m��e���@9�3D'�A�P��m�>�m���k*(��j�N�}��oM�%

I file sono cifrati e non è possibile leggerli direttamente e come si fa quindi? Se sei arrivato fin qui senza saltare i pezzi dovresti saperlo :)

Conclusioni

Ora che abbiamo visto com’è cambiata la git directory e sappiamo come navigare, possiamo andare avanti. Nella prossima lezione vedremo come semplificarci la vita con gli alias.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.