Ogni operazione Git sposta dati tra tre aree: la working tree (i tuoi file), l'index (bozza del prossimo commit) e il repository (storia committata sotto .git). Nominare cosa si sposta dove è il modello mentale più utile in assoluto.
Un branch di tracking remoto è un ref locale che rispecchia un branch su un remote. Vivono sotto refs/remotes/<remote>/ e vengono aggiornati da git fetch (e operazioni che implicano fetch, come git pull e git clone). Non si committa direttamente su di essi.
Normalmente HEAD punta a un ref di branch, che a sua volta punta a un commit. In detached HEAD, HEAD punta direttamente a un commit. Detached HEAD non è rotto; è una modalità deliberata utile per ispezionare la storia e pericolosa per committare senza pensare.
Il reflog registra ogni cambiamento di HEAD e di ogni punta di branch sulla tua macchina locale. È locale, per-clone, e non viene pushato. Quasi ogni commit "perso" può essere recuperato attraverso di esso.
Git ha due esiti di merge fondamentalmente diversi: fast-forward, dove la punta di un branch viene semplicemente spostata, e a tre vie, dove viene creato un nuovo commit con due parent. La scelta dipende dal fatto che i branch siano divergenti.
Fast-forward
Se la punta di main è un antenato di feature, nessun vero merge è necessario. Git sposta semplicemente main avanti:
In Git, un branch è solo un riferimento spostabile a un commit. Creare un branch è creare un file di 41 byte. Rispetto ai sistemi centralizzati dove i branch sono copie complete di directory, il modello di Git rende il branching abbastanza economico da usare con disinvoltura.
Cosa è realmente un branch
cat .git/refs/heads/main
# a1b2c3d4e5f6...
git rev-parse main
Quell'hash è la punta del branch. Il "branch" stesso è la catena di commit raggiungibili dalla punta tramite i puntatori parent.
L'index è un file binario in .git/index che rispecchia un oggetto tree: una lista ordinata di path con mode, hash e info di stat. È la staging area per il prossimo commit e una cache che permette a Git di saltare il re-hashing di file invariati.
Gli oggetti sono immutabili e nominati per hash. I riferimenti sono i nomi mutabili leggibili dall'uomo che puntano ad essi. Ogni branch, tag, branch di tracking remoto e HEAD è un ref.
Dove vivono i ref
I ref sono file (o entry nel file packed-refs) sotto .git/refs/:
Git nomina ogni oggetto con l'hash crittografico del suo contenuto. Questo è storage indirizzato per contenuto: il nome è l'impronta digitale del contenuto. L'hash predefinito è SHA-1 (160 bit, 40 caratteri esadecimali). Il supporto per SHA-256 esiste sperimentalmente da Git 2.29.
Come viene calcolato un hash
Git antepone un header della forma <type> <size>\0 al contenuto grezzo, poi lo hasha:
Il repository di Git è, nel cuore, un object store indirizzato per contenuto. Ci sono esattamente quattro tipi di oggetti: blob, tree, commit e tag. Ogni operazione alla fine tocca uno di essi. Capire questi quattro tipi demistifica la maggior parte di Git.
Blob
Un blob memorizza il contenuto di un file, niente di più. Nessun nome di file, nessun permesso, nessuna storia. Due file con contenuto identico condividono un solo blob, indipendentemente da dove vivono nell'albero.