Приходить code review, а три файли недоправлені — і ворушитися може лише один. Сховати все в stash і переписувати решту двох — ритуал, за яким ніхто не скучає.
TL;DR: git stash push -m "чому" -- <шлях> ховає рівно вказані шляхи і лишає решту вашого брудного дерева в спокої. Зміни повертаються через git stash pop (або окремий файл із будь-якого stash витягується через git restore --source stash@{0} -- <шлях>). Форма з pathspec прийшла в git 2.13, випущений у травні 2017 — будь-який інструментарій останніх восьми років її має.
git stash push -m "app.conf prod tweak" -- app.conf
git stash list
# stash@{0}: On main: app.conf prod tweak
git stash pop Ховай файл, а не дерево.
Як сховати в stash лише один файл у git?
Голий git stash вимітає все робоче дерево — кожна відстежена зміна йде в stash, усе повертається до HEAD. Це неправильна форма, коли брудне дерево змішане: один файл готовий до передачі, решта чесно недороблена. Відповідь — дієслово push з pathspec, за документацією git-stash:
# до: три брудні файли
$ git status --short
M app.conf
M notes.md
M main.py
# сховати лише app.conf
$ git stash push -m "app.conf prod tweak" -- app.conf
Saved working directory and index state On main: app.conf prod tweak
$ git status --short
M notes.md
M main.py app.conf повернувся до закоміченого стану і осів у stash@{0}; notes.md і main.py не рушили. Повідомлення -m необов’язкове, але варте своїх трьох секунд, щойно git stash list покаже більше одного запису — stash під ім’ям «WIP» старіють погано.
Два деталі, що мають значення:
- Pathspec іде після
--. Усе після подвійного тире — це шляхи, а не прапорці. Та сама конвенція, що йgit checkout -- <шлях>, і вона не декоративна: у старіших версіяхgit stash push -- main.pyіgit stash push main.pyвідрізнялися — голий шлях могли зчитати неправильно. - Невідстежувані файли вимагають
-u. У новонародженого файлу немає відстеженої історії, яку можна сховати, тож звичайнийpushйого пропускає.git stash push -u -- newfile.pyйого включає;-aіде далі й вимітає ще й ігноровані.
Як сховати частину одного файлу?
Коли сам файл напівготовий — три гарні hunks, один сороміцький — інтерактивний потік його розрізає:
git stash -p # або: git stash push -p Git проходить кожен hunk і питає Stash this hunk [y,n,q,a,d,j,g,/,e,p,?]?. Відповідайте y для hunk-ів, що йдуть у stash, n для тих, що лишаються. Результат — stash лише зі схваленим, решта файлу лишається брудною в дереві. Та сама hunk-за-hunk-машина крутить і git add -p, тож рефлекс переноситься напряму.
git stash push -- <шлях> | git stash -p | |
|---|---|---|
| Зерно | цілі файли | окремі hunks |
| Темп | одна команда, скриптується | інтерактивно, hunk за hunk |
| Повторюваність у CI | так (шляхи як аргументи) | ні — потрібна людина біля промпта |
| Найкраще для | «цей файл, не решта» | «ця зміна, не та» |
| З | git 2.13 (травень 2017) | доступно давно |
Практичне правило з моделі самої документації: pathspec, коли межа — це файл, -p, коли межа всередині нього.
Як повернути stash-нутий файл?
git stash pop відновлює stash@{0} і видаляє запис. Це нормальний шлях — але pop працює за принципом «все або нічого» на stash, і конфлікт перериває pop, лишаючи запис. Дві тонші опції:
# 1. застосувати без видалення (безпечно повторювано)
git stash apply stash@{0}
# 2. витягти ОДИН файл зі stash, запис лишається
git restore --source stash@{0} -- app.conf
# старіший запис тієї самої операції (до 2.23):
git checkout stash@{0} -- app.conf Форма restore/checkout відповідає на випадок «сховав три зміни разом, а потрібна тепер одна» — копіює stash-нутий вміст цього шляху в робоче дерево і лишає stash@{0} на ногах. Зверніть увагу: вона перезаписує копію в робочому дереві; якщо поточні правки на цьому шляху цінні, спершу diff:
git diff stash@{0} -- app.conf Stash-и зберігаються як справжні коміти на стеку, схожому на reflog — тому stash@{0} приймає будь-який commit-ish синтаксис, а випадково скинутий stash можна витягти з reflog, доки його не з’їсть збирач сміття.
Коли stash — неправильний інструмент?
Stash — це чорновик, не гілка: немає імені в git branch, немає review, немає показаного за замовчуванням diff, а записи мовчки громздяться до забуття. Якщо робота в процесі має пережити зміни контексту між машинами чи днями, комітьте її в гілку — прикріплена історія краща за безіменний стек. Коли ті припарковані коміти матимуть сісти в іншому місці, перевезе їх git cherry-pick. А якщо мета — скасувати, а не паркувати, діє дерево рішень із скасувати останній коміт.
Чесна книга поламок — усе з задокументованої поведінки:
| Симптом | Причина | Фікс |
|---|---|---|
| Файлу немає після stash | невідстежувані шляхи пропускаються | git stash push -u -- <шлях> |
error: Your local changes ... would be overwritten на pop | шлях змінився від часів stash | закомітьте або сховайте новий стан, потім pop |
| Повернуті зміни зникли | pop наткнувся на конфлікт і перервався | розʼяжіть, потім git stash apply |
| «Котрий це був stash?» | безіменна купа stash-ів | повідомлення -m, щоразу |
Травнева примітка 2017 року, що додала pathspec до git stash push, має вісім років, і більшість мʼязової памʼяті git stash старіша за неї. Варто перевчитись: вимітання цілого дерева — сьогодні окремий випадок, не типовий режим.
FAQ
Як сховати в stash лише один файл у git?
git stash push -m "нотатка" -- шлях/до/файлу — форма з pathspec ховає лише вказані шляхи і не чіпає решту змінених файлів. Потрібен git 2.13 або новіший (травень 2017).
Як витягти окремий файл зі stash?
git restore --source stash@{0} -- шлях/до/файлу (або старіший запис git checkout stash@{0} -- шлях/до/файлу). Копіює stash-нуту версію у ваше робоче дерево, не видаляючи запис stash.
Чому git stash проігнорував мій новий файл?
Невідстежувані (untracked) файли не входять до звичайного stash. Додайте -u: git stash push -u -- шлях/до/файлу. Ігноровані файли вимагають -a.
— mrsaynothing
— mrsaynothing
Польові нотатки про ШІ, Linux і self-hosting.
Наступний гайд — на email
Один лист на пост. Полагодили — і далі.
що це таке?«Польові нотатки» #1 із сайту агентів: машина випускає. Я затверджую.
Читається добре? Таке я будую за гроші. найміть мене