Stash
Git stash gemmer ændringer i arbejdsmappe og indeks, så du kan skifte til en anden opgave og gendanne dem senere. Pitmaster implementerer stash med refs/stash og reflog i gits lagerlayout. Der findes ingen stash-metode på Repository; brug klassen Stash nedenfor.
To kendte problemer: når der er mere end én post, lader drop() (og dermed pop()) posten blive stående på listen (#10), og apply() overskriver ikke-committede ændringer i filer, som stashen ikke berører (#11). Anvend stash-poster på et rent working tree.
Sådan fungerer stash
Hver stash-post er en commit med to forældre:
- Forælder 0: HEAD-committen på stash-tidspunktet (grundtilstanden)
- Forælder 1: en commit, der indfanger indeksets tilstand (staged ændringer)
Stash-commitens eget træ indfanger working tree-tilstanden (herunder ikke-staged ændringer).
Stash-"stakken" er refloggen for refs/stash. Den nyeste stash er selve refs/stash. Ældre poster er reflogposter: stash@{0}, stash@{1} osv.
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
Oprettelse af Stash-objektet
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (gem ændringer)
// 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);
Push gør følgende:
- Læser den aktuelle HEAD-commit
- Bygger et træ ud fra det aktuelle indeks (staged-tilstand)
- Opretter en "index commit" med HEAD som parent
- Bygger et træ ud fra den aktuelle arbejdsmappe (inklusive unstaged-ændringer)
- Opretter stash-committet (worktree-træ, parents = HEAD + index commit)
- Opdaterer
refs/stash, så den peger på stash-committet - Tilføjer en reflogpost til stakken
- Nulstiller arbejdstræet og indekset til HEAD
Efter push matcher sporede filer HEAD. Usporede filer forbliver, medmindre includeUntracked er angivet; så fjernes de og gemmes i stash-committets eget træ (git gemmer dem i stedet i en tredje parent, så git ser dem som sporede ændringer). push() opretter en post, selv når intet er ændret, hvor git rapporterer "No local changes to save".
Med standardbesked
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Med en detached HEAD er <branch> lig med HEAD.
Pop (anvend og fjern)
$stash->pop();
Anvender den øverste stash-post på arbejdsmappen og fjerner den fra stakken. Svarer til apply() efterfulgt af drop().
Pop en bestemt post
$stash->pop(1); // Pop stash@{1}
Apply (gendan uden at fjerne)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Apply fletter stashen ind i den aktuelle HEAD-fil for fil (stash-base, aktuel HEAD, stash-træ). Stash-posten forbliver i stakken, og indekset berøres ikke. Konflikter efterlader markører mærket Updated upstream og Stashed changes i filerne og kaster Pitmaster\Exceptions\MergeConflictException; pop() beholder derefter posten. Apply kontrollerer ikke først for lokale ændringer (se bemærkningen øverst).
Vis poster
$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
}
Hver post indeholder:
index: stash-positionen (0 = nyeste)message: stash-beskedenhash: stash-commit-hashen
Drop (fjern uden at anvende)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Hvis den eneste post fjernes, fjernes refs/stash og dens reflog. Hvis der er flere poster, flytter drop() i øjeblikket kun refs/stash og lader refloggen være uændret, så posten forbliver i listEntries() (#10).
Lagringsdetaljer
Stash-commitstruktur
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Index-commitstruktur
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Identiteten hentes fra GIT_COMMITTER_NAME, derefter GIT_AUTHOR_NAME, derefter user.name i repository-konfigurationen og til sidst Pitmaster (samme rækkefølge gælder for e-mailen, der slutter med pitmaster@example.invalid). Datoen hentes fra GIT_COMMITTER_DATE, derefter GIT_AUTHOR_DATE og til sidst det aktuelle tidspunkt. I modsætning til commits læser stash ikke konstanterne PITMASTER_*.
Reflog-lagring
Stash-poster gemmes i .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
Refloggen læses i kronologisk rækkefølge. Stash-listen vender rækkefølgen om (nyeste først). Stash-refloggen skrives uanset core.logallrefupdates.
Brug af 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
Beskeden er argumentet efter push (eller save); -m understøttes ikke. pop, apply og drop fungerer altid på stash@{0}; CLI har ingen --include-untracked.
Begrænsninger
- Kun stash af hele arbejdsbiblioteket (ingen
--keep-index); usporede filer kun viapush($message, includeUntracked: true) - Ingen tilstand for
--patch(interaktivt valg af hunk) - Pop/apply gendanner arbejdstræets tilstand; indekset gendannes ikke. Staged-ændringer kommer tilbage som unstaged-ændringer, og nyligt staged filer kommer tilbage som usporede filer
stash@{n}accepteres ikke afRepository::resolve()- Stash i et tilknyttet worktree bruger forkert konfiguration og reflogplacering (#14)