Stash
A Git stash elmenti a munkakönyvtár és az index módosításait, így átválthat egy másik feladatra, majd később visszaállíthatja őket. A Pitmaster a stash-t a refs/stash és a reflog használatával, a git tárolási elrendezésében valósítja meg. A Repository osztálynak nincs stash metódusa; használja az alábbi Stash osztályt.
Két ismert probléma: egynél több bejegyzés esetén a drop() (és így a pop() is) a listában hagyja a bejegyzést (#10), a apply() pedig felülírja a nem commitolt módosításokat azokban a fájlokban, amelyeket a stash nem érint (#11). A stash-t tiszta munkafán alkalmazza.
A stash működése
Minden stash-bejegyzés két szülővel rendelkező commit:
- 0. szülő: a stash készítésekor érvényes HEAD commit (az alapállapot)
- 1. szülő: az index állapotát rögzítő commit (az előkészített módosítások)
A stash commit saját fája a munkafa állapotát rögzíti (a még elő nem készített módosításokkal együtt).
A stash „verem” a refs/stash reflogja. A legutóbbi stash maga a refs/stash. A régebbi bejegyzések reflog-bejegyzések: stash@{0}, stash@{1} stb.
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
A Stash objektum létrehozása
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (módosítások mentése)
// 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);
A Push a következőket végzi:
- Beolvassa az aktuális HEAD commitot
- Fát épít az aktuális indexből (staged állapot)
- Létrehoz egy „index commitot”, amelynek szülője a HEAD
- Fát épít az aktuális munkakönyvtárból (az unstaged módosításokat is beleértve)
- Létrehozza a stash commitot (munkafa, szülők = HEAD + index commit)
- A
refs/stashértékét a stash commitra állítja - Reflog-bejegyzést fűz a veremhez
- Visszaállítja a munkafát és az indexet a HEAD állapotára
A push után a követett fájlok megegyeznek a HEAD állapotával. A nem követett fájlok megmaradnak, hacsak nincs beállítva a includeUntracked; ekkor eltávolítja és a stash commit saját fájában tárolja őket (a git ehelyett egy harmadik szülőben tárolja őket, így a git követett módosításként látja ezeket). A push() akkor is létrehoz egy bejegyzést, ha semmi sem változott; ilyenkor a git ezt jelzi: „Nincs mentendő helyi módosítás”.
Alapértelmezett üzenettel
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Leválasztott HEAD esetén a <branch> értéke HEAD.
Pop (alkalmazás és eltávolítás)
$stash->pop();
Alkalmazza a legfelső stash-bejegyzést a munkakönyvtárra, majd eltávolítja a veremből. A apply(), majd a drop() művelettel egyenértékű.
Adott bejegyzés Pop művelete
$stash->pop(1); // Pop stash@{1}
Apply (visszaállítás eltávolítás nélkül)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Az Apply fájlonként egyesíti a stash tartalmát az aktuális HEAD fájljaival (stash-alap, aktuális HEAD, stash-fa). A stash-bejegyzés a veremben marad, az index pedig érintetlen marad. Ütközés esetén a fájlokban Updated upstream és Stashed changes címkéjű jelölők maradnak, és a művelet Pitmaster\Exceptions\MergeConflictException kivételt dob; a pop() ezután is megtartja a bejegyzést. Az Apply előzetesen nem ellenőrzi a helyi módosításokat (lásd a fenti megjegyzést).
Bejegyzések listázása
$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
}
Minden bejegyzés a következőket tartalmazza:
index: a stash pozíciója (0 = legújabb)message: a stash üzenetehash: a stash commit hash-e
Drop (eltávolítás alkalmazás nélkül)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Ha az egyetlen bejegyzést dobja el, a refs/stash és a reflog is törlődik. Több bejegyzés esetén a drop() jelenleg csak a refs/stash értékét módosítja, a reflogot változatlanul hagyja, ezért a bejegyzés a listEntries() részében marad (#10).
Tárolási részletek
A stash commit felépítése
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Az index commit felépítése
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Az identitás a GIT_COMMITTER_NAME, majd a GIT_AUTHOR_NAME, aztán a tároló konfigurációjában szereplő user.name, végül a Pitmaster értékéből származik (az e-mail-címnél ugyanez a sorrend érvényes, amely a pitmaster@example.invalid értékkel zárul). A dátum a GIT_COMMITTER_DATE, majd a GIT_AUTHOR_DATE értékéből, végül az aktuális időből származik. A commitokkal ellentétben a stash nem olvassa be a PITMASTER_* konstansokat.
Reflog tárolása
A stash-bejegyzések tárolási helye: .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
A reflog időrendi sorrendben olvasható. A stash-lista megfordítja ezt a sorrendet (a legújabbal kezdve). A stash reflogja a core.logallrefupdates értékétől függetlenül íródik.
CLI használata
# 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
Az üzenet a push (vagy a save) utáni argumentum; a -m nem támogatott. A pop, a apply és a drop mindig a stash@{0} elemen működik; a CLI nem rendelkezik --include-untracked értékkel.
Korlátozások
- Csak teljes munkakönyvtár menthető el stashként (nincs
--keep-index); nem követett fájlok csak apush($message, includeUntracked: true)segítségével - Nincs
--patchmód (interaktív hunkkiválasztás) - A Pop/Apply visszaállítja a munkafa állapotát, az indexet azonban nem. A stage-elt módosítások unstaged módosításként térnek vissza, az újonnan stage-elt fájlok pedig nem követett fájlokként
- A
stash@{n}nem fogadható el aRepository::resolve()által - A stash egy összekapcsolt munkafában rossz konfigurációs és reflog-helyet használ (#14)