Ga naar de inhoud

Refs

Refs (verwijzingen) zijn benoemde pointers naar commits. Branches, tags, HEAD en remote tracking refs zijn allemaal refs. Pitmaster leest refs uit losse bestanden, packed-refs en reftable, en schrijft losse bestanden en packed-refs.

RefDatabase

De RefDatabase is de samengestelde laag die eerst losse refs doorzoekt, daarna reftable (als de repository er een heeft) en vervolgens 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');

Voor gekoppelde worktrees bewaart de RefDatabase HEAD en de andere pseudo-refs per worktree (ORIG_HEAD, MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD, REBASE_HEAD, BISECT_HEAD) in de git-map van de worktree. Branches, tags en andere refs/-items worden naar de gemeenschappelijke map geschreven, en packed-refs wordt altijd uit de gemeenschappelijke map gelezen.

Losse refs

Losse refs zijn afzonderlijke bestanden in .git/refs/. Elk bestand bevat de hex-hash van het doelobject (40 tekens, of 64 voor SHA-256), gevolgd door een nieuwe regel.

Code
.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 is een speciale ref die kan zijn:

Symbolisch: verwijst naar een andere ref (meestal wanneer je op een branch zit):

Code
ref: refs/heads/main\n

Losgekoppeld: verwijst rechtstreeks naar een commit-hash:

Code
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";
}

Gepackte refs

Het bestand packed-refs combineert veel refs in één bestand voor meer efficiëntie. Git maakt het aan tijdens git pack-refs of git gc.

Code
# pack-refs with: peeled fully-peeled sorted
abc123def456789012abc345def678901234abcd refs/heads/main
789012abc345def678901234abcdabc123def456 refs/tags/v1.0.0
^def456789012abc345def678901234abcdabc12

Regels die beginnen met ^ zijn uitgepelde waarden: de commit waar een geannoteerde tag uiteindelijk naar verwijst.

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";
}

Uitgepelde waarden

Voor geannoteerde tags kan het packed-refs-bestand de uitgepelde (gedereferentieerde) waarde bevatten op de regel na de tag-ref. De uitgepelde waarde is de commit waar het tagobject uiteindelijk naar verwijst (mogelijk via meerdere tagobjecten).

PHP
$peeled = $store->peeled('refs/tags/v1.0.0');
// Returns the commit ObjectId, or null if not peeled

Ref-resolutievolgorde

Bij het oplossen van een ref controleert Pitmaster dit in deze volgorde:

  1. Losse ref (bestand in .git/refs/)
  2. Reftable, als .git/reftable/tables.list bestaat
  3. Gepackte ref (item in .git/packed-refs)

Losse refs hebben voorrang. Wanneer een gepackte ref wordt bijgewerkt, wordt de nieuwe waarde als losse ref geschreven (het packed-refs-bestand wordt niet herschreven). Als je een ref verwijdert, worden zowel het losse bestand als het gepackte item verwijderd. $repo->packRefs() herschrijft packed-refs op basis van de huidige refs en verwijdert de losse bestanden.

SymbolicRef

Symbolische refs zijn verwijzingen naar andere verwijzingen, in plaats van rechtstreeks naar objecten.

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

De reflog registreert wanneer refs veranderen, zodat git reflog de geschiedenis van HEAD en branch-tips kan tonen.

Reflog-bestanden staan in .git/logs/:

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

Elke regel legt vast:

Code
<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',
);

De stash gebruikt de reflog van refs/stash als opslag voor de stapel.

Reftable

Reftable is een nieuwer binair formaat voor het opslaan van refs, ontworpen voor betere prestaties bij veel refs (duizenden branches). Het vervangt zowel losse refs als packed-refs.

Reftable-bestanden worden opgeslagen in .git/reftable/ en bevatten:

  • Ref-records (gesorteerd op naam)
  • Logrecords (reflog-items)
  • Indexering op blokniveau voor snelle zoekopdrachten

Pitmaster leest de ref-records van reftable-bestanden als die aanwezig zijn; logrecords worden niet gelezen. Wanneer er een reftable bestaat, vervangt die packed-refs voor het weergeven van refs. Ref-updates worden altijd als losse bestanden geschreven, die git in een reftable-repository niet leest. Behandel reftable-repositories daarom als alleen-lezen.

Naamgevingsconventies voor refs

Patroon Betekenis
refs/heads/<name> Lokale branch
refs/tags/<name> Tag (lichtgewicht of geannoteerd)
refs/remotes/<remote>/<name> Remote tracking branch
refs/stash Stash-stapel
refs/notes/<namespace> Notities van Git
HEAD Huidige checkout (symbolisch of losgekoppeld)
FETCH_HEAD Resultaat van de laatste fetch (geschreven door git; Pitmaster's fetch() schrijft dit niet)
MERGE_HEAD Branch die wordt samengevoegd (tijdens een merge)
ORIG_HEAD Vorige HEAD (vóór risicovolle bewerkingen)

Werken met refs via 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');