Git stash: описание команды, что делает - Академия Selectel

Руководство по git stash

Инструкция о том, как откладывать изменения, сделанные в рабочей копии, чтобы можно было применить их позже.

Введение

В процессе написания кода может возникнуть ситуация, когда нужно срочно переключиться на другую ветку (использовать git checkout) — или, например, внести правки, которые относятся к другой задаче. Однако новые изменения еще не готовы к тому, чтобы их коммитить и добавлять в репозиторий, — при этом терять их нельзя. В таком случае, как и во многих других, полезной окажется команда git stash.

Откладывание кода

Git stash

git stash

Git stash перемещает текущие изменения (так называемые local changes) в локальную директорию, которая выполняет роль специального хранилища, то есть скрывает эти изменения, сохраняя их отдельно, с опцией вернуть позже, когда это понадобится. Таким образом, файлы рабочей копии возвращаются к своему исходному состоянию. Внесенные изменения помещаются в стек, после чего их можно легко оттуда извлечь. Важно отметить, что в рабочей копии исчезнут все измененные файлы, — независимо от того, добавлены они в индекс или нет.

После выполнения этой строки все несохраненные изменения будут сохранены, но не закоммичены. Эти отложенные участки будут сохранены в локальном репозитории и при выполнении команды git push не будут переданы на сервер. Такие изменения иногда называют «прятанья».

Git stash save

Команда git stash save исполняет ту же функцию, но имеет другие опции.

Git stash save

Когда необходимо добавить комментарий, мы прописываем его вместе с основной командой:

git stash save "<message>"

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

git stash save --include-untracked

Или просто:

git stash save -u

Git stash push

Сейчас команда git stash save считается неактуальной, ее более полезная замена — git stash push. Вызвано это прежде всего тем, что, выполняя git stash push, можно сохранять определенные спецификации пути. Это, к сожалению, не поддерживается git stash save. Git stash save все еще работает, но в будущем следует ожидать постепенного перехода на git stash push из-за доступности нового функционала.

Применение отложенных изменений

Теперь мы знаем, как «спрятать» ненужные в текущий момент изменения, но не знаем, как их извлечь и вернуть в рабочую версию проекта (или, еще можно сказать, сделать «git unstash»). Для решения такой проблемы понадобится git stash pop:

git stash pop

После выполнения в хранилище очищаются изменения и возвращаются к рабочей копии. Если же необходимо, чтобы изменения остались (то есть не удалились из хранилища), но при этом вернулись в проект, мы применим git stash apply:

git stash apply

Этим полезно пользоваться, когда одни и те же части кода нужны для корректной работы нескольких веток — или в том случае, если необходимо удалить ветку, в которой изначально были сделаны эти изменения. Тогда можно автоматически перенести желаемые участки кода.

git branch --delete <name of branch>

Необычное откладывание

Еще одну вариацию использования git stash составляет флаг —keep-index:

git stash --keep-index

Git не просто спрячет изменения, уже добавленные в индекс, но и оставит эти «прятанья» в индексе.

Откладывание неотслеживаемых или игнорируемых файлов

При работе с git stash нельзя забывать, что простое выполнение этой команды не дает скрывать изменения в некоторых типах файлов, то есть не убирает в хранилище появившиеся правки кода. К таким типам относятся неотслеживаемые и игнорируемые.

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

Проиндексированными называются файлы, о чьих преобразованиях уже знает Git, но само изменение еще не записано в репозиторий.

Поэтому логично, что новые файлы не смогут быть отложены, если они все еще не проиндексированы. Точно так же, как и игнорируемые (помеченные явным образом как файлы, которые необходимо игнорировать).

При необходимости спрятать неотслеживаемые файлы поможет уже известный параметр -u (или же -include-untracked):

git stash -u

Перенос правок для игнорируемых файлов осуществляется с помощью флага -all:

git stash -all

Или короче:

git stash -a

Управление несколькими наборами отложенных изменений

При повторном использовании git stash мы можем понять, что фактически мы создаем коммит, но сохраняем его по отдельности. Используя следующее выражение, мы можем посмотреть на список всех спрятанных изменений:

git stash list

Полученный список будет отсортирован по времени, когда были отложены изменения: внизу списка будут самые старые изменения. Также каждое изменение имеет свой идентификатор с номером, например:

stash@{0}: WIP on master: 08f13aa add processing file
stash@{1}: WIP on master: c33b610 refactor code

Описание «прятаний» по умолчанию начинается с WIP (что указывает на незавершенную работу) перед названием ветки (в частности коммита), где их отложили.

После выполнения git stash pop будет возвращен последний из наборов откладываний (с индексом 0). Если нужно выбрать конкретный набор из всех ранее отложенных участков, то в фигурных скобках нужно явно прописать его идентификатор. Например:

git stash pop stash@{1}

Просмотр различий между наборами отложенных изменений

Команда git stash show выводит информацию по последнему сделанному изменению:

git stash show

Если нужно посмотреть информацию о другом изменении, то необходимо явно указать индекс:

git stash show stash@{1}

Очень полезной функцией может стать сравнение полученных наборов друг с другом. Для такой ситуации понадобится параметр —patch:

git stash show --patch

Или что будет равносильно:

git stash show -p

Частичное откладывание изменений

Иногда бывает удобно разделить существующие в коде незакоммиченные изменения на несколько откладываний. Тогда в разных откладываниях могут храниться изменения в разных файлах или даже в одном. Тогда необходимо написать флаг -p (или —patch):

git stash -p

Теперь откладывание для каждого из участков будет выполняться отдельно, при этом запрашивая подтверждение:

  • ? — узнать все возможные варианты для работы (ниже будут перечислены самые распространенные);
  • y — отложить этот участок;
  • / — использовать для поиска регулярное выражение;
  • n — не откладывать этот участок кода;
  • q — отложить все выбранные участки и выйти;
  • s — разделить текущее изменение на несколько откладываний.

Прервать работу откладывания поможет комбинация клавиш CTRL+C.

Создание ветки из отложенных изменений

При работе с разными ветками и откладыванием изменений (или другими операциями) могут возникнуть конфликты. Одной из возможностью избежать этого и ничего не сломать будет создание новой ветки и применение изменений в ней:

git stash branch <name of new branch>

Теперь будет создана новая ветка, к которой уже применятся сделанные изменения. Однако последние правки будут удалены из списка всех спрятанных изменений. Как и в предыдущих случаях, если нужно работать не с последним набором, то можно указать индекс.

Удаление отложенных изменений

Теперь нам нужно научиться удалять отложенные изменения:

git stash drop

Так мы удалим последний набор. Аналогично другим командам для удаления определенного набора указываем индекс:

git stash drop stash@{1}

Иногда нужно очищать хранилище от всех наборов, то есть отменять все созданные изменения. В таких случаях мы пишем:

git stash clear

Разрешение конфликтов

При работе с возвращением в исходный код спрятанных изменений могут возникнуть конфликты. Если это произошло, то после выполнения git stash pop появится сообщение о возникновении конфликта.

Чтобы посмотреть файлы, в которых возникли конфликты, нужно воспользоваться git status. Они будут находиться в секции Unmerged paths. Также все новые изменения отслеживаемых и проиндексированных файлов будут находиться в разделе Changes to be committed.

Разрешить такой конфликт необходимо вручную. Однако в отличие от конфликтов, например, при слиянии веток (выполнение git merge), нет потребности сразу коммитить изменения, так как код еще не закончен и не готов для полноценного коммита. А после этого можно добавить его в индекс и сделать привычный коммит.

Можно сделать вывод, что конфликты при работе git stash pop допустимо рассматривать в качестве незафиксированных изменений.

Принцип работы команды git stash

Разберемся, как устроена работа команды и получим более подробное git stash-описание.

Мы уже знаем, что наборы откладываний на самом деле являются коммитами (из этого следует, что команда git log корректно отработает, выводя изменения). Ссылка в .git/refs/stash ведет на последнюю группу откладываний. Чтобы найти более ранние наборы, существует журнал ссылок, который так и называется — stash. Поэтому если нужно просмотреть «прятанья», мы пишем stash@{n}, то есть обращаемся к записи с номером n.

В зависимости от откладываемых элементов git stash создает от двух до трех коммитов. Конкретно рассмотрим, какие это будут коммиты.

  • В коммите stash@{0} будут содержаться файлы в исходном коде на момент запуска команды.
  • Первым предком stash@{0} будет являться уже существующий коммит в той ветке, куда ведет HEAD.
  • Второй предок stash@{0} — новый коммит, который является индексом.
  • Третий коммит будет создан только в том случае, если в проекте содержатся неотслеживаемые Git-файлы или были указаны параметры -u или -a. Это будет коммит с файлами, которые содержатся в исходном коде и не видны Git’у.

Теперь поподробнее разберем, как git stash зашифровывает рабочий каталог и работает с проиндексированными файлами.

  • Выше мы разбирали, что каталог может содержать исправления в разных типах файлов. Такие файлы могут быть только частично отмечены в репозитории.
  • Git stash кодирует преобразования в отслеживаемых участках под видом ориентированного ациклического графа (это можно проверить, если нарисовать дерево, описанное выше). Целью одного коммита будет хранение неиндексированных, а второго — уже индексированных изменений, которые были отмечены как проиндексированные файлы.
  • Помним, что флаг -u зашифровывает все правки неотслеживаемых файлов в виде другого коммита, в то время как параметр -a, наоборот, включает изменения в игнорируемых файлах в тот же (не создавая при этом дополнительный коммит).

Осталось разобраться, что происходит, когда мы реализуем git stash pop. Спрятанные части кода из всех перечисленных выше пунктов возвращаются сразу и в рабочую копию, и, что важно, также к разделу проиндексированных файлов Git. После этого коммит, который мы извлекли, будет удален из stash. Остальные ссылки в журнале просто сдвигаются (чтобы обеспечить далее корректную работу). Коммиты, которые мы извлекли, не очищаются сразу, но отмечаются как мусор и впоследствии удаляются.

Заключение

Команда git stash открывает новые возможности перед разработчиками в написании кода. С помощью откладываемых изменений можно удобно и оперативно работать в нескольких ветках, не рискуя потерять важные участки кода. Если использовать специальные команды и параметры к ним, можно эффективно управлять сделанными отложенными изменениями. Все рассматриваемые операции применимы также и к таким типам файлов, как неотслеживаемые и игнорируемые, что позволяет комфортно работать со всем разрабатываемым проектом в целом.

Конечно, даже такой обширный функционал не может полностью обезопасить от конфликтов или ошибок. Но если научиться умело пользоваться git stash, можно избежать некоторых сложностей в процессе разработки мобильных приложений, веб-приложений и других продуктов.