Zásobník zmien
Funkcia Git ukladá zmeny pracovného adresára a indexu, aby ste mohli prejsť na inú úlohu a neskôr ich obnoviť. Pitmaster implementuje túto funkciu pomocou refs/stash a reflogu v rozložení úložiska git. Trieda Repository nemá metódu zásobníka zmien; použite nižšie uvedenú triedu Stash.
Známe sú dve chyby: ak je položiek viac než jedna, drop() (a teda aj pop()) ponechá položku v zozname (#10) a apply() prepíše nepotvrdené zmeny v súboroch, ktorých sa uložená zmena netýka (#11). Zmeny zo zásobníka použite na čistom pracovnom strome.
Ako funguje zásobník zmien
Každá položka zásobníka zmien je revízia s dvoma rodičmi:
- Rodič 0: revízia HEAD v čase uloženia (základný stav)
- Rodič 1: revízia zachytávajúca stav indexu (pripravené zmeny)
Vlastný strom revízie zásobníka zmien zachytáva stav pracovného stromu (vrátane nepripravených zmien).
„Zásobník“ zmien tvorí reflog pre refs/stash. Najnovšia uložená zmena je samotná refs/stash. Staršie položky sú záznamy reflogu: stash@{0}, stash@{1} atď.
refs/stash -> stash commit (worktree state)
parent[0] -> HEAD at time of stash
parent[1] -> index state commit
parent[0] -> HEAD at time of stash
Vytvorenie objektu zásobníka zmien
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (uloženie zmien)
// public function push(string $message = '', bool $includeUntracked = false): ObjectId
$stashId = $stash->push('Work in progress on login');
echo "Saved: {$stashId->hex}\n";
// Also stash untracked files
$stashId = $stash->push('With new files', includeUntracked: true);
Operácia Push vykoná tieto kroky:
- Načíta aktuálnu revíziu HEAD
- Vytvorí strom z aktuálneho indexu (stav pripravených zmien)
- Vytvorí „revíziu indexu“, ktorej rodičom je HEAD
- Vytvorí strom z aktuálneho pracovného adresára (vrátane nepripravených zmien)
- Vytvorí revíziu zásobníka zmien (strom pracovného adresára, rodičia = HEAD + revízia indexu)
- Aktualizuje
refs/stashtak, aby ukazoval na revíziu zásobníka zmien - Pridá záznam reflogu zásobníka
- Obnoví pracovný strom a index do stavu HEAD
Po operácii push sa sledované súbory zhodujú so stavom HEAD. Nesledované súbory zostanú, pokiaľ nie je nastavená možnosť includeUntracked; v takom prípade sa odstránia a uložia do vlastného stromu revízie zásobníka zmien (git ich namiesto toho uloží ako tretieho rodiča, takže git ich považuje za sledované zmeny). Funkcia push() vytvorí položku aj vtedy, keď sa nič nezmenilo; v takom prípade git oznámi „Nie sú žiadne miestne zmeny na uloženie“.
S predvolenou správou
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Pri odpojenom HEAD má <branch> hodnotu HEAD.
Pop (použitie a odstránenie)
$stash->pop();
Použije najvrchnejšiu položku zásobníka zmien na pracovný adresár a odstráni ju zo zásobníka. Zodpovedá operácii apply(), po ktorej nasleduje drop().
Použitie konkrétnej položky
$stash->pop(1); // Pop stash@{1}
Apply (obnovenie bez odstránenia)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Operácia Apply zlučuje uloženú zmenu so súbormi aktuálneho HEAD, a to po jednotlivých súboroch (základ uloženia, aktuálny HEAD, strom uloženia). Položka zostáva v zásobníku a index sa nemení. Pri konfliktoch sa v súboroch ponechajú značky Updated upstream a Stashed changes a vyvolá sa Pitmaster\Exceptions\MergeConflictException; pop() potom položku ponechá. Operácia Apply najprv nekontroluje, či existujú miestne zmeny (pozrite poznámku na začiatku).
Zoznam položiek
$entries = $stash->listEntries();
foreach ($entries as $entry) {
echo "stash@{{$entry['index']}}: {$entry['message']}\n";
// stash@{0}: On main: Work in progress on login
// stash@{1}: WIP on main: f707601 Add login form
}
Každá položka obsahuje:
index: pozíciu v zásobníku zmien (0 = najnovšia)message: správu o uloženej zmenehash: hašovací súčet revízie zásobníka zmien
Drop (odstránenie bez použitia)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Ak odstránite jedinú položku, odstráni sa refs/stash aj jeho reflog. Ak je položiek viac, drop() zatiaľ iba presunie refs/stash a reflog nezmení, takže položka zostane v listEntries() (#10).
Podrobnosti o úložisku
Štruktúra revízie zásobníka zmien
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Štruktúra revízie indexu
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Identita sa určí postupne z GIT_COMMITTER_NAME, GIT_AUTHOR_NAME a user.name v konfigurácii repozitára a potom z Pitmaster (rovnaké poradie platí aj pre e-mail; posledná možnosť je pitmaster@example.invalid). Dátum sa určí z GIT_COMMITTER_DATE, potom GIT_AUTHOR_DATE a napokon z aktuálneho času. Na rozdiel od revízií, zásobník zmien nečíta konštanty PITMASTER_*.
Úložisko Reflog
Položky zásobníka zmien sa ukladajú do .git/logs/refs/stash:
0000000000000000000000000000000000000000 abc123... Name <email> 1700000000 +0000 WIP on main: f707601 Add login form
abc123... def456... Name <email> 1700000001 +0000 On main: Work in progress on login
Reflog sa číta v chronologickom poradí. Zoznam zásobníka zmien ho zoradí opačne (od najnovšej položky). Reflog zásobníka zmien sa zapisuje bez ohľadu na core.logallrefupdates.
Používanie CLI
# Save current changes
./vendor/bin/pitmaster stash
./vendor/bin/pitmaster stash push
./vendor/bin/pitmaster stash push "Work in progress on login"
# Apply and remove top entry
./vendor/bin/pitmaster stash pop
# Apply without removing
./vendor/bin/pitmaster stash apply
# List all entries
./vendor/bin/pitmaster stash list
# Remove top entry without applying
./vendor/bin/pitmaster stash drop
Správa je argument za push (alebo save); možnosť -m nie je podporovaná. Operácie pop, apply a drop vždy pracujú s stash@{0}; CLI nemá --include-untracked.
Obmedzenia
- Podporuje sa iba uloženie celého pracovného adresára (bez
--keep-index); nesledované súbory možno uložiť iba pomocoupush($message, includeUntracked: true) - Režim
--patch(interaktívny výber častí zmien) nie je k dispozícii - Pri operácii Pop alebo Apply sa obnoví stav pracovného stromu, ale nie indexu. Pripravené úpravy sa obnovia ako nepripravené a novopripravené súbory ako nesledované
stash@{n}operáciaRepository::resolve()neprijíma- Uloženie zmien v prepojenom pracovnom strome používa nesprávne umiestnenie konfigurácie a reflogu (#14)