Resetting, checking out & reverting
The git reset, git checkout, and git revert commands are some of the most useful tools in your Git toolbox. They all let you undo some kind of change in your repository, and the first two commands can be used to manipulate either commits or individual files.
Поскольку команды очень похожи, легко запутаться, какую из них использовать в конкретном сценарии разработки. В этой статье мы сравним наиболее распространенные конфигурации команд git reset
, git checkout
и git revert
. Надеемся, что вы научитесь уверенно перемещаться по репозиторию с помощью любой из них.
It helps to think about each command in terms of their effect on the three state management mechanisms of a Git repository: the working directory, the staged snapshot, and the commit history. These components are sometimes known as "The three trees" of Git. We explore the three trees in depth on the git reset page. Keep these mechanisms in mind as you read through this article.
Переключение версий (команда checkout) перемещает указатель HEAD
на указанный коммит. Покажем это на следующем примере.
Связанные материалы
Перемещение полного репозитория Git
СМ. РЕШЕНИЕ
Изучите Git с помощью Bitbucket Cloud
В этом примере показана последовательность коммитов в ветке main
. Сейчас и указатель HEAD
, и указатель на главную ветку main
указывают на коммит d. Теперь давайте выполним команду git checkout b
Это обновление дерева «истории коммитов». Команду git checkout
можно использовать на уровне коммита или файла. При переключении на уровне файла его содержимое будет отражать состояние при конкретном коммите.
Операция отмены (команда revert) принимает в качестве аргумента коммит и создает новый коммит, изменения в котором будут противоположны указанному. git revert
действует только на уровне коммита и не работает на уровне файлов.
Операция сброса (команда reset) принимает в качестве аргумента коммит и сбрасывает «три дерева» до состояния репозитория при указанном коммите. Ее можно выполнить в трех разных режимах, соответствующих трем деревьям.
Команды checkout и reset обычно используются для локальных или частных отмен. Эти команды изменяют историю репозитория, что может вызвать конфликты при отправке в удаленные общие репозитории. Команда revert считается безопасной операцией для публичных отмен, поскольку добавляет в историю новые данные, которыми можно делиться удаленно, а не перезаписывает старые, от которых могут зависеть участники команды.
Git reset vs revert vs checkout reference
В таблице ниже приведены наиболее распространенные сценарии использования всех этих команд. Обязательно держите ее под рукой для справки, поскольку вам, несомненно, придется использовать хотя бы некоторые из этих сценариев при работе с Git.
Команда | Область действия | Примеры использования |
---|---|---|
| Область действия На уровне коммита | Примеры использования Discard commits in a private branch or throw away uncommitted changes |
| Область действия На уровне файла | Примеры использования Удаление файла из индекса |
| Область действия На уровне коммита | Примеры использования Переключение между ветками или проверка старых снимков состояния |
| Область действия На уровне файла | Примеры использования Отмена изменений в рабочем каталоге |
| Область действия На уровне коммита | Примеры использования Отмена коммитов в публичной ветке |
| Область действия На уровне файла | Примеры использования (Н/Д) |
Commit level operations
Параметры, которые вы передаете командам git reset
и git checkout
, определяют область их действия. Если в качестве параметра не указан путь к файлу, они работают с коммитами целиком. Данный раздел посвящен работе на этом уровне. Обратите внимание, что git revert
не имеет аналога на уровне файла.
Reset a specific commit
На уровне коммитов команда reset позволяет перенести конец ветки на другой коммит. Таким образом можно удалить коммиты из текущей ветки. Например, следующая команда перемещает ветку hotfix
на два коммита назад.
git checkout hotfix git reset HEAD~2
Два коммита, которые располагались в конце ветки hotfix
, стали коммитами без ссылок или без родителя. Это означает, что при следующей сборке мусора Git удалит их. Другими словами, вы говорите, что хотите выбросить эти коммиты. Выглядит это следующим образом:
Команда git reset
— это простой способ отменить изменения, которые еще никто не получал. Она прекрасно подойдет в случае, когда вы начали работать над функцией и вдруг подумали: «Ой, ну что я делаю? Лучше начать все сначала».
Помимо перемещения текущей ветки, с помощью команды git reset
можно также изменить снимок состояния индекса и (или) рабочий каталог, передав ей один из следующих флагов:
--soft
— снимок состояния индекса и рабочий каталог никак не изменяются.--mixed
— снимок состояния индекса обновляется в соответствии с указанным коммитом, а рабочий каталог остается неизменным. Это вариант по умолчанию.--hard
— снимок состояния индекса и рабочий каталог обновляются в соответствии с указанным коммитом.
It’s easier to think of these modes as defining the scope of a git reset
operation. For further detailed information visit the git reset page.
Переключение на старые коммиты
Команду git checkout
используют, чтобы обновлять состояние репозитория до определенной точки в истории проектов. Когда ей передают имя ветки, она позволяет переключиться на нее.
git checkout hotfix
Технически команда выше просто устанавливает указатель HEAD
на другую ветку и соответствующим образом обновляет рабочий каталог. Поскольку при этом можно перезаписать локальные изменения, Git требует сделать коммит изменений в рабочем каталоге, которые будут потеряны при переключении, или отложить их. В отличие от git reset
, команда git checkout
не перемещает ветки.
Кроме того, можно переключиться на конкретный коммит, передав в качестве аргумента ссылку на коммит, а не ветку. При этом происходит то же самое, что и при переключении на ветку: команда перемещает ссылку HEAD
на указанный коммит. Например, следующая команда переключит версию на два уровня до текущего коммита:
git checkout HEAD~2
Это полезно, когда нужно быстро изучить старую версию проекта. Однако, поскольку ссылка на текущий указатель HEAD
отсутствует, это переводит систему в состояние с открепленным указателем HEAD
. Это может быть опасно, если вы начнете добавлять новые коммиты, потому что после перехода на другую ветку у вас не будет возможности вернуться к ним. Поэтому перед добавлением коммитов с открепленным указателем HEAD
всегда следует создавать новую ветку.
Undo public commits with revert
При использовании команды revert коммит отменяется путем создания нового коммита. Это безопасный способ отменить изменения, поскольку при нем невозможно перезаписать историю коммитов. Например, следующая команда определит изменения, содержащиеся во втором с конца коммите, создаст новый коммит, в котором эти изменения будут отменены, и добавит его к проекту.
git checkout hotfix git revert HEAD~2
Выглядит это следующим образом:
Сравните это с командой git reset
, которая меняет существующую историю коммитов. Поэтому команду git revert
лучше использовать для отмены изменений в публичной ветке, а git reset
следует оставить для сброса изменений в частной.
Или можно рассматривать git revert
как инструмент для отмены изменений, попавших в коммиты, в то время как команда git reset HEAD
предназначена для отмены изменений, не попавших в коммиты.
Как и git checkout
, команда git revert
может перезаписать файлы в рабочем каталоге и поэтому предложит отложить изменения, которые будут потеряны при отмене.
File-level operations
Команды git reset
и git checkout
также могут принимать путь к файлу в качестве необязательного параметра. Это резко меняет их поведение. Вместо того чтобы действовать на снимки состояния целиком, их работа будет ограничена одним файлом.
Git reset a specific file
При вызове с указанием пути к файлу команда 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 file
Переключение на уровне файла аналогично использованию команды 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
, но действует только на указанный файл.
Резюме
You should now have all the tools you could ever need to undo changes in a Git repository. The git reset, git checkout, and git revert commands can be confusing, but when you think about their effects on the working directory, staged snapshot, and commit history, it should be easier to discern which command fits the development task at hand.
Поделитесь этой статьей
Следующая тема
Рекомендуемые статьи
Добавьте эти ресурсы в закладки, чтобы изучить типы команд DevOps или получать регулярные обновления по DevOps в Atlassian.