Введение
Как отменить изменения и коммиты в Git с помощью checkout, restore, reset, revert, clean и amend — с примерами и рекомендациями по выбору команды.

В отличие от большинства редакторов, Git не имеет единственной кнопки «отменить». Вместо этого он предоставляет небольшой набор команд, каждая из которых воздействует на определённую область, где хранится ваша работа: рабочий каталог (файлы на диске), индекс (staging index) (изменения, помеченные для следующего коммита) и история коммитов (уже записанные снимки). Выбор правильной команды зависит от того, какую из этих трёх областей нужно откатить, а также от того, были ли изменения кем-то получены.
На этой странице рассматривается полный набор инструментов: просмотр истории, восстановление файлов и отмена одного или нескольких коммитов — с помощью git checkout, git restore, git reset, git revert, git clean и git commit --amend.
Выбор правильной команды
Прежде чем переходить к конкретным командам, полезно знать, какую область затрагивает каждая из них и перезаписывает ли она историю:
| Цель | Команда | Безопасно делиться? |
|---|---|---|
| Отменить изменения в отслеживаемом файле | git restore <file> (или git checkout -- <file>) | да |
| Убрать файл из индекса | git restore --staged <file> (или git reset HEAD <file>) | да |
| Удалить неотслеживаемые файлы | git clean | да |
| Исправить последний коммит | git commit --amend | только если не отправлен |
| Вернуть ветку к более старому коммиту | git reset | только локально |
| Отменить коммит, добавив обратный | git revert | да |
Главное правило: reset и amend перезаписывают историю, поэтому используйте их только для коммитов, которые ещё не были отправлены (push). Для всего, что уже доступно другим, используйте revert.
Просмотр старых коммитов
Невозможно отменить то, что не можешь найти. Лучший инструмент для просмотра истории — git log. У каждого коммита есть идентификационный хеш, с помощью которого на него ссылаются:
git log --oneline
a3b2a21 Crossword solver with Vue.js
c54ce02 New logic for crossword game
3acb8d0 Some changes in crossword logic
de32112 Styling crossword table areaПо умолчанию git log показывает только коммиты, доступные из текущей ветки. Чтобы просмотреть коммиты из всех веток, добавьте --all. Используйте git checkout или git switch для переключения на другие ветки.
Просмотр старой ревизии
Чтобы изучить проект в том виде, в каком он был в более ранний момент времени, сначала найдите хеш нужной ревизии:
git log --oneline
b7119f2 Changes in Scrabble Solver
234be24 Fixing search input bug
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesЗатем переключитесь на этот коммит по его хешу:
git checkout b235bf4Теперь можно просматривать файлы, запускать тесты и даже вносить изменения. Ничего из сделанного здесь не записывается ни в одну ветку, поэтому текущая работа в безопасности. Это временное состояние называется detached HEAD — HEAD указывает непосредственно на коммит, а не на конец ветки. Когда закончите, вернитесь на свою ветку:
git switch - # or: git checkout masterВернувшись на ветку, используйте git revert или git reset, чтобы отменить любое нужное изменение.
Отмена зафиксированного снимка
Существует несколько способов отменить коммит. Правильный выбор зависит от того, был ли коммит кому-то передан. Допустим, история выглядит так:
git log --oneline
863fa8e Making some improvements
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesВ разделах ниже коммит 863fa8e Making some improvements отменяется тремя разными способами.
С помощью git checkout (просмотр без изменения ветки)
Переключение на предыдущий коммит b235bf4 переводит репозиторий в состояние до коммита с улучшениями:
git checkout b235bf4Это скорее просмотр в режиме только для чтения, а не настоящая отмена: вы оказываетесь в состоянии detached HEAD. Любой новый коммит, сделанный здесь, станет orphaned (осиротевшим), как только вы переключитесь обратно на существующую ветку, и сборщик мусора Git в итоге может его удалить. Чтобы сохранить работу, выполненную из этого состояния, создайте ответвление:
git checkout -b improvements-removedТеперь у вас есть новая ветка improvements-removed, в истории которой нет 863fa8e. Checkout лучше всего подходит для просмотра старого состояния; следующие две команды действительно отменяют коммит.
С помощью git revert (безопасная, передаваемая отмена)
git revert HEAD создаёт новый коммит, применяющий обратные изменения к целевому коммиту. Ничего не стирается — история движется вперёд:
git revert HEADПосле выполнения лог выглядит следующим образом:
git log --oneline
23a4b42 Revert "Making some improvements"
863fa8e Making some improvements
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesЭффект коммита 863fa8e теперь отменён, однако сам коммит по-прежнему присутствует в истории; 23a4b42 просто обращает его изменения. Поскольку ничего не перезаписывается, вы остаётесь на той же ветке и не ломаете чужие клоны. Это правильный способ отмены коммитов, которые уже были отправлены или опубликованы.
С помощью git reset (перезапись локальной истории)
git reset перемещает указатель текущей ветки на выбранный коммит. Выполнение git reset --hard b235bf4 откатывает ветку к этому коммиту и отбрасывает всё после него:
git reset --hard b235bf4
git log --oneline
b235bf4 Make some changes to solver.php
256a81c Create solver.php
3243e12 Initial changesКоммит 863fa8e исчез из истории этой ветки. Это чисто и прямолинейно, однако, поскольку история перезаписывается, это нужно делать только с коммитами, которые ещё не были отправлены. git reset принимает флаг режима, определяющий глубину отмены:
--soft— перемещает только указатель ветки; изменения остаются в индексе.--mixed(по умолчанию) — перемещает указатель и убирает изменения из индекса, но оставляет их в рабочем каталоге.--hard— перемещает указатель и удаляет все изменения из индекса и рабочего каталога.
git reset --hard безвозвратно удаляет незафиксированные изменения из рабочего каталога. Восстановить их из индекса не получится. Используйте --soft или --mixed, если хотите переделать коммит, но сохранить правки.
Отмена последнего коммита
Иногда нужно не удалить последний коммит, а только исправить его — вы зафиксировали слишком рано или написали неудачное сообщение. Добавьте в индекс забытые изменения с помощью git add, затем исправьте коммит:
git add forgotten-file.txt
git commit --amendGit откроет настроенный редактор, в котором можно изменить сообщение последнего коммита, а новые проиндексированные изменения войдут в тот же коммит. Чтобы изменить только сообщение одной командой:
git commit --amend -m "A clearer message"Amend заменяет предыдущий коммит новым (с новым хешем), поэтому обращайтесь с ним как с reset: исправляйте только коммиты, которые ещё не были отправлены.
Отмена незафиксированных изменений
До того как изменение зафиксировано, оно находится в рабочем каталоге и индексе, поэтому его можно отменить из любой области без затрагивания истории. Современная команда — git restore:
# Discard edits to a tracked file (working directory)
git restore <file>
# Unstage a file but keep its edits
git restore --staged <file>Старые эквиваленты по-прежнему работают и встречаются во многих документациях:
# Discard changes in the working directory
git checkout -- <file>
# Unstage a file
git reset HEAD <file>Удаление неотслеживаемых файлов
git restore и reset работают только с файлами, которые Git уже отслеживает. Чтобы удалить совершенно новые, неотслеживаемые файлы, используйте git clean. Всегда сначала делайте предварительный просмотр с помощью -n (пробный запуск):
git clean -n # list what would be removed
git clean -f # actually remove untracked filesКак связаны три области
Полезно чётко различать три области, поскольку каждая команда отмены воздействует на конкретную из них:
- Рабочий каталог — файлы на диске, которые изменяет ваш редактор. Здесь работают
git restore <file>иgit clean. - Индекс (staging index) — снимок, который вы формируете для следующего коммита.
git addпомещает изменения сюда;git restore --staged(сброс--mixed) возвращает их обратно в рабочий каталог. - История коммитов — записанные снимки.
git resetперезаписывает её;git revertдополняет.
Восстановление после ошибочной отмены
Сброс или удалённая ветка могут показаться катастрофой, но Git редко немедленно что-либо теряет. git reflog записывает, куда указывал HEAD, поэтому можно найти хеш «потерянного» коммита и вернуться к нему:
git reflog
1a2b3c4 HEAD@{0}: reset: moving to b235bf4
863fa8e HEAD@{1}: commit: Making some improvementsВосстановите с помощью git reset --hard 863fa8e (или создайте ветку от этого коммита). Если нужно временно отложить работу, а не отменять её, рассмотрите git stash.
Отмена публичных изменений
Как только коммит отправлен, другие люди могут его получить. Используйте git revert для публичных изменений, но не git reset. Reset удаляет коммиты из истории, и перезапись общей ветки вынуждает всех остальных восстанавливать свои клоны. Revert оставляет исходный коммит на месте и записывает новый коммит, который его обращает — это безопасно для всех, кто уже сделал pull.