Skip to content

Modulo 18 — Git dietro le quinte

Cosa contiene davvero la cartella .git? I file più interessanti:

.git/
├── config ├── HEAD ├── index
├── refs/ └── objects/ (+ altro)

Il file di configurazione per-repo: abbiamo visto le impostazioni globali (--global, nome ed email), ma qui dentro stanno le impostazioni valide solo per questo repository.

Contiene la cartella heads/: un file per ogni branch, dal nome del branch stesso, che contiene l’hash del commit alla punta del branch. Es. refs/heads/master contiene l’hash dell’ultimo commit di master.

C’è anche refs/tags/: un file per ogni tag.

Un semplice file di testo che registra dove punta HEAD:

  • contiene refs/heads/master → HEAD punta al branch master;
  • in detached HEAD contiene invece direttamente un hash di commit.

Un file binario che elenca i file tracciati dal repository, con i loro metadati. Nota: non contiene il contenuto dei file, solo riferimenti.

Il vero magazzino: qui Git salva tutto — i backup dei file, i commit e altro. I file sono compressi e cifrati, quindi appaiono illeggibili.

Git si basa su funzioni di hashing: mappe che trasformano input di dimensione arbitraria in output a dimensione fissa.

Le funzioni hash crittografiche hanno queste proprietà:

  1. funzione one-way: non si può ricostruire l’input dall’output;
  2. una piccola modifica dell’input genera un output molto diverso;
  3. deterministiche: stesso input → stesso output;
  4. collisioni estremamente improbabili (due input diversi con lo stesso output).

SHA-1 è la funzione usata da Git (destinata a cambiare in futuro):

  • genera sempre numeri esadecimali di 40 cifre;
  • i “commit hash” che vediamo ovunque sono l’output di SHA-1.

Git è in sostanza uno store chiave-valore: gli passi qualunque contenuto e ti restituisce una chiave univoca (l’hash SHA-1) con cui ritirare quel contenuto in futuro.

Terminal window
echo 'hello' | git hash-object --stdin # calcola (e stampa) la chiave
echo 'hello' | git hash-object --stdin -w # -w: scrive davvero l'oggetto nel database
git cat-file -p <hash> # ritira il contenuto (-p = pretty print)
Terminal window
$ echo 'hello' | git hash-object --stdin -w
ce013625030ba8dba906f756967f9e9ca394464a
$ git cat-file -p ce013625030ba8dba906f756967f9e9ca394464a
hello

Contenuti diversi (es. hello vs goodbye) producono chiavi diverse: ogni versione di un file è un oggetto distinto.

TipoCosa contiene
blobIl contenuto di un file — nient’altro. Non contiene nemmeno il nome del file
treeIl contenuto di una directory: puntatori a blob e ad altri tree, con mode, tipo e filename
commitUn tree + il contesto: commit genitore/i, autore, committer, messaggio
annotated tagL’oggetto del tag annotato

Ogni entry di un tree contiene l’hash SHA-1 di un blob o di un altro tree, più mode, tipo e nome. Si ispeziona così:

Terminal window
git cat-file -p master^{tree} # il tree puntato dalla punta di master

La struttura è ricorsiva: un tree può contenere blob (file) e sotto-tree (sottodirectory), che a loro volta contengono blob e tree — fino ai contenuti finali.

Quando esegui git commit, Git crea un commit object:

  • tree = lo stato attuale dell’index (la fotografia della directory);
  • parent = il commit corrente puntato da HEAD;
  • autore, committer e messaggio.

La history è così una catena di commit, ognuno legato al genitore — da cui l’annotazione (HEAD -> master) nel log.

La struttura di un commit: tree, blob e genitori Il commit punta al suo tree; il tree aggrega i blob dei file; i commit si collegano tra loro tramite i genitori.

Commit consecutivi: ogni nuovo snapshot eredita e aggiorna i riferimenti A ogni commit, Git salva un nuovo tree; i blob invariati vengono riuscita (stesso hash), quindi ogni snapshot costa solo ciò che è cambiato.

Il quarto tipo di oggetto: l’oggetto generato dai tag annotati, con metadati e messaggio.

  • Ogni versione di ogni file → blob (identificato dal suo hash SHA-1).
  • Ogni directory → tree che aggrega blob e sotto-tree.
  • Ogni snapshot → commit che punta a un tree e al commit genitore.
  • Branch e tag → semplici file in refs/ che contengono hash.
  • HEAD → un file che dice a quale ref si sta lavorando.

Immagini: documentazione ufficiale di Git (git-scm.com, dal libro Pro Git di Scott Chacon), licenza CC BY-NC-SA 3.0.