Программирование и IT
Как отменить коммит
Вопрос «как отменить коммит» на деле скрывает четыре разных: поправить последний, снять его и переделать, стереть несколько, отменить давний коммит в середине истории. Решение зависит в первую очередь от одного обстоятельства — видел ли этот коммит кто-нибудь, кроме тебя.
В этой статье
Первый вопрос: коммит уже отправлен?#
Ответ определяет весь дальнейший путь, поэтому начинают с него:
## main...origin/main [ahead 1]
Пометка ahead 1 означает: у тебя на коммит больше, чем на сервере, и этот
коммит никто не видел. Историю можно переписывать свободно. Если пометки нет,
коммит уже ушёл — переписывать нельзя, потому что у коллег останется старая
версия, и повторное слияние покажет изменения дважды.
Отсюда простое правило: личное правим переписыванием, общее — обратным
коммитом. Ниже способы разложены по этой границе.
Способ 1: поправить последний коммит#
Забыли файл или опечатались в сообщении — коммит не снимают, а дополняют:
Откроется редактор с прежним сообщением — поправь или оставь. Чтобы
изменить только состав файлов, не трогая текст, добавь --no-edit.
Важно понимать, что происходит: старый коммит не изменяется, вместо него
создаётся новый, с другим хешем. Для отправленного коммита это то же
переписывание истории, что и перенос ветки, — со всеми ограничениями,
описанными в статье про git rebase.
Способ 2: снять коммит, оставив работу#
Коммит сделан рано или не тем набором файлов:
## main...origin/main
Коммита нет, изменения целы и лежат в индексе — можно пересобрать их иначе:
часть закоммитить сейчас, часть позже. Без флага (git reset HEAD~1)
изменения останутся в файлах, но выйдут из индекса.
Флаг --hard в этом сценарии не нужен и опасен: он не просто снимет коммит, а
сотрёт всё, чего нет в других коммитах, включая правки, которые ты не успел
добавить. Разбор всех трёх режимов — в статье о git reset.
Способ 3: удалить последний коммит совсем#
Если содержимое коммита действительно не нужно:
Перед этой командой обязательно посмотри git status -s: всё, что в выводе,
исчезнет. Сам удалённый коммит при этом ещё несколько недель живёт в
репозитории, и вернуть его можно (см. последний раздел), а вот
незакоммиченные правки — нет.
Несколько коммитов снимаются тем же способом: HEAD~3 уберёт три последних.
Способ 4: обратный коммит для общей ветки#
git revert ничего не переписывает. Он читает указанный коммит, вычисляет
противоположные изменения и делает из них новый коммит поверх:
Оба коммита остаются в истории: видно и что сделали, и что передумали. Такой
коммит отправляется обычным git push и никому не мешает — поэтому для
отправленного кода это единственный безопасный способ.
Полезные варианты:
Если отменяемые изменения успели наложиться на более поздние правки, git
остановится на конфликте. Разбирается он обычным порядком, описанным в статье
о конфликте слияния, после чего
git revert --continue доводит дело до конца, а git revert --abort
отказывается от затеи.
Чего делать не стоит#
Форсировать отправку после переписывания общей ветки. git push --force
заменяет серверную ветку твоей и молча уничтожает коммиты, появившиеся там
после твоего последнего fetch. Если переписывание всё-таки согласовано с
командой, отправляй так:
Команда откажется работать, если вершина на сервере не та, которую ты видел.
Это не полная защита (другой твой терминал мог успеть сделать fetch), но она
ловит самый частый случай — коллегу, залившего работу пять минут назад.
Удалять коммит правкой файлов вручную. Вернуть файлы к прежнему виду
руками и закоммитить — рабочий, но мутный путь: в истории появляется коммит
без объяснения. git revert делает то же самое точнее и подписывает сам.
Если отменили лишнее#
Коммиты восстанавливаются, пока их не собрал сборщик мусора:
Ветка rescue теперь указывает на «удалённый» коммит, и содержимое доступно.
Оттуда его можно перенести обратно или забрать отдельные файлы.
С незакоммиченными изменениями этот приём не работает: журнал ссылок помнит
только коммиты. Ровно поэтому перед любой опасной операцией дешевле всего
сделать коммит-черновик или отложить правки на полку.
План по этапам
- Проверить, отправлен ли коммитgit status -sb: пометка ahead — коммит только твой, переписывать можно.
- Мелкая правка последнегоgit add нужного и git commit --amend, при необходимости с --no-edit.
- Снять коммит, сохранив работуgit reset --soft HEAD~1 и пересобрать изменения иначе.
- Отменить отправленноеgit revert <хеш>, при конфликте — разрешить и git revert --continue.
- Проверить результатgit log --oneline и git status: ожидаемая вершина, ожидаемое содержимое.
- Если отменили лишнееgit reflog, найти коммит, закрепить веткой через git switch -c.
Начать изучать эту тему у себя
План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.
Проверь себя
1.Коммит отправлен в общую ветку. Какая команда отменит его, не переписывая историю?
2.Ты забыл добавить один файл в последний неотправленный коммит. Самый короткий путь?
3.Что происходит с коммитом при git commit --amend?
4.Ветка сброшена командой git reset --hard HEAD~1, а коммит оказался нужен. Что поможет?
Источники
-
Справка git revertОбратный коммит, отмена слияний, работа с диапазономбесплатно
-
Справка git commitРежим --amend и его последствия для историибесплатно
-
Pro Git, русское изданиеГлава «Основы Git» — раздел об отмене измененийбесплатно
Было полезно?