Отмена изменений в Git: Reset, Restore, Revert — объяснение

Поймите, как работают git restore, reset и revert, чтобы снимать файлы с индексации, отменять локальные правки, откатывать коммиты и избегать перезаписи общей истории.

Отмена изменений в Git: Reset, Restore, Revert — объяснение

Отмена изменений в Git безопасна, если вы знаете, какой слой изменяете: рабочую директорию, область подготовки (staging area) или историю коммитов. Основные команды — git restore, git reset и git revert — решают разные задачи.

Главное правило простое: используйте restore для файлов, reset для локальных изменений истории и revert для отмены коммитов, которые уже могли быть у других.

Понимание ключевых понятий

Запомните эти области:

  • Рабочая директория: файлы, которые вы редактируете.
  • Область подготовки (индекс): изменения, выбранные для следующего коммита.
  • История коммитов: уже записанные коммиты.
  • HEAD: текущий коммит, на который указывает ваша ветка.

Перед любой отменой выполните git status. Он покажет, какие изменения не проиндексированы, проиндексированы или уже закоммичены.

git restore: для отмены изменений в рабочей директории и области подготовки

Используйте git restore, когда нужно изменить файлы в рабочей директории или области подготовки без перемещения указателя ветки.

Снятие файлов с индексации

Если вы случайно проиндексировали файл, снимите его с помощью --staged:

git restore --staged <файл>

Чтобы снять всё:

git restore --staged .

Ваши правки в файлах останутся в рабочей директории. Они просто будут исключены из следующего коммита.

Отмена изменений в рабочей директории

Чтобы отбросить неиндексированные правки в одном файле:

git restore <файл>

Это приведёт копию в рабочей директории к состоянию индекса. Если файл не проиндексирован, обычно это означает возврат к HEAD. Сначала выполните git diff, так как эта команда удаляет локальные правки.

Чтобы отбросить как проиндексированные, так и неиндексированные изменения для файла и восстановить его из HEAD:

git restore --source=HEAD --staged --worktree <файл>

Восстановление файла из определённого коммита

Вы также можете вернуть один файл из старого коммита:

git restore --source=<коммит> -- путь/к/файлу

Это изменит файл в вашей рабочей директории. Если старая версия нужна в будущем, сделайте коммит.

git reset: переписывание истории

Используйте git reset, когда нужно переместить указатель текущей ветки. В зависимости от режима он также может изменить область подготовки и рабочую директорию.

Понимание режимов (--soft, --mixed, --hard)

Три основных режима:

git reset --soft HEAD^

Перемещает HEAD на один коммит назад и оставляет изменения отменённого коммита проиндексированными.

git reset --mixed HEAD^

Перемещает HEAD на один коммит назад и оставляет изменения отменённого коммита неиндексированными. --mixed используется по умолчанию, поэтому git reset HEAD^ делает то же самое.

git reset --hard HEAD^

Перемещает HEAD на один коммит назад и удаляет соответствующие изменения из области подготовки и рабочей директории. Это разрушительное действие. Не выполняйте его бездумно.

Полезные шаблоны reset:

git reset --soft HEAD^      # переделать последний локальный коммит, оставить изменения проиндексированными
git reset HEAD^             # отменить последний локальный коммит, оставить изменения неиндексированными
git reset                   # снять все проиндексированные изменения
git reset --hard origin/main # отбросить локальные изменения и синхронизироваться с origin/main

Сброс к старому коммиту

Чтобы переместить ветку на два коммита назад, сохранив изменения в рабочей директории:

git reset --mixed HEAD~2

Сбрасывайте только локальные коммиты или те, которые ваша команда согласилась переписать. Если коммиты уже находятся в общей ветке, предпочтительнее git revert.

git revert: создание нового коммита для отмены изменений

git revert создаёт новый коммит, который применяет обратные изменения к предыдущему коммиту. Он не удаляет и не перемещает исходный коммит, поэтому безопасен для общих веток.

Отмена одного коммита:

git revert <хэш-коммита>

Отмена диапазона:

git revert HEAD~3..HEAD

Если возникли конфликты, разрешите их, проиндексируйте файлы и продолжите:

git add <исправленные-файлы>
git revert --continue

Отмена merge-коммита требует особой осторожности, так как Git должен знать, какой родитель считать основной линией:

git revert -m 1 <merge-коммит>

Делайте это только если понимаете, что привнесло слияние. Для продакшен-веток перед отменой слияний запрашивайте ревью.

Выбор правильной команды

Используйте эту краткую памятку:

  • Снять файл с индексации: git restore --staged <файл>.
  • Отбросить неиндексированные правки в файле: git restore <файл>.
  • Отменить последний локальный коммит и оставить изменения проиндексированными: git reset --soft HEAD^.
  • Отменить последний локальный коммит и оставить изменения неиндексированными: git reset HEAD^.
  • Разрушительно отбросить локальные изменения и коммиты: git reset --hard <коммит>.
  • Безопасно отменить отправленный коммит: git revert <хэш-коммита>.

Практический вывод

Перед любой отменой выполните git status и решите, что именно вы меняете. Используйте restore для очистки на уровне файлов, reset для локальных неотправленных коммитов и revert для общей истории. Если команда содержит --hard, остановитесь и сначала проверьте git diff.