Статья также доступна на украинском (перейти к просмотру).

Содержание:
- Что означает «отменить коммит» в Git
- Быстрая шпаргалка: какую команду и когда применять
- Как исправить последний коммит – git commit --amend
- Создание репозитория и его инициализация
- Создание веб-проекта и начало работы с ним
- Использование команды git commit --amend
- Как убрать последний локальный коммит – git resett
- Как убрать файл из staged – git restore --staged
- Как вернуть только один файл – git restore
- Как отменить уже отправленный коммит
- Как восстановиться после ошибки – git reflog
- Типичные ошибки и рекомендации
- В заключение
При работе над проектом с использованием системы контроля версий Git нередко случаются ситуации, когда нужно отменить внесенные в код изменения или, как говорится, «отменить коммит». Но простого решения здесь нет, поскольку нет универсальной кнопки «откатить все» для всех случаев. Все зависит от конкретной ситуации, которая бы, в частности, учитывала – как давно были внесены изменения, были ли они проиндексированы или «запушены». Ошибка в выборе команды для отмены операции может привести к неожиданным последствиям или к переписыванию истории. Чтобы уверенно выбрать правильный инструмент, необходимо понимать назначение каждого из них и применять в соответствии с ситуацией. Об этом и пойдет речь в статье с использованием конкретных примеров.
Что означает «отменить коммит» в Git
Работа Git построена на основе постоянного контроля и фиксации состояний файлов проекта, например, при работе пайплайна CI/CD, опираясь на жизненный цикл состояний
(см. Рисунок 1).

Рисунок 1. Жизненный цикл состояных файлов
Согласно этому, все файлы проекта могут быть либо под версионным контролем (отслеживаемые), либо нет (состояние Untracked).
К отслеживаемым относятся те объекты файловой системы, которые присутствовали при фиксации последнего состояния проекта. Они могут быть:
-
Немодифицированные (Unmodified);
-
Модифицированными (Modified);
-
Подготовленными к индексации (Staged).
До Untracked-файлов относятся любые файлы рабочего каталога, не присутствовавшие при фиксации последнего состояния проекта.
Цикл начинается с создания / клонирования Git репозитория. Все файлы будут отслеживаться и находиться в состоянии Unmodified.
После редактирования (Edit the file) файл(ы) переходит(ят) в состояние Modified.
После передачи изменений в индекс (Stage the file) объекты переходят в состояние Stage the file и считаются подготовленными к индексации для передачи изменений в репозиторий.
После фиксации изменений в локальном репозитории (Commit) происходит переход объектов к состоянию Unmodified, после чего цикл повторяется.
Таким образом, мы пришли к пониманию понятия коммита, представляющего собой действие пользователя по передаче подготовленных изменений в репозиторий (команда Commit), создавая точку фиксации текущего состояния проекта. Соответственно, отмена коммита означает откат изменений на предыдущее или другое заранее определенное состояние. Однако нередко случаются ситуации, когда изменения еще не дошли до фиксации в локальном репозитории или, наоборот, были уже отправлены в удаленный репозиторий, то есть «запушены». Для каждого случая применяются соответствующие команды со своим набором опций.
Системой Git предусмотрено наличие команды для отслеживания состояний всех файлов проекта. Ее название – git status.
В случае отсутствия отредактированных и/или неотслеживаемых файлов проекта указанная команда выдаст следующий результат:
On branch master nothing to commit, working tree clean
Команда сообщает, что в ветке master нет изменений для фиксации и рабочий каталог чист. Таким образом, Git включается в работу только в том случае, когда появляются отличия между рабочей версией проекта и репозиторием.
Здесь мы пришли к понятию ветви. Ветвь – это направление развития проекта, которому может быть присвоено произвольное имя для удобного управления. Стандартное имя – – master или main. Для главной ветви удаленного репозитория часто используется имя origin.
Еще одно важное понятие – HEAD. Это указатель на текущий коммит. Стандартно он указывает на последний коммит текущей ветви. Но может быть перенаправлен, например, с помощью команды git reset, что будет рассмотрено ниже.
Быстрая шпаргалка: какую команду и когда применять
В Таблице 1 приведены распространенные варианты команд для осуществления «откатов» разных уровней и показано их влияние на область индексации (Staged) и рабочий каталог проекта (Work dir). Столбик Безопасность показывает уровень безопасности выполнения команды.
Таблица 1. Распространенные команды для отката изменений и их влияние на Git среду.
| Команда | Назначение | Staged | Work dir | Безопасность |
|---|---|---|---|---|
| reset <commit> | Переключение HEAD на указанный коммит без сохранения изменений в индексе | + | - | + |
| reset --soft <commit> | Переключение HEAD на указанный коммит с сохранением изменений в индексе | - | - | + |
| reset --hard <commit> | Отмена всех изменений указанного коммита в индексе и рабочем каталоге | + | + | - |
| restore --staged <file> | Отмена индексации файла | + | - | + |
| restore <file> | Откат изменений в файле | + | + | - |
| commit --amend | Исправление последнего коммита | + | - | + |
| revert <commit> | Безопасная отмена уже отправленного коммита новым | - | - | + |
| reflog | Восстановление после неудачных действий | - | - | + |
Как исправить последний коммит – git commit --amend
Для демонстрации возможностей этой и других команд нам понадобится Git repository и новый веб-проект развернут на VPS-хостинге. Создадим их.
Создание репозитория и его инициализация
Создадим каталог с именем testgit_project и перейдем к нему. Он будет нашим рабочим каталогом проекта (Work di):
$ mkdir testgit_project $ cd testgit_project
Инициализируем в testgit_project пустой Git репозиторий:
$ git init Initialized empty Git repository in /home/testing_git/testgit_project/.git/
Тут testing_git – логин пользователя с правами sudo; .git – репозиторий.
Зарегистрируемся в Git:
$ git config --global user.email "alexander@meta.ua" $ git config --global user.name "Alexander"
Создание веб-проекта и начало работы с ним
Веб-проект будет состоять из двух html-файлов:: test1.html и test2.html.
Создадим файл test1.html:
$ nano test1.html <html> <head> </head> <body> <h1>Testing Git</h1> <p> Learning the basics of Git version control starts with examples.</p> </body> </html>

Создадим файл test2.html:
$ nano test2.html <html> <head> </head> <body> <h1>Examples</h1> <p>Here are some practical examples of using Git. </p> </body> </html>
Просмотрим дерево рабочего каталога:

Добавим файлы к индексу:
$ git add *
Просмотрим содержимое индекса с помощью команды:
$ git ls-files -s 100644 3c474c3b0c61a0e527c36376844d358c054e6001 0 test1.html 100644 fe39821a787582e6a91030f8554a7f9bf7bd93b2 0 test2.html
Можно убедиться, что файлы уже там.
Создадим коммит:
$ git commit -m 'Adding test1.html and test2.html files' [master (root-commit) 81efc13] Adding test1.html and test2.html files 2 files changed, 18 insertions(+) create mode 100644 test1.html create mode 100644 test2.html
Просмотрим историю:
$ git log commit 81efc131946c225904650ea92923a8ae40289186 (HEAD -> master) Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 18:03:29 2026 +0200 Adding test1.html and test2.html files
Здесь указатель HEAD указывает на ветку владелец, которая была создана автоматически.
HEAD всегда хешируется, и потому иногда полезно его просматривать. Это можно сделать с помощью команды:
$ git cat-file -p HEAD tree 5d52e869cf737ccbf38965f1ee81a361f73cf358 author Alexander <alexander@meta.ua> 1774022609 +0200 committer Alexander <alexander@meta.ua> 1774022609 +0200
- Adding test1.html and test2.html files
Проверим состояния или статус всех имеющихся файлов проекта:
$ git status

On branch master nothing to commit, working tree clean
Мы видим, что разница между рабочим каталогом и репозиторием отсутствует.
Импортируется в файл test1.html изменения, добавив новый абзац с текстом:
$ nano test1.html
Новый абзац: <p> First of all, let's discuss the capabilities of the system.</p>

Добавим файл в индекс с созданием соответствующего коммита:
$ git add test1.html $ git commit -m 'Added new <p> tag to test1.html file' [master c16c606] Added new <p> tag to test1.html file 1 file changed, 2 insertions(+), 2 deletions(-)
Проверим статус:
$ git status
Добавим файл в индекс с созданием соответствующего коммита:
$ git add test1.html $ git commit -m 'Added new <p> tag to test1.html file' [master c16c606] Added new <p> tag to test1.html file 1 file changed, 2 insertions(+), 2 deletions(-)
Проверим статус:
$ git status

On branch master nothing to commit, working tree clean
Полное равновесие, поскольку отличия между рабочим каталогом и репозиторием отсутствуют.
Внесем еще одно изменение, добавив тег <h2>:
$ nano test1.html
Новый тег: <h2>General overview of the system</h2>

Проверим статус:
$ git status

On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: test1.html no changes added to commit (use "git add" and/or "git commit -a")
Команда информирует, что индекс пуст и что тест1.html имеет статус модифицированный. Кроме того, команда дает подсказки: как можно добавить файл в индексную область (git add); как отменить изменения в рабочем каталоге (git restore).
Добавим файл в индекс и снова проверим его статус:
$ git add test1.html $ git status

On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: test1.html
Теперь команда информирует о том, что изменения подготовлены к отправке в репозиторий, а также предоставляет команду для возврата файла из индексной зоны (git restore --staged).
Создадим коммит:
$ git commit -m 'Added <h2> tag to test1.html file'
![[master 27e9cd9] Added <h2> tag to test1.html file](https://cdn.freehost.com.ua/How-to-Revert-Changes-in-Git-10.png)
[master 27e9cd9] Added <h2> tag to test1.html file 1 file changed, 1 insertion(+)
Снова проверим статус:
$ git status

On branch master nothing to commit, working tree clean
Мы видим, что снова наступило равновесие между рабочим каталогом и репозиторием. И потому Git сейчас бездейственна.
Использование команды git commit --amend
Нередко возникает потребность в исправлении сообщения последнего коммита. Здесь поможет следующая команда:
$ git commit --amend
При ее выполнении загружается редактор со старым сообщением, которое легко исправимо:

Эту же команду можно применять в случае, когда коммит уже создан, но необходимо внести в файл изменения или добавить новый. В таком случае последовательность действий будет следующей:
-
Вносим изменения в файл рабочего каталога:
$ nano test1.html
Добавим текст: «Git version control»)
2. Отправляем файл в индексную область:
$ git add test1.ht
3. Создаем соответствующий коммит:
$ git commit --amend --no-edit
Здесь мы использовали опцию --no-edit для того чтобы оставить сообщение коммита неизменным.

[master 616622c] Added one <h2> tag to test1.html file Date: Fri Mar 20 19:39:10 2026 +0200 1 file changed, 1 insertion(+)
Таким же образом мы можем добавить и новый файл, а не только редактируемый.
Проверим историю:
$ git lo

commit 616622cb955d8a34b4a8f7c6b2cba610c1e90550 (HEAD -> master) Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 19:39:10 2026 +0200 Added one <h2> tag to test1.html file commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 19:06:48 2026 +0200 Added new <p> tag to test1.html file commit 81efc131946c225904650ea92923a8ae40289186 Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 18:03:29 2026 +0200 Adding test1.html and test2.html files
Можно убедиться, что наши действия на историю коммитов не повлияли, поскольку рассматриваемая команда не перемещает HEAD и не изменяет ветки. Единственное ее «несовершенство» – она переписывает только последний коммит. Иногда ее применение может потребовать использования force push, однако для общих ветвей это следует делать осторожно.
Как убрать последний локальный коммит – git reset
Для отмены локальных коммитов очень часто используется команда git reset с разными параметрами. Она влияет на историю, рабочий каталог и индексную зону в зависимости от используемых параметров. Продемонстрируем его использование на конкретных примерах.
Ранее мы вывели нашу историю коммитов, где последний из них был связан с добавлением тега. <h2>. Его хеш: 616622cb955d8a34b4a8f7c6b2cba610c1e90550. Наша задача состоит в удалении тега из индекса, оставив его при этом в файле рабочего каталога. В таком случае в нашей ветке разработки HEAD нужно переместить на один шаг назад.
Команда может выглядеть так:
$ git reset --mixed HEAD~1
Или так:
$ git reset HEAD~1
Параметр --mixed применяется в стандартном режиме и потому его можно специально не указывать.
Выход команды:
Unstaged changes after reset: M test1.html
Проверим историю:
$ git log

commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 (HEAD -> master) Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 19:06:48 2026 +0200 Added new <p> tag to test1.html file commit 81efc131946c225904650ea92923a8ae40289186 Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 18:03:29 2026 +0200 Adding test1.html and test2.html files
Можно убедиться, что коммит с хэшем 616622cb955d8a34b4a8f7c6b2cba610c1e90550 теперь недоступен. Указатель HEAD теперь находится на предыдущем коммите.
Проверим, сохранились ли изменения в рабочем каталоге:
$ nano test1.html

Изменения сохранились (тег <h2> остался на своем месте).
Теперь вернем HEAD назад, указав в команде хеш удалённого коммита:
$ git reset 616622cb955d8a34b4a8f7c6b2cba610c1e90550
Проверим историю:
$ git log

Все хорошо, коммит возвращен.
Теперь нашей задачей будет только перемещение HEAD на один шаг назад, сохранив при этом изменения последнего коммита в индексе.
Для этого существует следующий вариант команды
$ git reset --soft HEAD~
Проверим историю:
$ git log

Проверим статус:
$ git status

On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: test1.html
Снова вернем состояние индекса назад:
$ git reset 616622cb955d8a34b4a8f7c6b2cba610c1e90550
Следующей нашей задачей будет отмена всех изменений последнего коммита в индексе и рабочем каталоге, то есть фактическое удаление их из репозитория.
Для этого воспользуемся командой:
$ git reset --hard HEAD~

HEAD is now at c16c606 Added new <p> tag to test1.html file
Проверим историю:
$ git log

Проверим рабочий каталог:
$ nano test1.html

Можно убедиться, что тег <h2> удален из файла
По результатам использования команды можно заключить, что команда с параметром --hard опасна, поскольку способна изменять файлы рабочего каталога, а также влияет на историю, требующую осторожности при ее применении.
Как убрать файл из staged – git restore --staged
Начиная с версии Git 2.23.0, появилась команда git restore как альтернатива git reset для большинства операций отмены изменений. Рассмотрим его использование для нашего проекта.
Сначала рассмотрим случай, когда необходимо вернуть только один файл из индексной зоны.
Создадим новый файл и отправим его в индекс:
$ touch test3.html $ git add test3.htm
Проверим статус:
$ git status

On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) new file: test3.html
Команда подсказывает, как можно вернуть файл из индексной зоны, и поэтому воспользуемся ею:
$ git restore --staged test3.html
Проверим статус
$ git status

On branch master Untracked files: (use "git add <file>..." to include in what will be committed) test3.html nothing added to commit but untracked files present (use "git add" to track)
Можно убедиться, что файл уже не находится в индексе. Следует заметить, что команда не влияет на состояние файлов рабочего каталога, и поэтому файл остался на месте
Как вернуть только один файл – git restore
Команду git restore можно применять и в других случаях, например, для отката файла в состояние последнего или любого другого коммита. Рассмотрим эти случаи
Внесем изменения в файл test1.html, добавив к нему еще один тэг <p>:
$ nano test1.htm
Новый тег: <p> Now we can move on to the first example.</p>

Сохраним внесенные изменения и закроем файл.
Проверим статус:
$ git status
On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: test1.html Untracked files: (use "git add <file>..." to include in what will be committed) test3.html
Команда подсказывает, как можно вернуть файл в предыдущее состояние. Воспользуемся ею
$ git restore test1.html
Проверим файл в рабочем каталоге:
$ nano test1.htm

Вы можете убедиться, что последние изменения были удалены из файла.
Команду можно использовать для возврата файла в определенное состояние:
$ git restore --source=HEAD~1 test1.html
Проверка:
$ nano test1.html

Можно убедиться, что файл вернулся в состояние предыдущего коммита за счет перемещения. HEAD на одну позицию вспять. Конечно, мы могли бы переместить HEAD и на другие позиции – на два, три или более коммитов назад, но это в данном случае несущественно.
Как отменить уже отправленный коммит - git revert
В Git существует команда, позволяющая аннулировать уже «запущенные» изменения, то есть, отправленные в главную ветвь удаленного репозитория. Продемонстрируем ее в работе.
Добавим к индексу обновленный нами файл test1.html и сформируем соответствующий коммит:
$ git add test1.html $ git commit -m 'Latest changes in file test1.html'
![[master 58a0a79] Latest changes in file test1.html](https://cdn.freehost.com.ua/How-to-Revert-Changes-in-Git-30.png)
[master 58a0a79] Latest changes in file test1.html 1 file changed, 2 insertions(+), 2 deletions(-)
Отправим все наши локальные изменения в удаленный репозиторий с помощью следующей команды:
$ git push origin mai
Тут origin – имя удаленного репозитория; main – имя ветви
Просмотрим историю для определения хэша коммита, который нужно аннулировать
$ git log

commit 58a0a79834d1ae04d7fb463063faa561365e4360 (HEAD -> master) Author: Alexander <alexander@meta.ua> Date: Sat Mar 21 18:15:06 2026 +0200 Latest changes in file test1.html commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 19:06:48 2026 +0200 Added new <p> tag to test1.html file commit 81efc131946c225904650ea92923a8ae40289186 Author: Alexander <alexander@meta.ua> Date: Fri Mar 20 18:03:29 2026 +0200 Adding test1.html and test2.html files
Аннулировать проблемный коммит можно с помощью команды:
$ git revert c16c606bf3ad3ea01f5bd06f2140e21f6b41f821
Тут c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 – хеш коммита, который аннулируется.
При этом автоматически будет создан новый коммит (реверт-коммит), отменяющий действия указанного в команде коммита. При этом реверт-коммит останется в истории.
Можно заключить, что команда не переписывает историю, а лишь аннулирует определенный коммит. Это лучший способ для общей ветви и уже отправленных изменений.
Как восстановиться после ошибки – git reflog
Иногда могут возникать конфликтные ситуации или ошибки, требующие восстановления определенных коммитов, например, после неудачного применения опасной команды git reset --hard. В таком случае лучше воспользоваться командой reflog. Продемонстрируем это.
Просмотрим журнал для поиска хэша коммита:
$ git reflog

58a0a79 (HEAD -> master) HEAD@{0}: commit: Latest changes in file test1.html
c16c606 HEAD@{1}: reset: moving to HEAD~1
616622c HEAD@{2}: reset: moving to 616622cb955d8a34b4a8f7c6b2cba610c1e90550
c16c606 HEAD@{3}: reset: moving to HEAD~1
616622c HEAD@{4}: reset: moving to 616622cb955d8a34b4a8f7c6b2cba610c1e90550
c16c606 HEAD@{5}: reset: moving to HEAD~1
616622c HEAD@{6}: commit (amend): Added one
tag to test1.html file 9f0dfee HEAD@{7}: commit (amend): Added one
tag to test1.html file 27e9cd9 HEAD@{8}: commit: Added
tag to test1.html file c16c606 HEAD@{9}: commit: Added new
tag to test1.html file 81efc13 HEAD@{10}: commit (initial): Adding test1.html and test2.html files
Например, мы нуждаемся в восстановлении коммита с хэшем c16c606. Для этого переключимся на него с помощью команды checkout:
$ git checkout c16c606

Note: switching to 'c16c606'. HEAD is now at c16c606 Added new <p> tag to test1.html file
В текущем состоянии HEAD отключен и указывает на c16c606. Сейчас мы можем создать отдельную ветвь для сохранения восстановленных изменений. Это можно сделать с помощью команды:
$ git switch -c new_branch

Switched to a new branch 'new_branch'
В результате указатель HEAD будет указывать на новую ветку. Проверим это:
$ git lo

Результат:
commit c16c606bf3ad3ea01f5bd06f2140e21f6b41f821 (HEAD -> new_branch)
Если же мы не хотим создавать новую ветвь, то отменить операцию можно с помощью команды
$ git switch -
По результатам применения команды можно заключить, что reflog перемещает HEAD и позволяет найти потерянный коммит после неудачного reset --hard.
Типичные ошибки и рекомендации
Укажем типичные ошибки и некоторые рекомендации при использовании команд для отмены внесенных изменений:
-
Не использовать reset --hard, если не уверен;
-
Не делать push --force в общую ветвь без осознания последствий;
-
Для уже созданных коммитов обычно лучше выбирать revert;
-
Перед опасными действиями полезно просмотреть git status и git log --oneline.
В заключение
-
Для безопасной работы важно не запоминать все команды, а понимать сценарии их использования;
-
В большинстве случаев выбор происходит между reset, revert и restore;
-
Команда reflog - страхование на случай ошибок.
Если вы хотите глубже разобраться в автоматизации процессов в Git, рекомендуем прочитать статью "Что такое Git hooks и зачем они нужны".
Подписывайтесь на наш Telegram-канал https://t.me/freehostua, чтобы быть в курсе новых полезных материалов
Смотрите наш канал YouTube на https://www.youtube.com/freehostua.
Мы в чем ошиблись, или что-то пропустили?
Напишите об этом в комментариях, мы с удовольствием ответим и обсуждаем Ваши замечания и предложения.
|
Дата: 31.03.2026
|
|

Авторам статьи важно Ваше мнение. Будем рады его обсудить с Вами:
comments powered by Disqus