Formát indexu
Súbor indexu (.git/index) slúži ako oblasť na prípravu zmien. Zaznamenáva súbory, ktoré sa zahrnú do nasledujúceho commitu, spolu s metadátami na efektívny výpočet stavu. Pitmaster číta verzie indexu 2, 3 a 4 a zapisuje verzie 2 a 3. Zápis indexu načítaného ako verzia 4 vytvorí súbor, ktorý git nedokáže prečítať (#15), preto pred zápisom cez Pitmaster preveďte takéto úložiská pomocou git update-index --index-version 2.
Čítanie indexu
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() vráti prázdny index, ak súbor neexistuje. entries() vráti iba položky fázy 0; allEntries() zahrnie konfliktné fázy a paths() vypíše sledované cesty.
IndexEntry
Každá položka indexu je nemenný objekt s hodnotami:
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
Vytváranie položiek
// 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);
Úprava indexu
// 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() a removeEntries() spracujú viacero položiek alebo ciest v jednom priechode. Každá zmena položiek odstráni uložené rozšírenia (pozrite nižšie).
Zápis indexu
use Pitmaster\Index\IndexWriter;
IndexWriter::write($index, '/path/to/.git/index');
Alebo cez úložisko (ktoré to spracuje automaticky):
$repo->add('src/File.php'); // Reads file, creates blob, updates index, writes
$repo->remove('--cached', 'old-file.txt'); // Removes from index only, writes
Binárny formát
Hlavička
Signature: DIRC (4 bytes, ASCII)
Version: 4 bytes, big-endian (2, 3, or 4)
Entry count: 4 bytes, big-endian
Formát položky (v2)
Každá položka má tento formát:
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
Pole príznakov:
- Bit 15: príznak assume-valid
- Bity 13-12: fáza zlúčenia (0-3)
- Bity 11-0: dĺžka názvu cesty alebo 0xFFF, ak cesta má aspoň 4095 bajtov (čitateľ potom hľadá znak NUL)
Rozdiely medzi verziami
Verzia 2: základný formát opísaný vyššie. Najbežnejšia.
Verzia 3: pridáva rozšírené príznaky (2 dodatočné bajty za bežnými príznakmi). Podporuje bity skip-worktree a intent-to-add. Používa sa, keď niektorá položka vyžaduje rozšírené príznaky.
Verzia 4: pridáva kompresiu prefixov ciest. Namiesto ukladania úplných ciest sa pri každej položke ukladá počet bajtov, ktoré treba odstrániť z cesty predchádzajúcej položky, a potom nová prípona. Výrazne zmenšuje veľkosť indexu pri hlboko vnorených stromoch.
Konfliktné fázy
Počas konfliktu zlúčenia môže mať jedna cesta viacero položiek indexu v rôznych fázach:
| Fáza | Význam |
|---|---|
| 0 | Bežná (bez konfliktu) |
| 1 | Základná verzia (spoločný predok) |
| 2 | Naša verzia (aktuálna vetva) |
| 3 | Ich verzia (zlučovaná vetva) |
// 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]
Rozšírenia indexu
Rozšírenia sa pridávajú za položky pred kontrolným súčtom. Každé rozšírenie má 4-bajtový podpis, 4-bajtovú veľkosť a dáta premenlivej dĺžky.
Pitmaster uchováva bloky rozšírení ako surové bajty ($index->extensions() vracia dvojice signature a data) a pri nezmenených položkách ich zapisuje späť bez úprav. Nerozpoznáva ani nevytvára žiadne rozšírenia; každá zmena položiek ich odstráni a git ich podľa potreby prijme a znovu vytvorí. Nižšie sú pre informáciu opísané rozšírenia používané v git.
Rozšírenie TREE
Ukladá do vyrovnávacej pamäte vopred vypočítané objekty stromov pre adresáre. Urýchľuje git status a git commit tým, že sa strom nemusí znova počítať, keď sa zmenil iba index.
Podpis: TREE (0x54524545)
Rozšírenie REUC
Zaznamenáva informácie na obnovenie zmien. Po vyriešení konfliktu sa sem presunú pôvodné položky 1/2/3, aby sa dali obnoviť pomocou git checkout -m.
Podpis: REUC (0x52455543)
Ďalšie rozšírenia
git zapisuje aj:
EOIE(koniec položky indexu): označuje, kde sa položky končia a začínajú rozšíreniaIEOT(tabuľka posunov položiek indexu): umožňuje paralelné načítanie indexulink(rozdelený index): pre veľmi veľké repozitáre používajúce rozdelený index
Vo formáte git sú rozšírenia, ktorých podpis sa začína veľkým písmenom, voliteľné a tie s malým písmenom povinné. Pitmaster prijíma oba druhy bez kontroly. Rozdelený index (link) sa nenačítava, takže repozitáre, ktoré ho používajú, nie sú podporované.
Kontrolný súčet
Súbor indexu sa končí kontrolným súčtom všetkých predchádzajúcich bajtov vypočítaným pomocou hašovacej funkcie repozitára (20 bajtov SHA-1, 32 bajtov SHA-256). Pitmaster ho pri ukladaní zapisuje, ale pri čítaní ho neoveruje.
Index::open() a Index::parse() vyvolajú IndexParseException, ak chýba podpis DIRC alebo ak verzia nie je podporovaná.
Index a stav
Index je kľúčový pri výpočte stavu. WorkingTreeStatus porovnáva:
- Strom HEAD s indexom: zisťuje zmeny pripravené na potvrdenie
- Index s pracovným stromom: zisťuje nepripravené zmeny
Štatistické polia (ctime, mtime, dev, ino, size, mode, uid, gid) umožňujú rýchlo overiť, či sa súbor zmenil, bez čítania jeho obsahu. Ak sa štatistické polia zhodujú a súbor bol naposledy upravený pred začiatkom kontroly stavu, považuje sa za nezmenený. Odlišná veľkosť okamžite označí súbor ako upravený. V opačnom prípade Pitmaster súbor načíta a vypočíta jeho hash, aby overil, či sa skutočne zmenil jeho obsah.
Príznak assume-valid
if ($entry->assumeValid()) {
// Entry has assume-valid set (flags & 0x8000)
}
Tento príznak hovorí git, aby pri kontrole stavu preskočil kontrolu súboru, aj keď sa štatistické polia zmenili (git update-index --assume-unchanged). Pitmaster tento bit zachováva pri prepisovaní položiek, ale jeho kontrola stavu ho nerešpektuje a súbor aj tak porovnáva.