$ sudo teach IT
Модуль 4 · Ветки · Урок 4.4

Git stash

Вы научитесь временно убирать незаконченные правки, переключаться по срочному делу и возвращать правки на место.

Теория + практика~20 минутНовичокgit stashpop и apply

Когда нужен stash

Git stash — это «полка» для незаконченных правок: команда git stash убирает изменения из рабочей папки, чтобы можно было переключиться на другую ветку, а потом вернуть их обратно. Вы спрячете недоделанные стили подвала, срочно поправите главную в main и вернёте правки на место.

Вы уже встречали ошибку would be overwritten: Git не пускает в другую ветку, пока в папке есть незакоммиченные правки. Коммитить половину работы не хочется, а терять её — тем более.

Простыми словами

Вы месите тесто, и тут звонок в дверь. Тесто — в холодильник, руки помыли, открыли. Вернулись — достали тесто и продолжили. git stash — такой холодильник для правок.

Ситуация

Создайте ветку feature/footer-style и добавьте в конец style.css стиль подвала: отступ сверху и тонкую линию.

CSS · style.css (в конец файла)
footer {
  margin-top: 40px;
  border-top: 1px solid #ddd;
}
Терминал
$ git switch -c feature/footer-style
Switched to a new branch 'feature/footer-style'
$ git add style.css
$ git commit -m "Добавить стили подвала"
[feature/footer-style 2bc48f1] Добавить стили подвала
 1 file changed, 4 insertions(+)

Продолжаете работу: добавляете в тот же блок цвет и размер текста, но ещё не закоммитили:

CSS · style.css (блок footer)
footer {
  margin-top: 40px;
  border-top: 1px solid #ddd;
  color: #666;
  font-size: 0.9rem;
}

И тут срочное дело: в разделе «Обо мне» на главной надо дописать фразу, и сделать это нужно в main. Пробуем перейти:

Терминал
$ git switch main
error: Your local changes to the following files would be overwritten by checkout:
	style.css
Please commit your changes or stash them before you switch branches.
Aborting

Разбор по строкам

  • would be overwritten by checkout: style.css — в main файл style.css другой (там нет блока footer), и переход затёр бы вашу правку.
  • Please commit your changes or stash them — Git сам предлагает два выхода: закоммитить или спрятать (stash).

git stash push -m

Прячем правки с понятной подписью. Команда git stash push (stash — «припрятать») берёт все незакоммиченные изменения отслеживаемых файлов, кладёт их в отдельную запись и возвращает файлы к последнему коммиту. Флаг -m — подпись, как у коммита:

Терминал
$ git stash push -m "Цвет и размер подвала"
Saved working directory and index state On feature/footer-style: Цвет и размер подвала
$ git status
On branch feature/footer-style
nothing to commit, working tree clean
$ git stash list
stash@{0}: On feature/footer-style: Цвет и размер подвала

Разбор по строкам

  • Saved working directory and index state — «сохранил состояние рабочей папки и индекса»: правки и то, что было в индексе.
  • On feature/footer-style: Цвет и размер подвала — на какой ветке сделана запись и ваша подпись.
  • nothing to commit, working tree clean — правок в папке больше нет: style.css снова как в коммите 2bc48f1.
  • stash@{0} — номер записи. Записи лежат стопкой: 0 — самая свежая. Спрячете ещё что-то — новая станет stash@{0}, а эта сдвинется на stash@{1}.

Без подписи и в старых статьях

Просто git stash делает то же, что git stash push, но в списке появится WIP on feature/footer-style: 2bc48f1 Добавить стили подвала — это подпись последнего коммита, а не ваших правок: через неделю не вспомните, что там. Подписывайте через -m. В старых статьях встречается git stash save "подпись" — эта форма устарела, вместо неё git stash push -m.

Теперь папка чистая, и Git пускает в main. Делаем срочную правку: в index.html в разделе «Обо мне» дописываем вторую фразу.

Терминал
$ git switch main
Switched to branch 'main'
$ git diff
...
-    <p>Учусь программировать и делаю свой первый сайт.</p>
+    <p>Учусь программировать и делаю свой первый сайт. Историю версий веду в Git.</p>
...
$ git add index.html
$ git commit -m "Дополнить раздел «Обо мне»"
[main 5ef5a2f] Дополнить раздел «Обо мне»
 1 file changed, 1 insertion(+), 1 deletion(-)

Вернуть: pop и apply

Срочное сделано. Возвращаемся в ветку и достаём правки с полки командой git stash pop:

Терминал
$ git switch feature/footer-style
Switched to branch 'feature/footer-style'
$ git stash pop
On branch feature/footer-style
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	modified:   style.css

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (279acbaba33ac1f216770618c25e2d7ec10a6f3f)
$ git stash list
$

Разбор по строкам

  • Сначала Git показывает git status: modified: style.css — правки снова в рабочей папке.
  • Dropped refs/stash@{0} (279acba...) — запись удалена с полки: правки вернулись, хранить её больше незачем.
  • Пустой git stash list — полка пуста.

Проверьте git diff: в нём ваши две строки color и font-size. Доделайте работу и закоммитьте «Доделать стили подвала».

Кроме pop есть apply: он тоже возвращает правки, но запись на полке оставляет. Это удобно, если одну и ту же заготовку нужно применить в двух ветках. Ненужную запись удаляют git stash drop:

КомандаПравки в папкуЗапись на полке
git stash popвозвращаетудаляется
git stash applyвозвращаетостаётся
git stash dropне возвращаетудаляется

drop — без возврата

git stash drop удаляет запись вместе с правками, и вернуть их обычными командами не получится. Прежде чем удалять, посмотрите, что в записи: git stash show stash@{0} покажет файлы и число изменённых строк.

Конфликт при pop

Если пока правки лежали на полке, те же строки поменялись в ветке, pop закончится конфликтом. Например, вы спрятали белый фон, а потом закоммитили серый:

Терминал
$ git stash pop
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
...
The stash entry is kept in case you need it again.
CSS · style.css (фрагмент с конфликтом)
<<<<<<< Updated upstream
  background: #f0f0f0;
=======
  background: #ffffff;
>>>>>>> Stashed changes

Разбор по строкам

  • <<<<<<< Updated upstream — сверху то, что сейчас в ветке (из последнего коммита).
  • >>>>>>> Stashed changes — снизу то, что было на полке.
  • The stash entry is kept in case you need it again. — «запись оставлена на случай, если понадобится снова»: при конфликте pop её не удаляет.

Решается так же, как конфликт слияния: оставьте нужную строку, удалите маркеры и выполните git add style.css. Запись больше не нужна — удалите её git stash drop.

Неотслеживаемые файлы

По умолчанию stash берёт только правки файлов, которые Git уже отслеживает. Новый файл, который вы ни разу не добавляли, останется в папке. Проверим на пробу (на main с чистым статусом, например после практики): создайте пустой черновик draft.html и допишите любую строку в style.css. Короткий статус git status --short печатает по строке на файл: M — изменён, ?? — неотслеживаемый.

Терминал
$ git status --short
 M style.css
?? draft.html
$ git stash push -m "Черновик"
Saved working directory and index state On main: Черновик
$ git status --short
?? draft.html

Разбор по строкам

  • После git stash правка style.css ушла на полку, а ?? draft.html остался: новый файл stash не тронул.

Чтобы забрать и новые файлы, добавьте флаг -u (--include-untracked, «включая неотслеживаемые»). Верните прошлую запись (git stash pop) и спрячьте всё разом:

Терминал
$ git stash push -u -m "Черновик со страницей"
Saved working directory and index state On main: Черновик со страницей
$ git status --short
$

Разбор по строкам

  • Пустой git status --short — в папке не осталось ни правок, ни нового файла: всё на полке.
  • Файлы из .gitignore (например, ваша папка notes/) stash не трогает и с -u.

Опыт закончен — приберитесь: git stash pop вернёт обе правки, затем удалите draft.html и отмените строку в стилях: git restore style.css.

Подробно — в документации git stash.

Альтернатива: коммит-черновик

Stash хорош на несколько минут. Если уходите надолго, честнее закоммитить недоделку прямо в своей ветке: git commit -m "Черновик стилей подвала". Ветка ваша, никто не пострадает, а коммит не потеряется и виден в git log. Вернувшись, доделайте работу и поправьте этот коммит через git commit --amend — им вы уже исправляли последний коммит. Записи stash, наоборот, легко забыть: в git log их не видно.

Практика у себя

Шаг 1. На main с чистым статусом создайте feature/footer-style, добавьте блок footer с двумя свойствами и закоммитьте «Добавить стили подвала».

Шаг 2. Допишите в блок color: #666; и font-size: 0.9rem;, не коммитьте. Попробуйте git switch main — увидите would be overwritten.

Шаг 3. git stash push -m "Цвет и размер подвала", затем git stash list — должна быть одна запись stash@{0}.

Шаг 4. git switch main, допишите в «Обо мне» фразу «Историю версий веду в Git.» и закоммитьте «Дополнить раздел «Обо мне»».

Шаг 5. git switch feature/footer-style, git stash pop — увидите Dropped refs/stash@{0}. Закоммитьте «Доделать стили подвала».

Шаг 6. Слейте ветку: git switch main, git merge feature/footer-style (сохраните сообщение в редакторе), git branch -d feature/footer-style. История:

Терминал
$ git log --oneline --graph
*   8f03c75 (HEAD -> main) Merge branch 'feature/footer-style'
|\
| * 7aeb0d8 Доделать стили подвала
| * 2bc48f1 Добавить стили подвала
* | 5ef5a2f Дополнить раздел «Обо мне»
|/
*   9a283db Merge branch 'feature/subtitle'
...

Проверьте себя

  • git stash list ничего не выводит: полка пуста.
  • В истории есть Merge branch 'feature/footer-style', а под ним — два коммита стилей и «Дополнить раздел «Обо мне»».
  • В браузере подвал серый, чуть мельче основного текста и отделён линией.
  • git status: nothing to commit, working tree clean.

Частые ошибки

Забытые записи. Спрятали, ушли, забыли — через неделю правки «пропали». Загляните в git stash list: они там. Посмотреть запись — git stash show stash@{1}, вернуть — git stash pop stash@{1}. Лучшая профилактика — подпись через -m и привычка не держать на полке ничего дольше дня.

pop не в той ветке. Правки применяются туда, где вы стоите, а не туда, где их прятали. Перед pop смотрите первую строку git status. Ошиблись — правки ещё не закоммичены: снова git stash push -m, перейдите в нужную ветку и pop там.

Новый файл не спрятался. Без -u stash не берёт неотслеживаемые файлы, и draft.html поедет за вами в другую ветку. Нужен git stash push -u -m "...".

No local changes to save. Прятать нечего: правок нет или они только в игнорируемых файлах. Это не ошибка.

Что важно запомнить

  • git stash push -m "подпись" убирает незакоммиченные правки на полку, папка становится чистой.
  • git stash list — что лежит на полке; stash@{0} — самая свежая запись.
  • pop — вернуть и удалить запись, apply — вернуть и оставить, drop — удалить без возврата.
  • Конфликт при pop решается как конфликт слияния, а запись после него остаётся — удалите её drop.
  • Новые файлы — только с -u. Надолго — лучше коммит-черновик в своей ветке.

Теперь у вас весь набор для работы с ветками: создать, слить, решить конфликт, отложить правки. В практикуме модуля соберёте всё вместе — сами проведёте две ветки к конфликту и решите его.

На следующий урок