Запис stash - це звичайні коміти, на які вказує ref refs/stash, а список stash@{n} - це reflog цього ref-а.
Структура одного запису:
.----W ← робочий каталог (сам запис stash@{0})
/ /|
H----I | H - HEAD на момент stash, I - стан індексу
U U - невідстежувані файли (лише з -u чи -a)
- W - merge-коміт з батьками
H,Iі, якщо був-u, -U; - окремий коміт для індексу дозволяє
git stash apply --indexвідновити, що саме було в індексі.
git cat-file -p stash@{0}
# tree ...
# parent 84ba035... ← H
# parent 48c2a34... ← I
# parent 84d625d... ← U (untracked files on main)
git show stash@{0}^3 # невідстежувані файли зі stash
Що з цього випливає:
git stash dropі успішнийpopвидаляють лише запис у reflogrefs/stash. Самі коміти лишаються в базі об'єктів, доки їх не прибереgit gc;- до stash можна звертатися як до будь-якого коміту:
git diff stash@{0}^1 stash@{0},git restore --source=stash@{0} -- file.php.
Відновлення видаленого stash:
Якщо щойно виконали pop чи drop, Git друкує хеш:
Dropped refs/stash@{0} (5c3e8a7f...)
git stash apply 5c3e8a7f
Якщо хеш загублено - шукати недосяжні коміти-сироти:
git fsck --no-reflog --unreachable | awk '/commit/ {print $3}' \
| xargs git log --no-walk --merges --format='%h %ci %s' | grep 'WIP on\|On '
Коміти stash - merge-коміти з повідомленнями «WIP on main» чи «On main: опис». Знайдений хеш - git stash apply <хеш> чи git branch rescue <хеш>.
Обмеження: після git gc (Git запускає його й автоматично) недосяжні об'єкти старші за gc.pruneExpire (за замовчуванням 2 тижні) видаляються остаточно.
git stash branch name stash@{1} - корисний для старих записів: створює гілку від коміту H, застосовує stash і видаляє запис, тож конфліктів з новим кодом не буде.