Lewati ke konten

Refs

Refs (referensi) adalah penunjuk bernama ke commit. Branch, tag, HEAD, dan refs pelacakan remote semuanya adalah refs. Pitmaster membaca refs dari file lepas, packed-refs, dan Reftable, serta menulis file lepas dan packed-refs.

RefDatabase

RefDatabase adalah lapisan gabungan yang mencari refs lepas terlebih dahulu, lalu Reftable (jika repositori memilikinya), kemudian 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');

Untuk worktree tertaut, RefDatabase menyimpan HEAD dan pseudo-ref lain per-worktree (ORIG_HEAD, MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD, REBASE_HEAD, BISECT_HEAD) di direktori git per-worktree. Branch, tag, dan entri refs/ lainnya ditulis ke direktori bersama, dan packed-refs selalu dibaca dari direktori bersama.

Refs lepas

Refs lepas adalah file individual di .git/refs/. Setiap file berisi hash heksadesimal objek target (40 karakter, atau 64 untuk SHA-256), diikuti baris baru.

Kode
.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 adalah ref khusus yang dapat berupa:

Simbolis: menunjuk ke ref lain (paling umum, saat berada di branch):

Kode
ref: refs/heads/main\n

Terlepas: menunjuk langsung ke hash commit:

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

Refs terkemas

File packed-refs menggabungkan banyak refs ke dalam satu file demi efisiensi. Git membuatnya selama git pack-refs atau git gc.

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

Baris yang dimulai dengan ^ adalah nilai peeled: commit yang pada akhirnya dituju oleh tag beranotasi.

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

Nilai peeled

Untuk tag beranotasi, file packed-refs dapat menyertakan nilai peeled (yang telah didereferensikan) pada baris setelah ref tag. Nilai peeled adalah commit yang pada akhirnya dituju oleh objek tag (melalui beberapa objek tag jika ada).

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

Urutan resolusi ref

Saat me-resolve ref, Pitmaster memeriksa urutan berikut:

  1. Ref lepas (file di .git/refs/)
  2. Reftable, jika .git/reftable/tables.list ada
  3. Ref terkemas (entri di .git/packed-refs)

Ref lepas diprioritaskan. Saat ref terkemas diperbarui, nilai baru ditulis sebagai ref lepas (file packed-refs tidak ditulis ulang). Menghapus ref menghapus file lepas dan entri terkemas. $repo->packRefs() menulis ulang packed-refs berdasarkan refs saat ini dan menghapus file lepas.

SymbolicRef

Ref simbolis adalah referensi yang menunjuk ke referensi lain, bukan langsung ke objek.

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

Reflog mencatat kapan refs berubah, sehingga git reflog dapat menampilkan riwayat HEAD dan ujung branch.

File Reflog berada di .git/logs/:

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

Setiap baris mencatat:

Kode
<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 menggunakan reflog refs/stash sebagai penyimpanan tumpukannya.

Reftable

Reftable adalah format biner yang lebih baru untuk menyimpan refs, dirancang agar lebih cepat saat menangani banyak refs (ribuan branch). Format ini menggantikan refs lepas dan packed-refs.

File Reftable disimpan di .git/reftable/ dan berisi:

  • Catatan ref (diurutkan berdasarkan nama)
  • Catatan log (entri reflog)
  • Pengindeksan tingkat blok untuk pencarian cepat

Pitmaster membaca catatan ref dari file Reftable jika tersedia; catatan log tidak dibaca. Jika Reftable tersedia, format ini menggantikan packed-refs untuk pencantuman. Pembaruan ref selalu ditulis sebagai file lepas, yang tidak dibaca git dalam repositori Reftable, jadi perlakukan repositori Reftable sebagai hanya-baca.

Konvensi penamaan ref

Pola Arti
refs/heads/<name> Branch lokal
refs/tags/<name> Tag (ringan atau beranotasi)
refs/remotes/<remote>/<name> Branch pelacakan remote
refs/stash Tumpukan stash
refs/notes/<namespace> Catatan Git
HEAD Checkout saat ini (simbolis atau terlepas)
FETCH_HEAD Hasil fetch terakhir (ditulis oleh git; Pitmaster milik fetch() tidak menulisnya)
MERGE_HEAD Branch yang sedang digabungkan (saat merge)
ORIG_HEAD HEAD sebelumnya (sebelum operasi berisiko)

Bekerja dengan refs melalui 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');