Oblika indeksa
Datoteka indeksa (.git/index) je območje priprave. Zabeleži datoteke, ki bodo vključene v naslednji potrditveni zapis, skupaj z metapodatki za učinkovito izračunavanje stanja. Pitmaster bere različice indeksa 2, 3 in 4 ter zapisuje različici 2 in 3. Zapis indeksa, prebranega kot različica 4, ustvari datoteko, ki je git ne more prebrati (#15), zato take repozitorije pred zapisovanjem prek Pitmaster pretvorite z git update-index --index-version 2.
Branje indeksa
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() vrne prazen indeks, če datoteka ne obstaja. entries() vrne samo vnose stopnje 0; allEntries() vključuje stopnje sporov, paths() pa izpiše sledene poti.
IndexEntry
Vsak vnos v indeksu je nespremenljiv objekt vrednosti:
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
Ustvarjanje vnosov
// 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);
Spreminjanje indeksa
// 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() in removeEntries() v enem prehodu obdelata več vnosov ali poti. Vsaka sprememba vnosov odstrani shranjene razširitve (glejte spodaj).
Zapisovanje indeksa
use Pitmaster\Index\IndexWriter;
IndexWriter::write($index, '/path/to/.git/index');
Ali prek repozitorija (ki to uredi samodejno):
$repo->add('src/File.php'); // Reads file, creates blob, updates index, writes
$repo->remove('--cached', 'old-file.txt'); // Removes from index only, writes
Binarna oblika
Glava
Signature: DIRC (4 bytes, ASCII)
Version: 4 bytes, big-endian (2, 3, or 4)
Entry count: 4 bytes, big-endian
Oblika vnosa (v2)
Vsak vnos je sestavljen takole:
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
Polje zastavic:
- Bit 15: zastavica »predpostavi, da je veljavno«
- Bita 13–12: stopnja združitve (0–3)
- Biti 11–0: dolžina imena poti ali 0xFFF, kadar je pot dolga 4095 bajtov ali več (bralnik nato poišče NUL)
Razlike med različicami
Različica 2: osnovna oblika, opisana zgoraj. Najpogostejša.
Različica 3: doda razširjene zastavice (2 dodatna bajta za običajnimi zastavicami). Podpira bita skip-worktree in intent-to-add. Uporablja se, kadar kateri koli vnos potrebuje razširjene zastavice.
Različica 4: doda stiskanje predpon poti. Namesto shranjevanja celotnih poti vsak vnos shrani število bajtov, ki jih je treba odstraniti s poti prejšnjega vnosa, nato pa novo pripono. To občutno zmanjša velikost indeksa pri globoko ugnezdenih drevesih.
Stopnje sporov
Med sporom pri združevanju ima lahko ena pot več vnosov indeksa na različnih stopnjah:
| Stopnja | Pomen |
|---|---|
| 0 | Običajno (brez spora) |
| 1 | Osnovna različica (skupni prednik) |
| 2 | Naša različica (trenutna veja) |
| 3 | Njihova različica (veja, ki se združuje) |
// 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]
Razširitve indeksa
Razširitve so dodane za vnosi in pred kontrolno vsoto. Vsaka razširitev ima 4-bajtni podpis, 4-bajtno velikost in podatke spremenljive dolžine.
Pitmaster ohrani bloke razširitev kot surove bajte ($index->extensions() vrne pare signature in data) ter jih nespremenjene zapiše nazaj, kadar vnosi niso bili spremenjeni. Razširitev ne razlaga ali ustvarja; vsaka sprememba vnosov pa vse razširitve odstrani, kar git sprejme in po potrebi znova zgradi. Razširitve, ki jih uporablja git, so za referenco opisane spodaj.
Razširitev TREE
Predpomni vnaprej izračunane drevesne objekte za imenike. Pospeši git status in git commit, saj prepreči ponovno računanje dreves, kadar se spremeni samo indeks.
Podpis: TREE (0x54524545)
Razširitev REUC
Zabeleži informacije za razveljavitev razrešitve. Ko je spor razrešen, se izvirni vnosi stopenj 1/2/3 premaknejo sem, da jih je mogoče obnoviti z git checkout -m.
Podpis: REUC (0x52455543)
Druge razširitve
git zapisuje tudi:
EOIE(konec vnosa indeksa): označuje, kje se vnosi končajo in začnejo razširitveIEOT(tabela odmikov vnosov indeksa): omogoča vzporedno nalaganje indeksalink(razdeljeni indeks): za zelo velike repozitorije, ki uporabljajo razdeljeni indeks
V obliki git so razširitve s podpisom, ki se začne z veliko črko, neobvezne, tiste z malo črko pa obvezne. Pitmaster sprejema obe vrsti brez preverjanja. Razdeljeni indeks (link) ni naložen, zato repozitoriji, ki ga uporabljajo, niso podprti.
Kontrolna vsota
Datoteka indeksa se konča s kontrolno vsoto vseh predhodnih bajtov, izračunano s funkcijo zgoščevanja repozitorija (20 bajtov SHA-1, 32 bajtov SHA-256). Pitmaster jo zapiše ob shranjevanju, pri branju pa je ne preveri.
Index::open() in Index::parse() ob manjkajočem podpisu DIRC ali nepodprti različici sprožita IndexParseException.
Indeks in stanje
Indeks je osrednji del izračunavanja stanja. WorkingTreeStatus primerja:
- Drevo HEAD z indeksom: zazna pripravljene spremembe
- Indeks z delovnim drevesom: zazna nepripravljene spremembe
Statistična polja (ctime, mtime, dev, ino, size, mode, uid, gid) omogočajo hitra preverjanja »ali se je ta datoteka spremenila?« brez branja vsebine datoteke. Če se statistična polja ujemajo in je bila datoteka nazadnje spremenjena pred začetkom pregleda stanja, se domneva, da je nespremenjena. Drugačna velikost jo takoj označi kot spremenjeno. Sicer Pitmaster prebere datoteko in izračuna njeno zgoščeno vrednost, da preveri, ali se je vsebina dejansko spremenila.
Zastavica »predpostavi, da je veljavno«
if ($entry->assumeValid()) {
// Entry has assume-valid set (flags & 0x8000)
}
Ta zastavica pove git, naj med preverjanjem stanja preskoči preverjanje datoteke, tudi če so se statistična polja spremenila (git update-index --assume-unchanged). Pitmaster ohrani bit pri ponovnem zapisovanju vnosov, vendar ga pri izračunu stanja ne upošteva in datoteko vseeno primerja.