Урок курса

Срочная задача, а текущая ещё не закончена

Git и GitHub с нуля: бесплатный курс с практикой

Переключение на другую ветку с незакоммиченными правками — ситуация, которую мы уже встречали раньше: Git либо не даёт переключиться, либо переносит изменения в другую ветку, смешивая чужую работу. Stash — инструмент, который решает именно это: убрать незавершённую правку с дороги, не делая коммит раньше времени, и вернуть её когда нужно.

Stash как временное хранилище вне истории коммитов

Stash хранит незавершённые правки отдельно от обычной истории ветки: создание записи не продвигает main, и обычный git log текущей ветки её не показывает. При этом правки сохраняются относительно исходного HEAD, связь с базовым состоянием есть. Это локальная временная копия, а не опубликованный коммит работы.

Когда вы выполняете git stash push, Git берёт текущие tracked-изменения — то есть изменения уже отслеживаемых файлов в рабочей папке и в индексе — и упаковывает их во внутреннюю запись stash@{0}. Отслеживаемую часть рабочей папки и индекс Git возвращает к состоянию последнего коммита (HEAD). Если до stash у вас была только правка в уже отслеживаемом файле, то после git stash push git status покажет чистое рабочее дерево.

Однако на диске могут остаться файлы, которые Git не отслеживает — untracked и ignored. По умолчанию stash их не трогает: они лежат там, где лежали. Поэтому git status после stash не обязан быть полностью пустым, если в репозитории есть untracked-файлы.

Важно понимать три категории:

  • Tracked-изменения (изменения файлов, которые уже есть в индексе) — сохраняются при обычном git stash push.
  • Untracked-файлы (рабочие файлы, сейчас отсутствующие в индексе и не скрытые правилами игнорирования) — по умолчанию остаются на диске.
  • Ignored-файлы (неотслеживаемые пути, скрытые правилами игнорирования) — остаются на диске в обеих формах, рассматриваемых здесь: обычном git stash push и git stash push -u.

Это не правило для всех возможных опций stash.

Stash не привязан к ветке: запись можно применить в другой ветке. Но это не значит, что применение пройдёт без проблем — содержимое могло разойтись с текущим состоянием файлов, и тогда возникнет конфликт.

git stash push и git stash apply: полный цикл на примере README.md

Для отдельного опыта распакуйте весь git_practice.zip в новую пустую папку. Откройте терминал в её git_practice либо перейдите туда через cd "полный путь к новой папке/git_practice" со своим путём. Папки my_repo ещё нет. Создайте клон и настройте оба поля автора:

git clone remote/shared.git my_repo
cd my_repo
git config user.name "Student"
git config user.email "student@example.com"
git status
git log --oneline

Исходно чистая main, один коммит, README с заголовком «# Командный справочник», контактом «команда» и статусом «черновик». Других изменений, untracked-файлов и stash-записей в этом новом клоне нет. Запишите исходный ID вне клона. Не выполняйте git init.

В редакторе добавьте после исходного текста пустую строку и Автор: в процессе, сохраните файл с прежними окончаниями строк. Не делайте add или commit этой правки: работа ещё не закончена.

Шаг 1. Убедиться, что изменение есть

git status

Git покажет README.md как изменённый (modified). Коммитить рано — работа не закончена.

Шаг 2. Убрать правку во временное хранилище

git stash push

Git выведет что-то вроде Saved working directory and index state WIP on main: .... После этого:

git status

Покажет nothing to commit, working tree clean. Правка в README.md убрана — Git вернул файл к HEAD.

Шаг 3. Переключиться и сделать срочную правку

git switch -c hotfix

На hotfix README снова исходный. В редакторе добавьте в его конец пустую строку и Статус: исправлено, сохраняя исходные строки. Зафиксируйте срочное исправление и вернитесь:

git add README.md
git commit -m "Срочное исправление"
git status
git log --oneline
git switch main

hotfix теперь содержит отдельный новый коммит; main остаётся на исходном ID. Не объединяйте hotfix в main в этом опыте, не меняйте main и не создавайте других stash-записей до apply. После возврата README исходный и status чист. Команда возврата:

git switch main

Шаг 4. Вернуть припрятанную правку

git stash apply

Git восстанавливает изменения в рабочую папку. В данном опыте возвращается одна unstaged-строка Автор: в процессе; main остаётся на исходном коммите. В общем случае apply без дополнительных флагов не обещает восстановить прежнее распределение правок между индексом и рабочей папкой: проверяйте оба diff. Важно: apply не удаляет запись stash@{0} из хранилища — она остаётся там даже после успешного применения.

Шаг 5. Проверить результат

git status
git diff
git diff --staged

Откройте README.md и убедитесь, что добавленная строка («Автор: в процессе» в примере с учебным репозиторием) вернулась, неожиданных изменений нет, конфликтных маркеров нет. Успешное завершение apply ещё не означает, что содержимое точно то, что нужно — status и diff дают фактическую картину.

Шаг 6. Удалить запись, если она больше не нужна

git stash drop

Удаляет верхнюю запись stash@{0}. Выполнять это стоит только после того, как вы убедились в корректности результата. Если сомневаетесь или хотите сохранить резервную копию — запись можно оставить: её наличие не мешает работе.

Проверить, что история ветки main не получила лишних коммитов:

git log --oneline

Обычный log main по-прежнему содержит только исходный коммит. Срочный коммит остался в hotfix; временная правка README восстановлена как незакоммиченная. Запись stash хранится отдельно, а drop не коммитит возвращённую работу.

Флаг -u: три категории файлов и граница ignored

По умолчанию git stash push работает только с tracked-файлами. Что происходит с остальными — зависит от категории.

Tracked — файл уже есть в индексе (когда-то был добавлен через git add и попал в коммит или остался в индексе после add). Его изменения всегда включаются в stash при обычном git stash push. Обратите внимание: отслеживание определяется текущим состоянием индекса, а не тем, когда-то ли файл добавлялся. Если файл есть в индексе сейчас — он tracked.

Untracked — рабочий файл сейчас отсутствует в индексе и не скрыт ignore. Он мог раньше отслеживаться, затем быть снят с отслеживания и остаться на диске; прежний add не определяет его нынешнюю категорию. По умолчанию git stash push его игнорирует и оставляет на диске. Чтобы включить такие файлы в stash, нужен флаг -u:

git stash push -u

После этого untracked-файлы (которые не скрыты .gitignore) тоже попадут в хранилище и исчезнут с диска вместе с tracked-изменениями.

Ignored — файлы, которые соответствуют шаблонам .gitignore и не отслеживаются. Эти файлы не включаются в stash ни при обычном push, ни при push -u. В обеих рассматриваемых формах они остаются на диске.

Компактно разница выглядит так:

Категория Без флагов С -u
Tracked (изменения) ✓ сохраняется ✓ сохраняется
Untracked (не в .gitignore) остаётся на диске ✓ сохраняется
Ignored (в .gitignore) остаётся на диске остаётся на диске

Отдельный случай: если файл уже отслеживается (есть в индексе), но его путь совпадает с шаблоном .gitignore — он всё равно tracked. Правило игнорирования не прекращает отслеживание. Изменения такого файла попадут в stash при обычном push.

Конфликт при stash apply: запись сохраняется, разрешение — вручную

Если с момента git stash push ветка получила новые коммиты, которые изменили те же строки, что лежат в stash-записи, git stash apply может завершиться конфликтом. Механизм точно такой же, как при слиянии веток: Git расставляет маркеры прямо в файле.

Для отдельного конфликтного опыта создайте новый клон из новой распаковки ровно как в начале предыдущей секции, с обеими настройками автора и чистой main. Вместо добавления автора измените исходную строку на Статус: в работе, сохраните окончания строк и выполните git stash push. Не создавайте hotfix: на той же main измените восстановленную исходную строку на Статус: исправлено, затем выполните:

git add README.md
git commit -m "Срочное исправление"
git stash apply
git status

Обе версии меняют одну исходную строку. Apply создаёт конфликт; типичный блок в README:

<<<<<<< Updated upstream
Статус: исправлено
=======
Статус: в работе
>>>>>>> Stashed changes

Соберите нужную итоговую строку, например Статус: исправлено, в работе, и удалите маркеры. Остальные исходные строки сохраните.

Ключевое поведение: при конфликте stash-запись не удаляется автоматически. stash@{0} остаётся в хранилище. Это сделано намеренно — вы не теряете данные даже если apply завершился с ошибками.

Порядок действий при конфликте:

  1. Открыть конфликтующий файл в редакторе.
  2. Убрать маркеры <<<<<<<, =======, >>>>>>> и оставить нужное содержимое.
  3. Добавить исправленный файл в индекс:
   git add README.md

  1. Проверить итоговое состояние:
   git status
   git diff --staged

Убедиться, что маркеров нет, содержимое файла корректно.

  1. Только после подтверждения — удалить запись, если она больше не нужна:
   git stash drop

Если результат вызывает сомнения, запись можно оставить — git stash drop никогда не вызывается автоматически. Хранить запись дольше необходимого не является ошибкой.

Задача 1

В двух независимых копиях одинаковое состояние: tracked README изменён, notes.txt не находится в индексе и не игнорируется, новый output.log не отслеживается и игнорируется. Что включается при stash push и stash push -u?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 2

Apply вернул часть текста, но завершился конфликтом. Запись по-прежнему в stash list. Как обращаться с этой записью?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 3

Самостоятельная практика:
Настоящая локальная практика Git на своём компьютере. Площадка не запускает команды и не проверяет ваш репозиторий.

Входные данные: Распакуйте git_practice.zip в новую папку и перейдите в git_practice. Клонируйте учебный remote:

git clone remote/shared.git my_repo
cd my_repo
git config user.name "Student"
git config user.email "student@example.com"

Каждую команду выполняйте отдельно; можно подставить свои значения автора. Clone не переносит локальные настройки автора, поэтому обе настройки нужны до первого нового коммита.
В репозитории ветка main, один коммит, README.md содержит «# Командный справочник», «Контакт: команда», «Статус: черновик». Исходный status чистый, других файлов и изменений в этом отдельном опыте нет.

Задача:

  1. Добавьте строку «Автор: в процессе» в конец README.md (не коммитьте).
  2. Убедитесь через git status, что файл изменён.
  3. Выполните git stash push. Проверьте через git status, что рабочая папка чистая.
  4. Создайте ветку hotfix и переключитесь на неё (git switch -c hotfix). Добавьте строку «Статус: исправлено» в конец README.md, закоммитьте с сообщением «Срочное исправление».
  5. Вернитесь на main (git switch main).
  6. Выполните git stash apply. Проверьте git status, git diff, git diff --staged и откройте README.md: строка «Автор: в процессе» должна вернуться, неожиданных правок и маркеров быть не должно.
  7. Только после этой проверки, если временная копия больше не нужна, выполните git stash drop. Убедитесь через git log --oneline, что история main не содержит stash-коммита.

Ожидаемый итог: На ветке main README.md содержит добавленную строку как незакоммиченное изменение; история main содержит только исходный коммит. Если после подтверждения результата вы выбрали drop, stash-хранилище пусто; при необходимости сохранить резервную запись её наличие не является ошибкой.

Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.

Контрольный случай:
В отдельном проекте на main исходный коммит содержит README.md без строки «План: проверить макет» и NOTES.md с текстом «Контакт: команда». Исходно status чистый. В README добавили указанную строку, выполнили git stash push и затем обычный git stash apply; применение прошло без конфликта. Далее в NOTES заменили контакт на «Контакт: редактор», выполнили только git add NOTES.md и git commit -m "Обновить контакт". После проверки README удалили запись через git stash drop. Других add или commit не было. Каков полный снимок двух файлов в последнем коммите main и положение строки плана?

Выберите один вариант
Без регистрации · результат не сохраняется

Задача 4

Письменное объяснение:
Коллега говорит: «Зачем мне stash? Если нужно срочно переключиться, я просто сделаю быстрый коммит git commit -m "wip", переключусь, сделаю своё, а потом исправлю коммит через git commit --amend. Stash — лишнее усложнение». Объясните: (1) какое конкретное ограничение возникнет при попытке использовать --amend после возврата на исходную ветку с несколькими коммитами между wip и исправлением; (2) в чём преимущество stash для незавершённой работы, которую намеренно не хочется фиксировать в истории.

Платформа проверяет только ответ ниже; действия в своём репозитории и письменные ответы вы проверяете самостоятельно.

Контрольный случай:
В текущей ветке после собственного WIP уже создали ещё два коммита. Теперь хотят исправить именно старый WIP обычным commit --amend. Подходит ли этот способ?

Выберите один вариант
Без регистрации · результат не сохраняется

Продолжить с сохранением прогресса

Перейдите к уроку в учебном кабинете.

Перейти к интерактивному уроку