Stash
Git stash išsaugo darbinio katalogo ir indekso pakeitimus, kad galėtumėte pereiti prie kitos užduoties ir vėliau juos atkurti. Pitmaster įgyvendina stash naudodama refs/stash ir reflog, pagal git saugyklos formatą. Repository neturi stash metodo; naudokite toliau pateiktą Stash klasę.
Žinomos dvi problemos: kai įrašų daugiau nei vienas, drop() (todėl ir pop()) palieka įrašą sąraše (#10), o apply() perrašo neįrašytus pakeitimus failuose, kurių stash neliečia (#11). Stash taikykite tik švariame darbiniame medyje.
Kaip veikia stash
Kiekvienas stash įrašas yra commit su dviem tėviniais commit:
- Tėvinis commit 0: stash sukūrimo metu buvęs HEAD commit (pradinė būsena)
- Tėvinis commit 1: commit, kuriame užfiksuota indekso būsena (paruošti pakeitimai)
Paties stash commit medis užfiksuoja darbinio medžio būseną (įskaitant neparuoštus pakeitimus).
Stash „dėklas“ yra refs/stash reflog. Naujausias stash yra pats refs/stash. Senesni įrašai pateikiami reflog įrašuose: stash@{0}, stash@{1} ir t. t.
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 objekto kūrimas
use Pitmaster\Stash\Stash;
$stash = new Stash(
$repo->objectDatabase(),
$repo->refDatabase(),
$repo->gitDir(),
$repo->workDir(),
);
Push (išsaugoti pakeitimus)
// 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 atlieka šiuos veiksmus:
- Nuskaito dabartinį HEAD commit
- Iš dabartinio indekso sukuria medį (paruošta būsena)
- Sukuria „indekso commit“, kurio tėvinis commit yra HEAD
- Iš dabartinio darbinio katalogo sukuria medį (įskaitant neparuoštus pakeitimus)
- Sukuria stash commit (darbinio medžio medis, tėviniai commit = HEAD + indekso commit)
- Atnaujina
refs/stash, kad rodytų į stash commit - Prideda dėklo reflog įrašą
- Atkuria darbinį medį ir indeksą į HEAD būseną
Po push sekami failai sutampa su HEAD. Nesekami failai lieka, nebent nustatytas includeUntracked; tada jie pašalinami ir išsaugomi paties stash commit medyje (git juos saugo trečiajame tėviniame commit, todėl git juos laiko sekamais pakeitimais). push() sukuria įrašą net tada, kai niekas nepasikeitė, nors git praneša „No local changes to save“.
Su numatytuoju pranešimu
$stashId = $stash->push();
// Message: "WIP on <branch>: <short hash> <HEAD subject>"
// With a message: "On <branch>: <message>"
Atjungus HEAD, <branch> yra HEAD.
Pop (pritaikyti ir pašalinti)
$stash->pop();
Pritaiko viršutinį stash įrašą darbiniam katalogui ir pašalina jį iš dėklo. Tai atitinka apply(), po kurio vykdomas drop().
Konkretaus įrašo pritaikymas ir pašalinimas
$stash->pop(1); // Pop stash@{1}
Apply (atkurti nepašalinant)
$stash->apply(); // Apply stash@{0}
$stash->apply(2); // Apply stash@{2}
Apply sujungia stash su dabartiniu HEAD failas po failo (stash bazė, dabartinis HEAD, stash medis). Stash įrašas lieka dėkle, o indeksas neliečiamas. Kilus konfliktams, failuose paliekami Updated upstream ir Stashed changes žymekliai ir išmetama Pitmaster\Exceptions\MergeConflictException; pop() tada įrašo nepašalina. Prieš taikant Apply vietiniai pakeitimai netikrinami (žr. pradžioje pateiktą pastabą).
Įrašų sąrašas
$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
}
Kiekviename įraše yra:
index: stash pozicija (0 = naujausias)message: stash pranešimashash: stash commit maiša
Drop (pašalinti nepritaikius)
$stash->drop(); // Drop stash@{0}
$stash->drop(1); // Drop stash@{1}
Pašalinus vienintelį įrašą, pašalinami refs/stash ir jo reflog. Kai įrašų daugiau, drop() šiuo metu tik perkelia refs/stash, o reflog palieka nepakeistą, todėl įrašas lieka listEntries() (#10).
Saugojimo informacija
Stash commit struktūra
commit <hash>
tree <worktree-tree-hash>
parent <HEAD-hash>
parent <index-commit-hash>
author <current identity>
committer <current identity>
<stash message>
Indekso commit struktūra
commit <hash>
tree <index-tree-hash>
parent <HEAD-hash>
author <current identity>
committer <current identity>
index on <branch>: <short hash> <HEAD subject>
Tapatybė nustatoma pagal GIT_COMMITTER_NAME, tada GIT_AUTHOR_NAME, tada user.name saugyklos konfigūracijoje, o galiausiai Pitmaster (el. paštas nustatomas ta pačia tvarka, paskutinis – pitmaster@example.invalid). Data nustatoma pagal GIT_COMMITTER_DATE, tada GIT_AUTHOR_DATE, o galiausiai pagal dabartinį laiką. Skirtingai nei commit, stash neskaito PITMASTER_* konstantų.
Reflog saugykla
Stash įrašai saugomi .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 skaitomas chronologine tvarka. Stash sąraše įrašai rikiuojami atvirkščiai – naujausi pateikiami pirmiausia. Stash reflog įrašomas nepriklausomai nuo core.logallrefupdates.
CLI naudojimas
# 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
Pranešimas yra argumentas po push (arba save); -m nepalaikomas. pop, apply ir drop visada veikia su stash@{0}; CLI neturi --include-untracked.
Apribojimai
- Galimas tik viso darbinio katalogo stash (nėra
--keep-index); nesekami failai įtraukiami tik perpush($message, includeUntracked: true) - Nėra
--patchrežimo (interaktyvaus pakeitimų gabalų pasirinkimo) - Pop/apply atkuria darbinio medžio būseną, bet ne indeksą. Paruošti pakeitimai grįžta kaip neparuošti, o naujai paruošti failai – kaip nesekami
stash@{n}nepriimaRepository::resolve()- Stash susietame darbiniame medyje naudoja netinkamą konfigūracijos ir reflog vietą (#14)