W3docs

Введение

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

undoing changes

В отличие от большинства редакторов, 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 HEADHEAD указывает непосредственно на коммит, а не на конец ветки. Когда закончите, вернитесь на свою ветку:

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 --amend

Git откроет настроенный редактор, в котором можно изменить сообщение последнего коммита, а новые проиндексированные изменения войдут в тот же коммит. Чтобы изменить только сообщение одной командой:

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.

Практика

Практика
Каковы функции различных команд Git, используемых для отмены изменений и коммитов?
Каковы функции различных команд Git, используемых для отмены изменений и коммитов?
Was this page helpful?