Stash
Git stash tallentaa työhakemiston ja indeksin muutokset, jotta voit siirtyä toiseen tehtävään ja palauttaa muutokset myöhemmin. Pitmaster toteuttaa stashin refs/stash- ja reflog-rakenteilla gitin tallennusmuodossa. Repository-luokassa ei ole stash-metodia; käytä alla olevaa Stash-luokkaa.
Tunnettuja ongelmia on kaksi: jos merkintöjä on useampi kuin yksi, drop() (ja siksi myös pop()) jättää merkinnän luetteloon (#10), ja apply() korvaa keskeneräiset muutokset tiedostoissa, joita stash ei koske (#11). Käytä stasheja vain puhtaassa työhakemistossa.
Stashin toiminta
Jokainen stash-merkintä on commit, jolla on kaksi vanhempaa:
- Vanhempi 0: HEAD-commit stashin luontihetkellä (pohjatila)
- Vanhempi 1: indeksin tilan tallentava commit (vaiheistetut muutokset)
Stash-commitin oma puu tallentaa työhakemiston tilan (myös vaiheistamattomat muutokset).
Stash-"pino" on kohteen refs/stash reflog. Uusin stash on itse refs/stash. Vanhemmat merkinnät ovat reflog-merkintöjä: stash@{0}, stash@{1} jne.
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
Stash-olion luominen
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (tallenna muutokset)
// 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 tekee seuraavaa:
- Lukee nykyisen HEAD-commitin
- Luo puun nykyisestä indeksistä (vaiheistettu tila)
- Luo "index commitin", jonka vanhempi on HEAD
- Luo puun nykyisestä työhakemistosta (myös vaiheistamattomat muutokset)
- Luo stash-commitin (työpuu, vanhempina HEAD ja index commit)
- Päivittää kohteen
refs/stashosoittamaan stash-commitiin - Lisää pinolle reflog-merkinnän
- Palauttaa työhakemiston ja indeksin HEAD-tilaan
Pushin jälkeen seuratut tiedostot vastaavat HEAD-tilaa. Seuraamattomat tiedostot säilyvät, ellei includeUntracked ole asetettu; silloin ne poistetaan ja tallennetaan stash-commitin omaan puuhun (git tallentaa ne sen sijaan kolmantena vanhempana, jolloin git näkee ne seurattuina muutoksina). push() luo merkinnän myös silloin, kun mitään ei ole muuttunut; git ilmoittaa tällöin "No local changes to save".
Oletusviestillä
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Kun HEAD on irrotettu, <branch> on HEAD.
Pop (käytä ja poista)
$stash->pop();
Käyttää pinon ylimmän stash-merkinnän työhakemistossa ja poistaa sen pinosta. Vastaa toimintoja apply() ja sitten drop().
Tietyn merkinnän pop
$stash->pop(1); // Pop stash@{1}
Apply (palauta poistamatta)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Apply yhdistää stashin nykyiseen HEAD-tiedostoon tiedosto kerrallaan (stashin pohja, nykyinen HEAD, stash-puu). Stash-merkintä säilyy pinossa, eikä indeksiin kosketa. Ristiriitatapauksissa tiedostoihin jää merkinnät Updated upstream ja Stashed changes, ja toiminto aiheuttaa Pitmaster\Exceptions\MergeConflictException-poikkeuksen; pop() säilyttää tällöin merkinnän. Apply ei tarkista ensin paikallisia muutoksia (katso alussa oleva huomautus).
Merkintöjen luettelo
$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
}
Jokainen merkintä sisältää:
index: stash-sijainti (0 = uusin)message: stash-viestihash: stash-commitin tiiviste
Drop (poista käyttämättä)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Jos poistetaan ainoa merkintä, refs/stash ja sen reflog poistetaan. Jos merkintöjä on useita, drop() siirtää tällä hetkellä vain kohdetta refs/stash eikä muuta reflogia, joten merkintä säilyy kohteessa listEntries() (#10).
Tallennuksen yksityiskohdat
Stash-commitin rakenne
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Index commitin rakenne
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Identiteetti tulee ensin kohteesta GIT_COMMITTER_NAME, sitten kohteesta GIT_AUTHOR_NAME, sitten arkiston asetuksesta user.name ja lopuksi kohteesta Pitmaster (sähköpostiosoitteessa sama järjestys, ja viimeisenä pitmaster@example.invalid). Päivämäärä tulee kohteesta GIT_COMMITTER_DATE, sitten kohteesta GIT_AUTHOR_DATE ja lopuksi nykyhetkestä. Toisin kuin commitit, stash ei lue PITMASTER_*-vakioita.
Reflog-tallennus
Stash-merkinnät tallennetaan kohteeseen .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
Reflog luetaan aikajärjestyksessä. Stash-luettelo kääntää järjestyksen (uusin ensin). Stash-reflog kirjoitetaan core.logallrefupdates-asetuksesta riippumatta.
CLI-käyttö
# 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
Viesti on argumentti kohteen push (tai save) jälkeen; -m ei ole tuettu. pop, apply ja drop toimivat aina kohteessa stash@{0}; kohteella CLI ei ole --include-untracked-toimintoa.
Rajoitukset
- Vain koko työhakemiston stash (ei
--keep-index); seuraamattomat tiedostot vain kohteenpush($message, includeUntracked: true)kautta - Ei
--patch-tilaa (vuorovaikutteista muutosten osittaista valintaa) - Pop/apply palauttaa työhakemiston tilan, mutta ei indeksiä. Vaiheistetut muokkaukset palaavat vaiheistamattomina, ja vasta lisätyt tiedostot palaavat seuraamattomina
stash@{n}ei ole hyväksytty kohteessaRepository::resolve()- Linkitetyssä työhakemistossa tehty stash käyttää vääriä asetuksia ja reflog-sijaintia (#14)