Команды reset, checkout и revert | Учебные руководства Atlassian по Git

Команды reset, checkout и revert

Команды git reset, git checkout и git revert входят в число наиболее полезных инструментов Git. Все три позволяют отменять изменения в репозитории, причем первые две можно использовать для управления коммитами или отдельными файлами.

Поскольку команды очень похожи, легко запутаться, какую из них использовать в конкретном сценарии разработки. В этой статье мы сравним наиболее распространенные конфигурации команд git reset, git checkout и git revert. Надеемся, что вы научитесь уверенно перемещаться по репозиторию с помощью любой из них.

Три дерева Git

Полезно воспринимать эти команды с точки зрения их влияния на три механизма управления состоянием репозитория Git: рабочий каталог, снимок состояния индекса и историю коммитов. Эти компоненты иногда называют «тремя деревьями» Git. Мы подробно рассматриваем «три дерева» на странице, посвященной git reset. Помните об этих механизмах при чтении данной статьи.

Переключение версий (команда checkout) перемещает указатель HEAD на указанный коммит. Покажем это на следующем примере.

Перемещение указателя HEAD на указанный коммит

В этом примере показана последовательность коммитов в ветке main. Сейчас и указатель HEAD, и указатель на главную ветку main указывают на коммит d. Теперь давайте выполним команду git checkout b

Последовательность коммитов в главной ветке

Это обновление дерева «истории коммитов». Команду git checkout можно использовать на уровне коммита или файла. При переключении на уровне файла его содержимое будет отражать состояние при конкретном коммите.

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

Операция сброса (команда reset) принимает в качестве аргумента коммит и сбрасывает «три дерева» до состояния репозитория при указанном коммите. Ее можно выполнить в трех разных режимах, соответствующих трем деревьям.

Команды checkout и reset обычно используются для локальных или частных отмен. Эти команды изменяют историю репозитория, что может вызвать конфликты при отправке в удаленные общие репозитории. Команда revert считается безопасной операцией для публичных отмен, поскольку добавляет в историю новые данные, которыми можно делиться удаленно, а не перезаписывает старые, от которых могут зависеть участники команды.

Справка по различиям git reset, git revert и git checkout

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

Команда Область действия Примеры использования
git reset На уровне коммита Отмена коммитов в частной ветке или сброс изменений, не попавших в коммиты
git reset На уровне файла Удаление файла из индекса
Git checkout На уровне коммита Переключение между ветками или проверка старых снимков состояния
Git checkout На уровне файла Отмена изменений в рабочем каталоге
git revert На уровне коммита Отмена коммитов в публичной ветке
git revert На уровне файла (Н/Д)

Операции на уровне коммитов

Параметры, которые вы передаете командам git reset и git checkout, определяют область их действия. Если в качестве параметра не указан путь к файлу, они работают с коммитами целиком. Данный раздел посвящен работе на этом уровне. Обратите внимание, что git revert не имеет аналога на уровне файла.

Сброс до конкретного коммита

На уровне коммитов команда reset позволяет перенести конец ветки на другой коммит. Таким образом можно удалить коммиты из текущей ветки. Например, следующая команда перемещает ветку hotfix на два коммита назад.

git checkout hotfix git reset HEAD~2

Два коммита, которые располагались в конце ветки hotfix, стали коммитами без ссылок или без родителя. Это означает, что при следующей сборке мусора Git удалит их. Другими словами, вы говорите, что хотите выбросить эти коммиты. Выглядит это следующим образом:

Сброс ветки исправлений до HEAD-2

Команда git reset — это простой способ отменить изменения, которые еще никто не получал. Она прекрасно подойдет в случае, когда вы начали работать над функцией и вдруг подумали: «Ой, ну что я делаю? Лучше начать все сначала».

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

  • --soft — снимок состояния индекса и рабочий каталог никак не изменяются.
  • --mixed — снимок состояния индекса обновляется в соответствии с указанным коммитом, а рабочий каталог остается неизменным. Это вариант по умолчанию.
  • --hard — снимок состояния индекса и рабочий каталог обновляются в соответствии с указанным коммитом.

Можно сказать, что эти режимы определяют область действия операции git reset. Дополнительную информацию можно найти на странице, посвященной git reset.

Переключение на старые коммиты

Команду git checkout используют, чтобы обновлять состояние репозитория до определенной точки в истории проектов. Когда ей передают имя ветки, она позволяет переключиться на нее.

 git checkout hotfix

Технически команда выше просто устанавливает указатель HEAD на другую ветку и соответствующим образом обновляет рабочий каталог. Поскольку при этом можно перезаписать локальные изменения, Git требует сделать коммит изменений в рабочем каталоге, которые будут потеряны при переключении, или отложить их. В отличие от git reset, команда git checkout не перемещает ветки.

Перемещение указателя HEAD с главной ветки на ветку исправлений

Кроме того, можно переключиться на конкретный коммит, передав в качестве аргумента ссылку на коммит, а не ветку. При этом происходит то же самое, что и при переключении на ветку: команда перемещает ссылку HEAD на указанный коммит. Например, следующая команда переключит версию на два уровня до текущего коммита:

 git checkout HEAD~2
Перемещение указателя HEAD на конкретный коммит

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

Отмена публичных коммитов с помощью команды revert

При использовании команды revert коммит отменяется путем создания нового коммита. Это безопасный способ отменить изменения, поскольку при нем невозможно перезаписать историю коммитов. Например, следующая команда определит изменения, содержащиеся во втором с конца коммите, создаст новый коммит, в котором эти изменения будут отменены, и добавит его к проекту.

git checkout hotfix git revert HEAD~2

Выглядит это следующим образом:

Отмена второго коммита с конца

Сравните это с командой git reset, которая меняет существующую историю коммитов. Поэтому команду git revert лучше использовать для отмены изменений в публичной ветке, а git reset следует оставить для сброса изменений в частной.

Или можно рассматривать git revert как инструмент для отмены изменений, попавших в коммиты, в то время как команда git reset HEAD предназначена для отмены изменений, не попавших в коммиты.

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

Операции на уровне файлов

Команды git reset и git checkout также могут принимать путь к файлу в качестве необязательного параметра. Это резко меняет их поведение. Вместо того чтобы действовать на снимки состояния целиком, их работа будет ограничена одним файлом.

git reset для определенного файла

При вызове с указанием пути к файлу команда git reset обновляет снимок состояния индекса, чтобы он соответствовал версии из указанного коммита. Например, следующая команда получит версию foo.py во втором с конца коммите и занесет ее в индекс для следующего коммита:

git reset HEAD~2 foo.py

Как и в случае с версией git reset на уровне коммита, этот вариант чаще используется с указателем HEAD, а не с конкретным коммитом. Выполнение команды git reset HEAD foo.py удалит файл foo.py из индекса. Содержащиеся в нем изменения по-прежнему будут присутствовать в рабочем каталоге.

Перемещение файла из истории коммитов в снимок состояния индекса

Флаги --soft, --mixed и --hard не влияют на работу команды git reset на уровне файла, поскольку снимок состояния индекса обновляется всегда, а рабочий каталог — никогда.

git checkout для файла

Переключение на уровне файла аналогично использованию команды git reset с указанием пути к файлу, за исключением того, что оно обновляет рабочий каталог, а не индекс. В отличие от версии на уровне коммитов эта команда не перемещает ссылку HEAD, а значит, не переключает ветку.

Перемещение файла из истории коммитов в рабочий каталог

Например, после выполнения следующей команды файл foo.py в рабочем каталоге будет совпадать с его версией из второго коммита с конца:

git checkout HEAD~2 foo.py

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

Если вы выполните индексирование и коммит для файла, на который переключились, это приведет к восстановлению старой версии файла. Обратите внимание, что при этом удаляются все последующие изменения в файле, тогда как команда git revert отменяет только изменения, внесенные указанным коммитом.

Как и git reset, эту команду обычно используют с указателем HEAD в качестве ссылки на коммит. Например, команда git checkout HEAD foo.py отменит неиндексированные изменения в файле foo.py. Это поведение аналогично команде git reset HEAD --hard, но действует только на указанный файл.

Резюме

Теперь вы располагаете всеми инструментами, которые могут понадобиться для отмены изменений в репозитории Git. В командах git reset, git checkout и git revert можно запутаться, но если вы вспомните об их воздействии на рабочий каталог, снимок состояния индекса и историю коммитов, вам будет легче понять, какая из них подойдет для текущей задачи при разработке.