Программирование и IT

Как отменить коммит

Вопрос «как отменить коммит» на деле скрывает четыре разных: поправить последний, снять его и переделать, стереть несколько, отменить давний коммит в середине истории. Решение зависит в первую очередь от одного обстоятельства — видел ли этот коммит кто-нибудь, кроме тебя.

Обновлено
В этой статье

Первый вопрос: коммит уже отправлен?#

Ответ определяет весь дальнейший путь, поэтому начинают с него:

$ git status -sb
## main...origin/main [ahead 1]

Пометка ahead 1 означает: у тебя на коммит больше, чем на сервере, и этот
коммит никто не видел. Историю можно переписывать свободно. Если пометки нет,
коммит уже ушёл — переписывать нельзя, потому что у коллег останется старая
версия, и повторное слияние покажет изменения дважды.

Отсюда простое правило: личное правим переписыванием, общее — обратным
коммитом
. Ниже способы разложены по этой границе.

Способ 1: поправить последний коммит#

Забыли файл или опечатались в сообщении — коммит не снимают, а дополняют:

$ git add tests/test_rates.py
$ git commit --amend
[main 4e77b02] курс валют с тестом
 Date: Mon Sep 14 12:31:02 2026 +0300
 2 files changed, 38 insertions(+)

Откроется редактор с прежним сообщением — поправь или оставь. Чтобы
изменить только состав файлов, не трогая текст, добавь --no-edit.

Важно понимать, что происходит: старый коммит не изменяется, вместо него
создаётся новый, с другим хешем. Для отправленного коммита это то же
переписывание истории, что и перенос ветки, — со всеми ограничениями,
описанными в статье про git rebase.

Способ 2: снять коммит, оставив работу#

Коммит сделан рано или не тем набором файлов:

$ git log --oneline -2
4e77b02 (HEAD -> main) курс валют с тестом
a03f1d9 вынес настройки в env
$ git reset --soft HEAD~1
$ git status -sb
## main...origin/main
M  rates.py
A  tests/test_rates.py

Коммита нет, изменения целы и лежат в индексе — можно пересобрать их иначе:
часть закоммитить сейчас, часть позже. Без флага (git reset HEAD~1)
изменения останутся в файлах, но выйдут из индекса.

Флаг --hard в этом сценарии не нужен и опасен: он не просто снимет коммит, а
сотрёт всё, чего нет в других коммитах, включая правки, которые ты не успел
добавить. Разбор всех трёх режимов — в статье о git reset.

Способ 3: удалить последний коммит совсем#

Если содержимое коммита действительно не нужно:

$ git reset --hard HEAD~1
HEAD is now at a03f1d9 вынес настройки в env

Перед этой командой обязательно посмотри git status -s: всё, что в выводе,
исчезнет. Сам удалённый коммит при этом ещё несколько недель живёт в
репозитории, и вернуть его можно (см. последний раздел), а вот
незакоммиченные правки — нет.

Несколько коммитов снимаются тем же способом: HEAD~3 уберёт три последних.

Способ 4: обратный коммит для общей ветки#

git revert ничего не переписывает. Он читает указанный коммит, вычисляет
противоположные изменения и делает из них новый коммит поверх:

$ git revert 77d0e1b
[main 91ac4f0] Revert "округление валюты"
 1 file changed, 7 insertions(+), 7 deletions(-)
$ git log --oneline -2
91ac4f0 (HEAD -> main) Revert "округление валюты"
77d0e1b округление валюты

Оба коммита остаются в истории: видно и что сделали, и что передумали. Такой
коммит отправляется обычным git push и никому не мешает — поэтому для
отправленного кода это единственный безопасный способ.

Полезные варианты:

git revert --no-commit 77d0e1b   # подготовить отмену, но коммит сделать самому
git revert HEAD~3..HEAD          # отменить диапазон коммитов
git revert -m 1 5c1a9f0          # отменить слияние: 1 — та линия, что остаётся

Если отменяемые изменения успели наложиться на более поздние правки, git
остановится на конфликте. Разбирается он обычным порядком, описанным в статье
о конфликте слияния, после чего
git revert --continue доводит дело до конца, а git revert --abort
отказывается от затеи.

Чего делать не стоит#

Форсировать отправку после переписывания общей ветки. git push --force
заменяет серверную ветку твоей и молча уничтожает коммиты, появившиеся там
после твоего последнего fetch. Если переписывание всё-таки согласовано с
командой, отправляй так:

git push --force-with-lease

Команда откажется работать, если вершина на сервере не та, которую ты видел.
Это не полная защита (другой твой терминал мог успеть сделать fetch), но она
ловит самый частый случай — коллегу, залившего работу пять минут назад.

Удалять коммит правкой файлов вручную. Вернуть файлы к прежнему виду
руками и закоммитить — рабочий, но мутный путь: в истории появляется коммит
без объяснения. git revert делает то же самое точнее и подписывает сам.

Если отменили лишнее#

Коммиты восстанавливаются, пока их не собрал сборщик мусора:

$ git reflog
a03f1d9 HEAD@{0}: reset: moving to HEAD~1
4e77b02 HEAD@{1}: commit: курс валют с тестом
a03f1d9 HEAD@{2}: commit: вынес настройки в env
$ git switch -c rescue 4e77b02
Switched to a new branch 'rescue'

Ветка rescue теперь указывает на «удалённый» коммит, и содержимое доступно.
Оттуда его можно перенести обратно или забрать отдельные файлы.

С незакоммиченными изменениями этот приём не работает: журнал ссылок помнит
только коммиты. Ровно поэтому перед любой опасной операцией дешевле всего
сделать коммит-черновик или отложить правки на полку.

План по этапам

  1. Проверить, отправлен ли коммитgit status -sb: пометка ahead — коммит только твой, переписывать можно.
  2. Мелкая правка последнегоgit add нужного и git commit --amend, при необходимости с --no-edit.
  3. Снять коммит, сохранив работуgit reset --soft HEAD~1 и пересобрать изменения иначе.
  4. Отменить отправленноеgit revert <хеш>, при конфликте — разрешить и git revert --continue.
  5. Проверить результатgit log --oneline и git status: ожидаемая вершина, ожидаемое содержимое.
  6. Если отменили лишнееgit reflog, найти коммит, закрепить веткой через git switch -c.

Начать изучать эту тему у себя

План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.

Начать план

Проверь себя

1.Коммит отправлен в общую ветку. Какая команда отменит его, не переписывая историю?

2.Ты забыл добавить один файл в последний неотправленный коммит. Самый короткий путь?

3.Что происходит с коммитом при git commit --amend?

4.Ветка сброшена командой git reset --hard HEAD~1, а коммит оказался нужен. Что поможет?

Источники

Было полезно?

Ещё темы

Программирование и IT git commit Коммит — это сохранённое состояние проекта с автором, временем и сообщением. Команда git commit берёт то, что ты заранее отобрал командой git add, и записывает это в историю одной неделимой порцией. Программирование и IT git reset Команда двигает указатель текущей ветки на другой коммит. Насколько сильно при этом пострадает твоя работа, решает один флаг: `--soft` не трогает ничего кроме истории, `--mixed` очищает индекс, `--hard` переписывает файлы на диске. Третий режим — единственная команда git, которая уничтожает несохранённое безвозвратно. Программирование и IT Как создать репозиторий с нуля Репозиторий — это папка проекта плюс скрытая папка `.git`, в которой хранится вся история. Создаётся одной командой, но между ней и первым осмысленным коммитом есть несколько шагов, которые потом трудно переделать: имя ветки, список игнорируемых файлов, привязка к серверу. Программирование и IT git push Пока ты не выполнил git push, твои коммиты существуют только на твоём диске. Команда отправляет их в удалённый репозиторий и сдвигает там указатель ветки — поэтому она же чаще всего и отказывает, если кто-то успел отправить своё раньше. Программирование и IT git merge Слияние переносит работу из одной ветки в другую. Команда git merge берёт указанную ветку и соединяет её историю с текущей — иногда просто сдвигая указатель, иногда создавая отдельный коммит, а иногда останавливаясь и прося помощи. Программирование и IT git clone Команда git clone забирает репозиторий с сервера целиком — со всей историей, ветками и тегами — и кладёт копию в новую папку на твоём компьютере. С неё начинается работа с любым уже существующим проектом, своим или чужим.

Ещё сценарии