Preskočiť na obsah

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

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() 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:

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

Vytváranie položiek

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);

Úprava indexu

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() 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

PHP
use Pitmaster\Index\IndexWriter;

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

Alebo cez úložisko (ktoré to spracuje automaticky):

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

Kód
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:

Kód
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)
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]

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šírenia
  • IEOT (tabuľka posunov položiek indexu): umožňuje paralelné načítanie indexu
  • link (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:

  1. Strom HEAD s indexom: zisťuje zmeny pripravené na potvrdenie
  2. 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

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