Система управления версиями git очень популярна среди разработчиков программного обеспечения. Она очень сильно облегчает работу в команде, разворачивание проектов на сервере, ну и конечно же управление историей изменений. Изменения в Git сохраняются порциями - коммитами. Каждый коммит содержит набор изменений, к которым в любой момент можно вернуться, и продолжить разработку с этого момента или же просто посмотреть что и когда было изменено.
Иногда случаются ситуации, что вы закомитили что-то не то, не туда или не так. Такой коммит надо удалить или отменить. В этой небольшой статье мы рассмотрим как отменить коммит Git. Обратите внимание, что если вам надо внести изменения, то коммит не обязательно отменять, можно его поправить. Но об этом в следующей статье.
Содержание статьи
Как отменить коммит в Git
Бывает, что вы что-то закомитили, а потом решили, что часть изменений вносить не надо было или в коммит попали лишние файлы. Тогда можно отменить коммит и сделать его уже заново, но без ненужных данных. Сначала желательно посмотреть историю коммитов и текущий коммит, для того чтобы не откатить ничего лишнего. Для этого выполните:
git log
Команда вернет список коммитов с их описанием и идентификаторами (хешами), которые можно использовать для того чтобы посмотреть подробную информацию о коммите с помощью команды show. По умолчанию команда показывает изменения в последнем коммите:
git show
Теперь можно использовать идентификатор коммита для того чтобы его отменить.
1. Отменить коммит, но оставить изменения
Для того чтобы отменить последний коммит git без удаления изменений используется команда reset с параметром --soft. Команде надо передать идентификатор коммита или его позицию относительно HEAD. В терминологии git термин HEAD - это самая последняя версия проекта в текущей ветке. С помощью HEAD можно ссылаться на коммиты в истории. Для этого используется символ ~. Таким образом ссылка на предыдущий коммит будет выглядеть как HEAD~ или HEAD~1, а на коммит перед ним - HEAD~2 и так далее. Для отмены последнего коммита достаточно выполнить команду:
git reset --soft HEAD~
Как видите, все файлы сохранились, а если посмотреть отличия HEAD и текущего состояния проекта, то будет видно, добавление файла file3:
git diff HEAD
Аналогичного результата можно добиться, передав идентификатор коммита, например, давайте отменим коммит, добавляющий file2. Для этого посмотрите идентификатор коммита перед ним, в данном случае, это "Inital Commit" с помощью следующей команды:
git log
А затем передайте его в команду git reset. Например:
git reset --soft 887080eea5fd8bd3bc2503dcf043ac6f5c19a8e5
И снова все файлы на месте, а в HEAD теперь будет добавлено два файла: file2 и file3:
Таким образом вы можете отменить несколько коммитов за раз, надо только указать идентификатор самого раннего коммита.
Обратите внимание, что файлы, которые ранее были в коммите, сейчас всё ещё добавлены в индекс, поэтому вам не надо вызывать git add, можно сразу создавать новый коммит. Но у команды reset есть ещё одна опция: --mixed. Она используется по умолчанию. При использовании этой опции ваши изменения тоже сохраняются, но перед следующим коммитом их снова надо будет добавить в индекс с помощью git add. При выполнении команды git status эти файлы будут отображаться как не отслеживаемые:
git status
2. Отменить коммит и удалить изменения
Отмена коммита git с удалением изменений работает аналогично. Только здесь необходимо вместо опции --soft указывать опцию --hard. Например, при той же структуре коммитов, можно удалить последний коммит с добавлением файла file3 вместе с этим файлом:
git reset --hard HEAD~
Теперь файла нет. Аналогично, вы можете указать идентификатор коммита, до которого надо отменить коммиты. Обратите внимание, что указывается не тот коммит, который надо отменить, а коммит перед ним. Ещё важно отметить, что это всё работает пока вы не отправили свои коммиты в удалённый репозиторий. Если коммиты уже отправлены, их идентификаторы сохранены там, а поэтому менять их нельзя, иначе могут возникнуть конфликты слияния, которые будет сложно решить. Теперь вы знаете отменить последний локальный коммит git.
3. Как вернуть отмененный коммит
Если вы всё же удалили что-то нужное с помощью команды reset --hard, и вовремя об этом вспомнили, то можно попытаться вернуть потерянные данные. Первый способ будет работать если вы ещё ничего не комитили после отмены комита. Для того чтобы посмотреть историю добавления/удаления коммитов используйте команду:
git reflog
Затем, для того чтобы вернуться к нужному удалённому коммиту надо использовать ту же команду reset --hard со ссылкой на удалённый коммит, полученной из предыдущей команды, в виде HEAD{@номер}. Например, для коммита c файлом file3 это будет выглядеть так:
git reset --hard HEAD@{1}
Аналогично можно использовать адрес:
git reset --hard fc1f295
Срок хранения удалённых коммитов ограничен. Время от времени git удаляет мусор, так что если ждать слишком долго, то нужных данных уже может и не быть. Но найти удалённые коммиты, если git их ещё не удалил можно с помощью такой команды:
git fsck --lost-found
Затем просто используйте идентификатор коммита для того чтобы посмотреть какие в нём были изменения:
git show 8a996dd76fbacb05a2df91c0f2d19b1a3afd8451
Затем можно переключиться на этот коммит с помощью команды:
git rebase 8a996dd76fbacb05a2df91c0f2d19b1a3afd8451
Это всё тоже безопасно делать только с коммитами, ещё не отправленными в удалённый репозиторий.
4. Отменить изменения но не отменять коммит
Если вы уже отправили коммит в удалённый репозиторий, удалять его не желательно, потому что если кто-то успеет скачать репозиторий до отмены коммита, то потом у него возникнут проблемы. Однако изменения, сделанные в коммите можно отменить новым коммитом. Для того чтобы не делать это вручную существует специальная команда revert. Её уже нужно передать идентификатор именно того коммита, изменения из которого надо отменить. Для этого сначала найдите хэш коммита:
git log
Затем выполните команду revert, например:
git revert 8a996dd76fbacb05a2df91c0f2d19b1a3afd8451
Команда предложит вам написать сообщение для отменяющего коммита, можно просто закрыть этот файл:
Затем изменения, которые были в коммите исчезнут и уже это можно будет снова пушить в удалённые репозиторий.
Выводы
В этой небольшой статье мы рассмотрели как отменить коммит git с сохранением изменений или без них. Как видите всё довольно просто. Будьте осторожны, и не сотрите ничего лишнего, чтобы не создать проблем коллегам и себе.
Для возврата на отменённый коммит можно истользовать ссылку ORIG_HEAD
git reset --hard ORIG_HEAD
Вот хороший скринкаст по гит:
https://www.youtube.com/watch?v=DMncFUqzDuM&list=PLDyvV36pndZFHXjXuwA_NywNrVQO0aQqb&index=23
git reset --soft 887080eea5fd8bd3bc2503dcf043ac6f5c19a8e5
Судя по логу, это отменяет все комиты после идентификатора. А не один.
А если нужно отменить только один?