Preskočiť na obsah

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

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

PHP
use Pitmaster\Stash\Stash;

$stash = new Stash(
    $repo->objectDatabase(),
    $repo->refDatabase(),
    $repo->gitDir(),
    $repo->workDir(),
);

Push (uloženie zmien)

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

  1. Načíta aktuálnu revíziu HEAD
  2. Vytvorí strom z aktuálneho indexu (stav pripravených zmien)
  3. Vytvorí „revíziu indexu“, ktorej rodičom je HEAD
  4. Vytvorí strom z aktuálneho pracovného adresára (vrátane nepripravených zmien)
  5. Vytvorí revíziu zásobníka zmien (strom pracovného adresára, rodičia = HEAD + revízia indexu)
  6. Aktualizuje refs/stash tak, aby ukazoval na revíziu zásobníka zmien
  7. Pridá záznam reflogu zásobníka
  8. 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

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

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

PHP
$stash->pop(1);  // Pop stash@{1}

Apply (obnovenie bez odstránenia)

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

PHP
$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 zmene
  • hash: hašovací súčet revízie zásobníka zmien

Drop (odstránenie bez použitia)

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

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

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

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

Shell
# 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 pomocou push($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ácia Repository::resolve() neprijíma
  • Uloženie zmien v prepojenom pracovnom strome používa nesprávne umiestnenie konfigurácie a reflogu (#14)