Format Indeks
File indeks (.git/index) adalah area staging. File ini mencatat file yang akan disertakan dalam commit berikutnya, beserta metadata untuk menghitung status secara efisien. Pitmaster membaca indeks versi 2, 3, dan 4, serta menulis versi 2 dan 3. Menulis indeks yang dibaca sebagai versi 4 menghasilkan file yang tidak dapat dibaca git (#15), jadi konversikan repositori tersebut dengan git update-index --index-version 2 sebelum menulis melalui Pitmaster.
Membaca indeks
use Pitmaster\Index\Index;
$index = Index::open('/path/to/.git/index'); // pass 32 as the second argument for SHA-256
// Or through the repository
$index = $repo->index();
// Entry count (all stages)
echo $index->count();
// Iterate stage 0 entries, keyed by path
foreach ($index->entries() as $path => $entry) {
echo "{$entry->path} {$entry->hash->hex}\n";
}
// Look up a specific entry
$entry = $index->entry('src/File.php');
Index::open() mengembalikan indeks kosong jika file tidak ada. entries() hanya mengembalikan entri tahap 0; allEntries() menyertakan tahap konflik, dan paths() mencantumkan jalur yang dilacak.
IndexEntry
Setiap entri dalam indeks adalah objek nilai readonly:
use Pitmaster\Index\IndexEntry;
$entry->path; // File path relative to repo root
$entry->hash; // ObjectId of the blob
$entry->mode; // File mode as an int (0100644, 0100755, 0120000, 0160000)
$entry->fileSize; // File size in bytes
$entry->ctimeSec; // Metadata change time (seconds)
$entry->ctimeNsec; // Metadata change time nanoseconds
$entry->mtimeSec; // Modification time (seconds)
$entry->mtimeNsec; // Modification time nanoseconds
$entry->dev; // Device number
$entry->ino; // Inode number
$entry->uid; // User ID
$entry->gid; // Group ID
$entry->flags; // Raw flags field
$entry->extendedFlags; // v3 extended flags (skip-worktree, intent-to-add)
$entry->stage(); // Conflict stage (0 = normal, 1 = base, 2 = ours, 3 = theirs)
$entry->assumeValid(); // Assume-valid bit
$entry->skipWorktree(); // Skip-worktree bit
Membuat entri
// From file stat
$entry = IndexEntry::fromStat('src/File.php', $blobId, '/full/path/to/src/File.php');
// Minimal (no stat info)
$entry = IndexEntry::create('src/File.php', $blobId);
// With explicit mode
$entry = IndexEntry::create('deploy.sh', $blobId, 0100755);
Mengubah indeks
// Add or replace an entry
$index->addEntry($entry);
// Remove an entry
$index->removeEntry('src/OldFile.php');
// Replace all stages of a conflicted path with one resolved entry
$index->resolveConflict('src/File.php', $entry);
addEntries() dan removeEntries() memproses beberapa entri atau jalur sekaligus. Setiap perubahan pada entri akan menghapus ekstensi yang tersimpan (lihat di bawah).
Menulis indeks
use Pitmaster\Index\IndexWriter;
IndexWriter::write($index, '/path/to/.git/index');
Atau melalui repositori (yang menanganinya secara otomatis):
$repo->add('src/File.php'); // Reads file, creates blob, updates index, writes
$repo->remove('--cached', 'old-file.txt'); // Removes from index only, writes
Format biner
Header
Signature: DIRC (4 bytes, ASCII)
Version: 4 bytes, big-endian (2, 3, or 4)
Entry count: 4 bytes, big-endian
Format entri (v2)
Setiap entri terdiri dari:
ctime seconds: 4 bytes, big-endian
ctime nanoseconds: 4 bytes, big-endian
mtime seconds: 4 bytes, big-endian
mtime nanoseconds: 4 bytes, big-endian
dev: 4 bytes, big-endian
ino: 4 bytes, big-endian
mode: 4 bytes, big-endian
uid: 4 bytes, big-endian
gid: 4 bytes, big-endian
file size: 4 bytes, big-endian
object hash: 20 bytes SHA-1 or 32 bytes SHA-256 (raw binary)
flags: 2 bytes, big-endian
path: variable length, NUL-terminated
padding: 1-8 NUL bytes to align to 8-byte boundary
Kolom flags:
- Bit 15: flag assume-valid
- Bit 13-12: tahap merge (0-3)
- Bit 11-0: panjang nama jalur, atau 0xFFF jika jalur berukuran 4095 byte atau lebih (pembaca kemudian mencari NUL)
Perbedaan versi
Versi 2: format dasar yang dijelaskan di atas. Paling umum.
Versi 3: menambahkan flags tambahan (2 byte setelah flags biasa). Mendukung bit skip-worktree dan intent-to-add. Digunakan saat entri mana pun memerlukan flags tambahan.
Versi 4: menambahkan kompresi awalan jalur. Alih-alih menyimpan jalur lengkap, setiap entri menyimpan jumlah byte yang dihapus dari jalur entri sebelumnya, lalu akhiran baru. Ukuran indeks berkurang secara signifikan untuk pohon bertingkat dalam.
Tahap konflik
Saat terjadi konflik merge, satu jalur dapat memiliki beberapa entri indeks pada tahap berbeda:
| Tahap | Arti |
|---|---|
| 0 | Normal (tanpa konflik) |
| 1 | Versi dasar (leluhur bersama) |
| 2 | Milik kita (cabang saat ini) |
| 3 | Milik mereka (cabang yang sedang di-merge) |
// During a conflict, entries have non-zero stages
foreach ($index->allEntries() as $entry) {
if ($entry->stage() > 0) {
echo "Conflict: {$entry->path} (stage {$entry->stage()})\n";
}
}
$index->hasUnmerged(); // true while conflicts remain
$index->unmergedPaths(); // conflicted paths
$index->stageEntries('a.txt'); // [1 => base, 2 => ours, 3 => theirs]
Ekstensi indeks
Ekstensi ditambahkan setelah entri, sebelum checksum. Setiap ekstensi memiliki tanda tangan 4 byte, ukuran 4 byte, dan data dengan panjang bervariasi.
Pitmaster menyimpan blok ekstensi sebagai byte mentah ($index->extensions() mengembalikan pasangan signature dan data) dan menulisnya kembali tanpa perubahan jika entri tidak disentuh. Ia tidak menafsirkan atau membuat ekstensi apa pun. Setiap perubahan pada entri menghapus seluruh ekstensi; git menerima hal ini dan membangunnya kembali sesuai kebutuhan. Ekstensi yang digunakan git dijelaskan di bawah sebagai referensi.
Ekstensi TREE
Menyimpan cache objek pohon yang telah dihitung sebelumnya untuk direktori. Mempercepat git status dan git commit dengan menghindari penghitungan ulang pohon saat hanya indeks yang berubah.
Tanda tangan: TREE (0x54524545)
Ekstensi REUC
Mencatat informasi pembatalan resolusi. Setelah konflik diselesaikan, entri tahap 1/2/3 yang asli dipindahkan ke sini agar dapat dipulihkan dengan git checkout -m.
Tanda tangan: REUC (0x52455543)
Ekstensi lainnya
git juga menulis:
EOIE(akhir entri indeks): menandai akhir entri dan awal ekstensiIEOT(tabel offset entri indeks): memungkinkan pemuatan indeks secara paralellink(indeks terpisah): untuk repositori sangat besar yang menggunakan indeks terpisah
Dalam format git, ekstensi dengan tanda tangan yang diawali huruf besar bersifat opsional, sedangkan yang diawali huruf kecil wajib ada. Pitmaster menerima kedua jenis tanpa memeriksanya. Indeks terpisah (link) tidak dimuat, sehingga repositori yang menggunakannya tidak didukung.
Checksum
File indeks diakhiri checksum dari semua byte sebelumnya menggunakan hash repositori (20 byte SHA-1, 32 byte SHA-256). Pitmaster menulisnya saat menyimpan, tetapi tidak memverifikasinya saat membaca.
Index::open() dan Index::parse() melempar IndexParseException jika tanda tangan DIRC tidak ada atau versinya tidak didukung.
Indeks dan status
Indeks menjadi bagian utama dalam penghitungan status. WorkingTreeStatus membandingkan:
- Pohon HEAD dengan indeks: mendeteksi perubahan yang sudah di-stage
- Indeks dengan pohon kerja: mendeteksi perubahan yang belum di-stage
Kolom stat (ctime, mtime, dev, ino, size, mode, uid, gid) memungkinkan pemeriksaan cepat "apakah file ini berubah?" tanpa membaca isi file. Jika kolom stat cocok dan file terakhir diubah sebelum pemindaian status dimulai, file dianggap tidak berubah. Ukuran yang berbeda langsung menandai file sebagai berubah. Jika tidak, Pitmaster membaca file dan menghitung hash-nya untuk memeriksa perubahan isi yang sebenarnya.
Flag assume-valid
if ($entry->assumeValid()) {
// Entry has assume-valid set (flags & 0x8000)
}
Flag ini memberi tahu git agar melewati pemeriksaan file saat menghitung status, sekalipun kolom stat berubah (git update-index --assume-unchanged). Pitmaster mempertahankan bit tersebut saat menulis ulang entri, tetapi penghitungan statusnya tidak mengindahkan flag itu dan tetap membandingkan file.