Ugrás a tartalomhoz

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.

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

A Stash objektum létrehozása

PHP
use Pitmaster\Stash\Stash;

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

Push (módosítások mentése)

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

A Push a következőket végzi:

  1. Beolvassa az aktuális HEAD commitot
  2. Fát épít az aktuális indexből (staged állapot)
  3. Létrehoz egy „index commitot”, amelynek szülője a HEAD
  4. Fát épít az aktuális munkakönyvtárból (az unstaged módosításokat is beleértve)
  5. Létrehozza a stash commitot (munkafa, szülők = HEAD + index commit)
  6. A refs/stash értékét a stash commitra állítja
  7. Reflog-bejegyzést fűz a veremhez
  8. 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

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

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

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

Apply (visszaállítás eltávolítás nélkül)

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

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
}

Minden bejegyzés a következőket tartalmazza:

  • index: a stash pozíciója (0 = legújabb)
  • message: a stash üzenete
  • hash: a stash commit hash-e

Drop (eltávolítás alkalmazás nélkül)

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

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

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

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

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

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

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 a push($message, includeUntracked: true) segítségével
  • Nincs --patch mó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 a Repository::resolve() által
  • A stash egy összekapcsolt munkafában rossz konfigurációs és reflog-helyet használ (#14)