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.
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.
.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 adalah ref khusus yang dapat berupa:
Simbolis: menunjuk ke ref lain (paling umum, saat berada di branch):
ref: refs/heads/main\n
Terlepas: menunjuk langsung ke hash 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";
}
Refs terkemas
File packed-refs menggabungkan banyak refs ke dalam satu file demi efisiensi. Git membuatnya selama git pack-refs atau git gc.
# 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.
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).
$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:
- Ref lepas (file di
.git/refs/) - Reftable, jika
.git/reftable/tables.listada - 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.
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/:
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/stash
Setiap baris mencatat:
<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 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
// 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');