git stash сохраняет незакоммиченные изменения в локальную запись и убирает сохранённые правки из рабочего каталога. Это удобно, когда нужно прервать работу над корзиной, исправить другую задачу, а потом вернуться к незавершённому коду.
Для первого восстановления используй git stash apply: запись останется в списке, пока ты не проверишь результат. git stash pop удаляет запись после успешного применения. При конфликте pop оставляет её на месте.
Что сохраняет stash: tracked, untracked и ignored
По умолчанию git stash push забирает изменения отслеживаемых файлов: как добавленные в индекс через git add, так и оставшиеся только в рабочем каталоге. Новые неотслеживаемые файлы требуют -u, а игнорируемые — -a. Флаги и разницу apply/pop описывает справка git-stash.
| Состояние файла | stash push | stash push -u | stash push -a |
|---|---|---|---|
| Отслеживаемый файл изменён или удалён | Сохраняет | Сохраняет | Сохраняет |
Новый файл после git add | Сохраняет | Сохраняет | Сохраняет |
| Новый неотслеживаемый файл | Оставляет на месте | Сохраняет | Сохраняет |
Неотслеживаемый файл под правилом .gitignore | Оставляет на месте | Оставляет на месте | Сохраняет |
Не добавляй -a автоматически: под игнорирование могут попадать зависимости, сборка и локальные настройки. При сохранении они исчезнут из рабочего каталога. В примере ниже используем -u, чтобы забрать новый файл с заметками и оставить журнал debug.log.
Если файл уже отслеживается, запись в .gitignore не переводит его в последнюю строку таблицы. Это отдельный случай из статьи как работает .gitignore.
Подготовка учебного репозитория
Выполняй команды последовательно в одном терминале. Первый блок создаёт отдельный временный каталог; работающий проект для упражнения не нужен. Команды рассчитаны на Bash в Linux или Git Bash.
stash_demo=$(mktemp -d)
cd "$stash_demo"
git init -b main
git config user.name 'Handbook Demo'
git config user.email 'demo@example.com'
git config commit.gpgsign false
printf '*.log\n' > .gitignore
printf 'items=1\n' > cart.txt
git add .gitignore cart.txt
git commit -m 'Начальная корзина'
git switch -c feature/cart
printf 'items=2\n' > cart.txt
git add cart.txt
printf 'items=3\n' > cart.txt
printf 'Проверить скидку\n' > notes.txt
printf 'local debug\n' > debug.log
git status --short --ignored
Состояние файлов:
MM cart.txt
?? notes.txt
!! debug.log
В первой колонке MM показана правка индекса относительно коммита: в индексе items=2. Во второй — правка рабочего файла относительно индекса: на диске items=3. ?? отмечает неотслеживаемый файл, !! — игнорируемый. Последняя строка видна благодаря --ignored. Формат короткого git status.
Сохранить, посмотреть и переключиться
git stash push -u -m 'Корзина: количество и заметка о скидке'
git stash list
git stash show --include-untracked --stat 'stash@{0}'
git stash show --include-untracked -p 'stash@{0}'
cat cart.txt
git status --short --ignored
В list появится запись с твоим сообщением. show --stat перечислит изменённые файлы, show -p покажет сами правки. Флаг --include-untracked добавляет в показ содержимое сохранённого notes.txt; без него при стандартных настройках видны только изменения отслеживаемых файлов.
В рабочем cart.txt снова items=1, notes.txt отсутствует, а короткий статус с --ignored показывает только !! debug.log.
stash@{0} — самая свежая запись, stash@{1} — предыдущая. После добавления или удаления записи номера сдвигаются. Перед восстановлением старой работы сверяй сообщение и содержимое через list и show. Кавычки вокруг 'stash@{0}' защищают фигурные скобки от обработки оболочкой.
Теперь можно сделать отдельную правку:
git switch main
printf 'Исправление доставки\n' > hotfix.txt
git add hotfix.txt
git commit -m 'Исправить описание доставки'
git switch feature/cart
Срочная правка осталась в main, рабочая ветка снова открыта. Stash доступен во всём репозитории: имя ветки в описании не запрещает применить запись в другой ветке. Поэтому перед apply проверь текущую ветку через git branch --show-current.
apply возвращает правки и оставляет запись
--index восстанавливает и подготовленную часть правок. Применение через apply не удаляет сохранённую запись.Вернуть изменения: apply, pop и —index
В учебном репозитории восстанови запись так:
git status --short
git stash apply --index 'stash@{0}'
git status --short
git diff --cached -- cart.txt
git diff -- cart.txt
git stash list
Перед apply короткий статус пуст. После него снова видны MM cart.txt и ?? notes.txt: в индекс вернулось items=2, в рабочий файл — items=3. Запись по-прежнему есть в list.
Без --index Git по умолчанию не восстанавливает прежнее разделение на staged и unstaged: содержимое возвращается, но подготовленную часть придётся снова добавить. Флаг пытается восстановить индекс; при конфликте это может не получиться. В Git 2.55 настройка stash.index=true включает такой режим и без явного флага. Описание —index и stash.index.
| Команда | Возвращает изменения | Удаляет запись |
|---|---|---|
git stash apply 'stash@{0}' | Да | Нет |
git stash pop 'stash@{0}' | Да | После успешного применения |
git stash drop 'stash@{0}' | Нет | Да |
Не запускай pop сразу после успешного apply: это повторная попытка применить те же изменения. После apply проверь файлы и тесты своей задачи, затем удали проверенную запись отдельным drop. В нашем примере закончим восстановленную правку коммитом:
git add cart.txt notes.txt
git commit -m 'Сохранить количество товаров и заметку'
git stash drop 'stash@{0}'
git stash list
Список теперь пуст. drop не удалил восстановленные файлы и не отменил коммит; он убрал отложенную запись.
Конфликт при git stash pop
Stash хранит правку относительно прежнего состояния. Если после сохранения тот же участок изменился, при возврате Git может потребовать ручного решения. Воспроизведём это в том же временном репозитории:
printf 'items=4\n' > cart.txt
git stash push -m 'Корзина: четыре товара'
printf 'items=5\n' > cart.txt
git add cart.txt
git commit -m 'Корзина: пять товаров'
git stash pop 'stash@{0}'
git status --short
git stash list
Команда pop завершится с ошибкой конфликта, статус покажет UU cart.txt. Список stash всё ещё содержит запись «Корзина: четыре товара». В файле встретятся оба варианта: пять товаров из текущего коммита и четыре из stash.
Git не может выбрать правильное количество. Для упражнения принимаем решение оставить четыре товара, целиком записываем итоговый файл и фиксируем решение:
printf 'items=4\n' > cart.txt
git add cart.txt
git diff --cached -- cart.txt
git commit -m 'Применить отложенное количество товаров'
git stash drop 'stash@{0}'
test "$(cat cart.txt)" = 'items=4'
test -z "$(git stash list)"
Две команды test проверяют итоговое количество и пустой список stash; при успехе они ничего не печатают и завершаются с кодом 0. В реальном файле отредактируй конфликтующие участки и проверь итог до коммита. У stash нет команды --continue: после разрешения конфликта работа продолжается обычными add и commit. Подробности маркеров и проверок вынесены в разбор конфликтов Git.
Повторять pop на неразрешённом конфликте не нужно. Если передумал применять запись, отдельной команды git stash --abort тоже нет. Сначала сохрани новые ручные правки, если они нужны, и разбери состояние файлов; общий reset --hard способен удалить работу, которой в stash нет.
Почему stash не заменяет коммит
Обычный git push ветки не отправляет её stash-записи. Для работы, которую нужно сохранить надолго или передать коллеге, создай коммит в рабочей ветке и отправь эту ветку. Stash оставь для короткого переключения контекста. Сам механизм и варианты частичного сохранения разобраны в Pro Git.
git stash clear удаляет весь список. Не используй его как завершение упражнения с одной записью: после удаления обычное восстановление уже недоступно, а поиск недостижимых объектов не гарантирует успех.
В следующий раз перед откладыванием работы посмотри git status --short --ignored: по нему видно, нужен ли -u и какие файлы останутся на месте. Различие между рабочим каталогом, индексом и коммитом можно повторить в основных командах Git.