Treci la conținut

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.

PHP
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ă.

Cod
.git/refs/heads/main           -> abc123def456...\n
.git/refs/tags/v1.0.0          -> 789012abc345...\n
.git/refs/remotes/origin/main  -> abc123def456...\n
PHP
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 este o referință specială care poate fi:

Simbolică: indică spre o altă referință (cel mai frecvent când vă aflați pe o ramură):

Cod
ref: refs/heads/main\n

Detașată: indică direct spre hashul unui commit:

Cod
abc123def456789012abc345def678901234abcd\n
PHP
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.

Cod
# 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.

PHP
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).

PHP
$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:

  1. Referință loose (fișier în .git/refs/)
  2. Reftable, când există .git/reftable/tables.list
  3. 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.

PHP
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/:

Cod
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/stash

Fiecare linie înregistrează:

Cod
<old-hash> <new-hash> <author> <timestamp> <tz>\t<message>
PHP
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

PHP
// 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');