Referințe
Referințele sunt pointeri numiți către commituri. Ramurile, tagurile, HEAD și referințele de urmărire la distanță sunt toate referințe. Pitmaster citește referințe din fișiere loose, packed-refs și reftable și scrie în fișiere loose și packed-refs.
RefDatabase
RefDatabase este stratul compozit care caută mai întâi referințe loose, apoi reftable (dacă depozitul are unul), apoi packed-refs.
use Pitmaster\Ref\RefDatabase;
$refs = new RefDatabase($gitDir, $commonDir);
// Resolve a ref to an ObjectId
$id = $refs->resolve('refs/heads/main');
// Resolve HEAD (follows symbolic refs)
$headId = $refs->resolveHead();
// Read HEAD as a symbolic ref (null when HEAD is detached)
$symref = $refs->readHead();
echo $symref?->target; // 'refs/heads/main'
// List all refs
foreach ($refs->list() as $name => $id) {
echo "{$id->hex} {$name}\n";
}
// Update a ref
$refs->update('refs/heads/main', $newCommitId);
// Delete a ref
$refs->delete('refs/heads/old-branch');
Pentru worktree-uri asociate, RefDatabase păstrează HEAD și celelalte pseudo-referințe specifice worktree-ului (ORIG_HEAD, MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD, REBASE_HEAD, BISECT_HEAD) în directorul git specific worktree-ului. Ramurile, tagurile și celelalte intrări refs/ sunt scrise în directorul comun, iar packed-refs este citit întotdeauna din directorul comun.
Referințe loose
Referințele loose sunt fișiere individuale în .git/refs/. Fiecare fișier conține hashul hexazecimal al obiectului țintă (40 de caractere sau 64 pentru SHA-256), urmat de un caracter de linie nouă.
.git/refs/heads/main -> abc123def456...\n
.git/refs/tags/v1.0.0 -> 789012abc345...\n
.git/refs/remotes/origin/main -> abc123def456...\n
use Pitmaster\Ref\LooseRefStore;
$store = new LooseRefStore($gitDir, $commonDir); // $commonDir defaults to $gitDir
$id = $store->resolve('refs/heads/main');
$store->update('refs/heads/main', $commitId);
$store->delete('refs/heads/old');
HEAD
HEAD este o referință specială care poate fi:
Simbolică: indică spre o altă referință (cel mai frecvent când vă aflați pe o ramură):
ref: refs/heads/main\n
Detașată: indică direct spre hashul unui commit:
abc123def456789012abc345def678901234abcd\n
use Pitmaster\Ref\SymbolicRef;
$symref = SymbolicRef::parse('HEAD', $headContent);
if ($symref !== null) {
echo "On branch: {$symref->target}\n";
// 'refs/heads/main'
} else {
echo "Detached HEAD\n";
}
Referințe împachetate
Fișierul packed-refs combină multe referințe într-un singur fișier, pentru eficiență. Git îl creează în timpul git pack-refs sau git gc.
# pack-refs with: peeled fully-peeled sorted
abc123def456789012abc345def678901234abcd refs/heads/main
789012abc345def678901234abcdabc123def456 refs/tags/v1.0.0
^def456789012abc345def678901234abcdabc12
Liniile care încep cu ^ sunt valori peeled: commitul spre care indică în cele din urmă un tag adnotat.
use Pitmaster\Ref\PackedRefStore;
$store = new PackedRefStore($commonDir);
$id = $store->resolve('refs/heads/main');
foreach ($store->list() as $name => $id) {
echo "{$id->hex} {$name}\n";
}
Valori peeled
Pentru tagurile adnotate, fișierul packed-refs poate include valoarea dereferențiată pe linia de după referința tagului. Valoarea peeled este commitul spre care indică în cele din urmă obiectul tag (eventual prin mai multe obiecte tag).
$peeled = $store->peeled('refs/tags/v1.0.0');
// Returns the commit ObjectId, or null if not peeled
Ordinea rezolvării referințelor
La rezolvarea unei referințe, Pitmaster verifică în această ordine:
- Referință loose (fișier în
.git/refs/) - Reftable, când există
.git/reftable/tables.list - Referință împachetată (intrare în
.git/packed-refs)
Referințele loose au prioritate. Când o referință împachetată este actualizată, noua valoare este scrisă ca referință loose (fișierul packed-refs nu este rescris). Ștergerea unei referințe elimină atât fișierul loose, cât și intrarea împachetată. $repo->packRefs() rescrie packed-refs din referințele curente și elimină fișierele loose.
SymbolicRef
Referințele simbolice sunt referințe care indică spre alte referințe, nu direct spre obiecte.
use Pitmaster\Ref\SymbolicRef;
// Parse
$ref = SymbolicRef::parse('HEAD', "ref: refs/heads/main\n");
echo $ref->name; // 'HEAD'
echo $ref->target; // 'refs/heads/main'
// Update
$refs->updateSymbolic('HEAD', 'refs/heads/feature');
Reflog
Reflogul înregistrează când se schimbă referințele, permițându-i lui git reflog să afișeze istoricul HEAD și al vârfurilor ramurilor.
Fișierele Reflog se află în .git/logs/:
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/stash
Fiecare linie înregistrează:
<old-hash> <new-hash> <author> <timestamp> <tz>\t<message>
use Pitmaster\Ref\Reflog;
$reflog = Reflog::open($gitDir, 'HEAD');
// Read entries
$entries = $reflog->entries();
foreach ($entries as $entry) {
echo "{$entry['old']} -> {$entry['new']}: {$entry['message']}\n";
}
// Append an entry
$reflog->append(
$oldId,
$newId,
'Name <email> 1700000000 +0000',
'commit: Add feature',
);
Stash-ul folosește reflogul lui refs/stash ca stocare de tip stivă.
Reftable
Reftable este un format binar mai nou pentru stocarea referințelor, conceput pentru performanțe mai bune în cazul multor referințe (mii de ramuri). Înlocuiește atât referințele loose, cât și packed-refs.
Fișierele Reftable sunt stocate în .git/reftable/ și conțin:
- Înregistrări de referințe (sortate după nume)
- Înregistrări de jurnal (intrări reflog)
- Indexare la nivel de bloc pentru căutări rapide
Pitmaster citește înregistrările de referințe din fișierele reftable când acestea sunt prezente; înregistrările de jurnal nu sunt citite. Când există un reftable, acesta înlocuiește packed-refs pentru listare. Actualizările referințelor sunt întotdeauna scrise în fișiere loose, pe care git nu le citește într-un depozit reftable; prin urmare, tratați depozitele reftable ca fiind doar pentru citire.
Convenții de denumire a referințelor
| Model | Semnificație |
|---|---|
refs/heads/<name> |
Ramură locală |
refs/tags/<name> |
Tag (ușor sau adnotat) |
refs/remotes/<remote>/<name> |
Ramură de urmărire la distanță |
refs/stash |
Stivă stash |
refs/notes/<namespace> |
Note Git |
HEAD |
Checkout curent (simbolic sau detașat) |
FETCH_HEAD |
Rezultatul ultimei preluări (scris de git; Pitmaster fetch() nu îl scrie) |
MERGE_HEAD |
Ramura care se îmbină (în timpul îmbinării) |
ORIG_HEAD |
HEAD anterior (înainte de operațiuni riscante) |
Lucrul cu referințele prin Repository
// Read
$branch = $repo->branch(); // Current branch name
$branches = $repo->branches(); // All branch names
$tags = $repo->tags(); // All tag names
$refs = $repo->allRefs(); // All refs as name => hash
// Write
$repo->createBranch('feature'); // Create refs/heads/feature
$repo->deleteBranch('feature'); // Delete refs/heads/feature
$repo->createTag('v1.0', 'Release');// Create annotated tag
$repo->createLightweightTag('v1.0-lite'); // Create lightweight tag
$repo->updateRef('refs/heads/main', $id); // Direct ref update
// Resolve
$id = $repo->resolve('HEAD');
$id = $repo->resolve('main');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('HEAD~3');