Stash
Git stash sparar ändringar i arbetskatalogen och indexet så att du kan växla till en annan uppgift och återställa dem senare. Pitmaster implementerar stash med refs/stash och reflog, enligt gits lagringslayout. Det finns ingen stash-metod på Repository; använd klassen Stash nedan.
Två kända problem: med fler än en post lämnar drop() (och därmed pop()) posten kvar i listan (#10), och apply() skriver över oincheckade ändringar i filer som stash inte berör (#11). Tillämpa stashar på ett rent arbetsträd.
Så fungerar stash
Varje stash-post är en commit med två föräldrar:
- Förälder 0: HEAD-committen vid stash-tillfället (bastillståndet)
- Förälder 1: en commit som fångar indexets tillstånd (stegade ändringar)
Stash-commitens eget träd fångar arbetsträdets tillstånd (inklusive ostegade ändringar).
Stash-"stacken" är refloggen för refs/stash. Den senaste stashen är själva refs/stash. Äldre poster är 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
Skapa Stash-objektet
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Skicka (spara ändringar)
// 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);
Skicka gör följande:
- Läser den aktuella HEAD-committen
- Bygger ett träd från det aktuella indexet (stegat tillstånd)
- Skapar en "index-commit" med HEAD som förälder
- Bygger ett träd från den aktuella arbetskatalogen (inklusive ostegade ändringar)
- Skapar stash-committen (arbetsträdets träd, föräldrar = HEAD + index-commit)
- Uppdaterar
refs/stashså att den pekar på stash-committen - Lägger till en reflogpost för stacken
- Återställer arbetsträdet och indexet till HEAD
Efter push matchar spårade filer HEAD. Ospårade filer finns kvar om includeUntracked inte är angivet; annars tas de bort och lagras i stash-commitens eget träd (git lagrar dem i en tredje förälder i stället, så git ser dem som spårade ändringar). push() skapar en post även när inget har ändrats, då rapporterar git ”Inga lokala ändringar att spara”.
Med standardmeddelande
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Med frikopplad HEAD är <branch> HEAD.
Pop (tillämpa och ta bort)
$stash->pop();
Tillämpa den översta stash-posten på arbetskatalogen och ta bort den från stacken. Motsvarar apply() följt av drop().
Poppa en specifik post
$stash->pop(1); // Pop stash@{1}
Tillämpa (återställ utan att ta bort)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Apply sammanfogar stashen med den aktuella HEAD-filen fil för fil (stashens bas, aktuell HEAD, stash-trädet). Stash-posten ligger kvar i stacken och indexet påverkas inte. Konflikter lämnar markörer märkta Updated upstream och Stashed changes i filerna och utlöser Pitmaster\Exceptions\MergeConflictException; pop() behåller då posten. Apply kontrollerar inte först om det finns lokala ändringar (se anmärkningen högst upp).
Lista 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
}
Varje post innehåller:
index: stashens position (0 = senaste)message: stash-meddelandethash: stash-commitens hash
Ta bort (utan att tillämpa)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Om den enda posten tas bort försvinner refs/stash och dess reflogg. Med fler poster flyttar drop() för närvarande bara refs/stash och lämnar refloggen oförändrad, så posten ligger kvar i listEntries() (#10).
Lagringsdetaljer
Stash-commitens struktur
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Index-commitens struktur
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Identiteten hämtas från GIT_COMMITTER_NAME, därefter GIT_AUTHOR_NAME, sedan user.name i arkivkonfigurationen och slutligen Pitmaster (samma ordning gäller för e-postadressen och avslutas med pitmaster@example.invalid). Datumet hämtas från GIT_COMMITTER_DATE, därefter GIT_AUTHOR_DATE och sedan aktuell tid. Till skillnad från commits läser stash inte konstanterna PITMASTER_*.
Reflog-lagring
Stash-poster lagras 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 ordning. Stash-listan vänder på ordningen (nyaste först). Stash-refloggen skrivs oavsett core.logallrefupdates.
Användning av 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
Meddelandet är argumentet efter push (eller save); -m stöds inte. pop, apply och drop fungerar alltid på stash@{0}; CLI har ingen --include-untracked.
Begränsningar
- Endast stash av hela arbetskatalogen (ingen
--keep-index); ospårade filer endast viapush($message, includeUntracked: true) - Inget läge
--patch(interaktiv val av delar) - Pop/apply återställer arbetsträdets tillstånd; indexet återställs inte. Stegade ändringar kommer tillbaka som ostegade ändringar, och nyligen stegade filer kommer tillbaka som ospårade filer
stash@{n}accepteras inte avRepository::resolve()- Stash i ett länkat arbetsträd använder fel konfiguration och reflogplats (#14)