Introduzione
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/refs/heads/<name>: branch locali..git/refs/tags/<name>: tag..git/refs/remotes/<remote>/<name>: branch di tracking remoto..git/HEAD: il ref corrente.
Ogni ref non simbolico è un file di una riga contenente un hash:
cat .git/refs/heads/main
# a1b2c3d4...
Elencare i ref
git for-each-ref
git for-each-ref refs/tags/
git for-each-ref --format='%(refname:short) %(objectname:short) %(subject)'
git show-ref
Ref simbolici
Un ref simbolico punta a un altro ref invece che a un hash. HEAD è l'esempio canonico:
cat .git/HEAD
# ref: refs/heads/main
git symbolic-ref HEAD
git symbolic-ref refs/remotes/origin/HEAD
Leggere i ref in sicurezza
Passa sempre per il plumbing piuttosto che leggere i file:
git rev-parse HEAD
git rev-parse main
git rev-parse origin/main
git rev-parse v1.0.0^{commit}
Il plumbing gestisce ref impacchettati, ref simbolici e lookup di ref in modo uniforme.
Aggiornare i ref
git update-ref refs/heads/feature/login <sha>
git update-ref -d refs/heads/feature/login # cancella
git update-ref refs/heads/feature/login <new> <old> # CAS
La forma a tre argomenti è compare-and-set; rifiuta se il valore corrente differisce.
Ref impacchettati
Per performance, Git può impacchettare molti ref in un singolo file .git/packed-refs. git pack-refs --all fa questo; le scritture future vanno comunque a file loose fino al prossimo pack.
Sintassi dei refspec
I remote usano i refspec per mappare tra ref locali e remoti:
+refs/heads/*:refs/remotes/origin/*
Il + iniziale significa "force update". Questa singola riga è ciò che fa popolare a git fetch refs/remotes/origin/.
Il backend reftable
Git moderno (2.45+) include un formato di storage ref alternativo chiamato reftable, sviluppato originariamente per JGit. Reftable memorizza milioni di ref efficientemente in un formato binario log-strutturato e supporta transazioni atomiche su molti ref contemporaneamente. Abilita su un nuovo repo:
git init --ref-format=reftable
git rev-parse --show-ref-format
Per la maggior parte degli utenti il formato classico loose+packed va ancora bene. Reftable brilla sui server e sui monorepo enormi con centinaia di migliaia di ref (es., un ref per pull request).
Gerarchie di ref personalizzate
I ref non devono vivere sotto i quattro namespace standard. Gli strumenti che hanno bisogno dei propri nomi usano gerarchie personalizzate sotto refs/:
refs/notes/*:git notes.refs/stash:git stash.refs/replace/*:git replace.refs/bisect/*: stato digit bisect.refs/pull/*/headerefs/merge-requests/*/head: provider di hosting.
git for-each-ref refs/notes/
git for-each-ref refs/replace/
Puoi creare i tuoi finché il nome è un ref valido (niente doppi punti, niente spazi, nessun trattino iniziale).
Errori comuni
Modificare i file ref a mano e finire con un problema di newline finale o un file scritto a metà. Usa git update-ref. Confondere refs/heads/main, main e refs/main; git rev-parse --symbolic-full-name risolve qualsiasi nome corto nella sua forma canonica. Nominare un branch e un tag in modo identico; gli strumenti preferiscono il tag, portando a confusione sottile. Infine, cancellare .git/packed-refs per "fare pulizia"; cancella ref che non erano anche loose. Passa sempre attraverso Git.