Stash
Git stash slaat wijzigingen in de werkdirectory en index op, zodat u naar een andere taak kunt overschakelen en ze later kunt herstellen. Pitmaster implementeert stash met refs/stash en de reflog, volgens de opslagindeling van git. Repository heeft geen stash-methode; gebruik de onderstaande class Stash.
Twee bekende problemen: bij meer dan één item laat drop() (en dus ook pop()) het item in de lijst staan (#10), en apply() overschrijft niet-vastgelegde wijzigingen in bestanden die de stash niet aanraakt (#11). Pas stashes toe op een schone werkboom.
Hoe stash werkt
Elke stashvermelding is een commit met twee ouders:
- Ouder 0: de HEAD-commit ten tijde van de stash (de basistoestand)
- Ouder 1: een commit die de indexstatus vastlegt (klaargezette wijzigingen)
De eigen boom van de stash-commit legt de status van de werkboom vast (inclusief niet-klaargezette wijzigingen).
De stash-'stack' is de reflog van refs/stash. De meest recente stash is refs/stash zelf. Oudere items zijn reflog-vermeldingen: stash@{0}, stash@{1}, enzovoort.
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
Het Stash-object maken
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (wijzigingen opslaan)
// 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 doet het volgende:
- Leest de huidige HEAD-commit
- Bouwt een tree op basis van de huidige index (de gestagede status)
- Maakt een "index-commit" met HEAD als parent
- Bouwt een tree op basis van de huidige werkdirectory (inclusief niet-gestagede wijzigingen)
- Maakt de stash-commit (worktree-tree, parents = HEAD + index-commit)
- Werkt
refs/stashbij zodat deze naar de stash-commit verwijst - Voegt een reflog-vermelding toe voor de stack
- Zet de werkdirectory en index terug naar HEAD
Na push komen bijgehouden bestanden overeen met HEAD. Niet-bijgehouden bestanden blijven staan, tenzij includeUntracked is ingesteld; dan worden ze verwijderd en opgeslagen in de eigen tree van de stash-commit (git slaat ze in plaats daarvan op in een derde parent, zodat git ze als bijgehouden wijzigingen ziet). push() maakt ook een vermelding aan als er niets is gewijzigd; git meldt dan "No local changes to save".
Met standaardbericht
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Bij een detached HEAD is <branch> gelijk aan HEAD.
Pop (toepassen en verwijderen)
$stash->pop();
Past de bovenste stash-vermelding toe op de werkdirectory en verwijdert deze uit de stack. Dit is gelijk aan apply() gevolgd door drop().
Een specifieke vermelding poppen
$stash->pop(1); // Pop stash@{1}
Apply (herstellen zonder verwijderen)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Apply voegt de stash bestand voor bestand samen met het huidige HEAD-bestand (stash-basis, huidig HEAD, stash-tree). De stash-vermelding blijft in de stack staan en de index wordt niet aangepast. Bij conflicten blijven markeringen met de labels Updated upstream en Stashed changes in de bestanden staan en wordt Pitmaster\Exceptions\MergeConflictException gegenereerd; pop() behoudt de vermelding vervolgens. Apply controleert vooraf niet op lokale wijzigingen (zie de opmerking bovenaan).
Vermeldingen weergeven
$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
}
Elke vermelding bevat:
index: de stash-positie (0 = meest recent)message: het stash-berichthash: de hash van de stash-commit
Drop (verwijderen zonder toe te passen)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Als je de enige vermelding verwijdert, worden refs/stash en de bijbehorende reflog verwijderd. Zijn er meer vermeldingen, dan verplaatst drop() momenteel alleen refs/stash en blijft de reflog ongewijzigd, waardoor de vermelding in listEntries() blijft staan (#10).
Opslagdetails
Structuur van de stash-commit
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Structuur van de index-commit
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
De identiteit komt uit GIT_COMMITTER_NAME, vervolgens GIT_AUTHOR_NAME, daarna user.name in de repositoryconfiguratie en vervolgens Pitmaster (voor het e-mailadres geldt dezelfde volgorde, eindigend met pitmaster@example.invalid). De datum komt uit GIT_COMMITTER_DATE, vervolgens GIT_AUTHOR_DATE en daarna de huidige tijd. In tegenstelling tot commits leest stash de constanten PITMASTER_* niet.
Reflog-opslag
Stash-vermeldingen worden opgeslagen in .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
De reflog wordt in chronologische volgorde gelezen. De stash-lijst keert die volgorde om (nieuwste eerst). De stash-reflog wordt geschreven ongeacht core.logallrefupdates.
Gebruik van 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
Het bericht is het argument na push (of save); -m wordt niet ondersteund. pop, apply en drop werken altijd op stash@{0}; de CLI heeft geen --include-untracked.
Beperkingen
- Alleen een volledige stash van de werkdirectory (geen
--keep-index); niet-bijgehouden bestanden alleen viapush($message, includeUntracked: true) - Geen modus
--patch(interactieve selectie van hunk-wijzigingen) - Pop/apply herstelt de status van de werkdirectory; de index wordt niet hersteld. Gestagede bewerkingen komen terug als niet-gestagede bewerkingen en nieuw gestagede bestanden komen terug als niet-bijgehouden bestanden
stash@{n}wordt niet geaccepteerd doorRepository::resolve()- Stash in een gekoppelde worktree gebruikt de verkeerde configuratie en reflog-locatie (#14)