Как отменить или откатить коммит в Git командами reset, revert и restore
Всем привет!
Если вы работаете с Git относительно недавно, почти наверняка у вас возникала потребность в отмене коммита. И, что печально, вы быстро поняли, что простого «откатить все назад» нет.
Где-то советуют git reset, где-то — git revert, а в третьих и вовсе пишется про checkout. По тому же правилу летят restore, amend, reflog и куча технологических формулировок, требующих знания git и после которых становится только сложнее, нежели понятнее.
На самом деле все это объясняется: под словами «отменить commit» можно иметь в виду совершенно разные действия:
-
Убрать последний commit;
-
Убрать commit, но оставить изменения в файлах;
-
Отменить commit, который уже отправили в удаленный репозиторий;
-
Вернуть один конкретный файл;
-
и т.д.
Именно это и вызывает необходимость в немалом количестве различных команд, которые вам и советуют в интернете.
В этой статье моя главная цель — разобрать все это нормально и по-человечески, без лишней каши из терминов, после которых новичок сразу закрывает статью. Прежде чем начать самую интересную для читателей, кликнувшим по заголовку, части, я хочу кратко разобрать малую часть терминологии.
Если это все вам не интересно и вы просто зашли скопипастить команду — можете смело листать вниз.
Итак, что мы разберем:
-
Что такое коммит и что вообще значит «отменить commit» в Git;
-
Когда нужно использовать какую команду;
-
Как убрать последний commit без потери кода;
-
Как безопасно отменить уже отправленный commit;
-
Как вернуть только один файл;
Немного теории
Commit в Git — что это?
Простыми словами, коммит — это просто сохраненная точка проекта в определенный момент времени. Здесь возникает очень удобная аналогия с сохранениями в игре: в определенный момент появляется надобность в сохранении прогресса. Также и с Git: сохраняется текущий вариант файлов в репозитории.
Почему «откатить коммит» часто только путает
Как я уже говорил выше, под этой фразой можно подразумевать очень разные вещи. Иногда нужно убрать только коммит, но сохранить состояние кода. Когда-то нужно убрать и коммит, и код, а когда-то — вообще не трогать старый коммит, а только создать новый, который перекроет старый.
Поэтому в Git нет одной конкретной команды, зато есть множество команд, закрывающие каждую из потребностей.
Малая часть терминологии
Дальше, чтобы не путаться, достаточно знать несколько вещей:
-
Локальный репозиторий — то есть проект, который лежит у вас на компьютере;
-
Удаленный репозиторий — например репозиторий в GitHub, GitLab или в другом git-сервисе, куда вы отправляете коммиты через
git push; -
Коммит — сохраненная точка проекта.
-
Ветка — это линия развития проекта. Вы обычно работаете в основной ветке —
mainилиmaster, но при желании можно создать другие. Например:devдля ветки с версиями кода, находящегося на разработке илиprodдля ветки с кодом, готового к публикации/запуска. -
HEAD — это указатель на текущий коммит, с которым вы сейчас работаете. Обычно он указывает на последний коммит текущей ветки.
Репозиторий — это папка, директория проекта, в которой Git хранит историю изменений.
git push— это команда, которая отправляет ваши коммиты (сохраненные точки проекта) в удаленный репозиторий.
С чего лучше начать запоминание
Если хочется максимально короткую версию без деталий, то:
-
git reset— когда нужно убрать последний commit локально (при этом изменения можно либо оставить, либо удалить). Здесь я все-таки рекомендую пролистать и прочитать про эту команду и ее использование, т.к. ей можно легко сломать историю; -
git revert— когда нужно создать новый commit, который отменяет изменения старого commit (отменить commit, который уже отправлен); -
git restore— когда нужно вернуть файл; -
git commit --amend— когда нужно перекрыть старый коммит новым (исправить).
Теперь давайте разберем каждый случай отдельно.
Применения каждой из команд
1. Убрать последний commit локально — git reset
Если вы сделали commit, но поняли, что он лишний или ошибочный, можно убрать его с помощью git reset.
Есть несколько вариаций reset:
-
--mixed(по умолчанию)
git reset HEAD~1
Здесь commit убирается из истории ветки, изменения остаются в файлах, но они удаляются из git add (git add нужно будет делать заново).
Что за «HEAD~1»? Тут все очень просто: ~1 значит «один коммит назад». Можно указать 2, 3, 4 и так далее.
-
--soft
git reset --soft HEAD~1
Здесь похоже: commit удаляется из истории текущей ветки, изменения остаются, но файлы не удаляются из git add. Соответственно, добавлять их туда заново не нужно.
-
--hard
git reset --hard HEAD~1
Это уже опасная команда: commit удаляется из истории вместе со всеми изменениями в файлах. То есть репозиторий полностью возвращается к состоянию предыдущего коммита.
Если вы случайно (бывает и такое) выполнили эту команду, но не хотели удалять коммит, то можно его восстановить с помощью команды
git reflog, которую мы разберем чуть ниже.
2. Отменить commit, который уже был отправлен — git revert
Если commit уже был отправлен в удалённый репозиторий, использовать reset плохо — это может сломать историю у других разработчиков (если они есть)
В таком случае используют:
git revert <айди коммита>
Например:
git revert a1b2c3d
Тут git оставляет старый commit, но создает новый, который отменяет его изменения. То есть история фактически не переписывается.
Пример на буковках, где каждая буковка — айди коммита:
A - B - C — текущий коммит, где C — ошибочный коммит, который нужно перекрыть.
Если выполнить git revert C, получится:
A - B - C - D, где D — commit, который отменяет изменения C.
Это самый рекомендуемый и безопасный способ отмены изменений, если код уже был отправлен.
3. Вернуть один файл — git restore
Иногда нужно отменить изменения не во всем коммите, а только в одном файле.
Например, вы что-то сломали в index.js. Тогда можно вернуть файл к состоянию последнего коммита:
git restore index.js
Git просто заменит файл на версию из последнего коммита.
Помимо возврата из последнего коммита, можно вернуть его из конкретного коммита:
git restore --source=<айди коммита> index.js
или
git restore --source=HEAD~1 index.js
4. Исправить последний commit — git commit —amend
Иногда commit уже сделан, но вы осознаете, что по каким-то причинам вы забыли добавить файл, написали плохой комментарий к коммиту или нужно слегка поправить код, то можно перезаписать последний commit.
Для этого используется комманда:
git commit --amend
Git откроет редактор, в котором можно изменить комментарий к коммиту. После изменений введите :wq или :qa и жмите Enter.
Если нужно добавить файлы в этот коммит, то сначала нужно:
git add файл
а потом:
git commit --amend
Git пересоберёт последний commit так, будто вы его сделали заново.
При этом я считаю важным подчеркнуть, что:
-
amend меняет историю
-
его желательно использовать только для таких коммитов, которые еще не отправлены
5. Восстановление коммита после —hard — git reflog
Git ведёт журнал перемещений HEAD, что позволяет найти hash коммита и использовать его для восстановления.
Можно воспользоваться командой:
git reflog
Она покажет hash коммита в виде:
a1c2b3d (HEAD -> master) HEAD@{0}: commit (initial): Initial Commit
После того можно вернуть коммит:
git reset --hard a1c2b3d
6. Отмена git add — git restore —staged <имя файла>
Статья хоть и про коммиты, но я считаю, что про это тоже важно написать, хоть тут и писать нечего:
git restore --staged <имя файла>
Это отменит git add для конкретного файла.
Итог
Надеюсь, что статья была вам действительно полезна и вы узнали что-то новое. Если у вас остались вопросы или вы увидели в статье неточности — смело пишите в комментарии.
Автор: MarkovM

