Lewati ke konten

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

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

PHP
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

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

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

PHP
use Pitmaster\Index\IndexWriter;

IndexWriter::write($index, '/path/to/.git/index');

Atau melalui repositori (yang menanganinya secara otomatis):

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

Kode
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:

Kode
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)
PHP
// 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 ekstensi
  • IEOT (tabel offset entri indeks): memungkinkan pemuatan indeks secara paralel
  • link (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:

  1. Pohon HEAD dengan indeks: mendeteksi perubahan yang sudah di-stage
  2. 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

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