git reset: soft, mixed и hard — как откатить коммит

Git Автор: Среда и версия: git 2.55.0, Linux; каждый режим проверен на отдельном чистом репозитории 31 августа 2026
содержание

git reset откатывает ветку на указанный коммит. Вопрос всегда один: что при этом произойдёт с твоими правками — они вернутся в индекс, вернутся в рабочий каталог или исчезнут совсем. Ответ и задаёт флаг.

Здесь пригодится модель из разбора основных команд: у Git три места, где живёт код. reset отматывает назад первое, первые два или все три.

Git / 01

Режим определяет, насколько далеко пойдёт reset

Режим определяет, насколько далеко пойдёт resetрежим что сбрасывается что с файлами --soft HEAD → назад индекс и файлы целы --mixed / обычно HEAD + индекс → назад файлы целы --hard HEAD + индекс + файлы правки файлов теряютсярежимчто сбрасываетсячто с файлами--softHEAD → назадиндекс и файлы целы--mixed / обычноHEAD + индекс → назадфайлы целы--hardHEAD + индекс + файлыправки файлов теряются
Три режима отличаются ровно тем, докуда доходит откат. Файлы на диске меняет только --hard. Но --mixed тоже сбрасывает индекс: отдельная версия, сохранённая только через git add, перестанет быть в нём доступна.

Один и тот же откат, три результата

Три одинаковых репозитория, в каждом три коммита по строке в notes.txt, и в каждом — HEAD~1, то есть «отмотать на один коммит назад».

git reset --soft HEAD~1    # сам reset ничего не печатает
git status -s
M  notes.txt
git reset --mixed HEAD~1   # то же, что просто git reset HEAD~1
Unstaged changes after reset:
M	notes.txt
git status -s
 M notes.txt
git reset --hard HEAD~1
HEAD is now at bbba675 коммит 2

А git status -s после него не печатает ничего: откатывать больше нечего.

Во всех трёх случаях git log теперь показывает два коммита вместо трёх. Разница — в файле и в том, где лежат его изменения:

Режимgit status -snotes.txt на диске
--softM␣notes.txt — изменения в индексетри строки, всё на месте
--mixed␣M notes.txt — изменения в рабочем каталогетри строки, всё на месте
--hardпустодве строки — третья исчезла

Позиция буквы M — та же нотация, что в разборе основных команд: левый столбец про индекс, правый про рабочий каталог. По ней сразу видно, куда именно откат положил твою работу.

Отсюда самый частый рабочий сценарий: git reset --soft HEAD~1 — «переделать последний коммит». Коммит исчез, всё его содержимое лежит в индексе готовым, поправил и закоммитил заново.

Что можно вернуть, а что нет

--hard перезаписывает рабочий каталог. --mixed его не трогает, но сбрасывает индекс: если после git add файл меняли снова, отдельная проиндексированная версия тоже может потребовать восстановления. Перед экспериментом надёжнее сделать коммит, stash или отдельную копию.

Git / 02

Вернуть можно лишь то, что Git успел сохранить

Вернуть можно лишь то, что Git успел сохранить01 / вход 02 / операция 03 / результат был коммит git reflog найти прежний HEAD был только add git fsck --lost-found искать объект не было add Git не видел данные в Git копии нет01 / вход02 / операция03 / результатбыл коммитgit reflogнайти прежний HEADбыл только addgit fsck --lost-foundискать объектне было addGit не видел данныев Git копии нет
reflog помнит прежние положения ссылок на коммиты. Правку, которую не закоммитили и не добавили в индекс, Git восстановить не может.

Коммит возвращается

git reflog
7eb8148 HEAD@{0}: reset: moving to HEAD~1
28aef82 HEAD@{1}: commit: коммит 3
7eb8148 HEAD@{2}: commit: коммит 2
acbb610 HEAD@{3}: commit (initial): коммит 1

Reflog — журнал всех перемещений HEAD, включая сам reset. Отброшенный коммит виден в строке HEAD@{1}, и возврат к нему — тот же reset:

git reset --hard HEAD@{1}
HEAD is now at 28aef82 коммит 3

Файл снова с тремя строками. Не запускай такой --hard поверх новых несохранённых правок: сначала можно закрепить найденный коммит командой git branch rescue 28aef82. Стандартные сроки очистки reflog — 90 дней для обычных записей и 30 для записей, недостижимых из текущей вершины. Настройки и сборка мусора меняют сроки; гарантированного окна восстановления нет.

Незакоммиченная правка не возвращается

echo "важная работа, которую не закоммитили" >> notes.txt
git reset --hard HEAD

Строки в файле больше нет, и в git reflog про неё ничего — там только коммиты. Никакой команды, возвращающей её, не существует: Git этих байтов никогда не видел.

--hard обычно оставляет посторонние неотслеживаемые файлы, но удаляет или перезаписывает те, которые мешают восстановить отслеживаемые пути. Это касается и файлов, исключённых через .gitignore: правило игнорирования не защищает от такого удаления. Отдельную массовую очистку неотслеживаемых и игнорируемых файлов делает git clean -fdx; обе команды требуют заранее сохранить нужные данные.

А вот заиндексированная — уцелеет

Если перед --hard успел сделать git add, содержимое попало в базу объектов и переживёт откат — потеряется только имя:

git fsck --lost-found
dangling blob 5ec200bf8b1a23de52b6cf2c94c100cfcc321ea2
git cat-file -p 5ec200bf
полузащищённая работа

git add повышает шанс восстановления, но не заменяет резервную копию: недостижимые объекты могут быть удалены сборщиком мусора. Перед опасным экспериментом сохрани работу коммитом, через stash или вне репозитория.

reset, revert и restore — три разные команды

  1. reset — переписать историю у себяОтматывает ветку назад: коммитов становится меньше. Годится, пока эти коммиты никуда не отправлены.
  2. revert — отменить, не переписываяСоздаёт НОВЫЙ коммит, отменяющий указанный. История растёт, ничего не пропадает. Единственный безопасный способ отменить то, что уже видели другие.
  3. restore — вернуть файл, не трогая историюgit restore файл откатывает файл к индексу, git restore --staged файл убирает из индекса. Коммиты при этом не двигаются вообще.

Разница между reset и revert видна в логе. После git revert HEAD содержимое файла такое же, как после reset --hard HEAD~1, но история противоположная:

git revert --no-edit HEAD
git log --oneline
b1fe329 Revert "коммит 3"
b56f660 коммит 3
a471d5a коммит 2
6f8ec68 коммит 1

Коммит 3 на месте, к нему добавлен отменяющий. Ничего не потеряно, и у коллег, которые уже забрали коммит 3, ничего не разъедется.

Почему нельзя откатывать отправленное

Отправишь ветку, откатишь её через reset и попробуешь запушить снова — Git откажет:

 ! [rejected]        main -> main (non-fast-forward)
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

Обойти это можно только --force, и цена известна: у всех, кто успел забрать старую историю, при следующем pull начнётся расхождение, а чужие коммиты поверх отброшенных могут пропасть. Правило простое: отправленное отменяют через revert, не отправленное — через reset.

Если форсить всё же приходится, --force-with-lease безопаснее голого --force: он откажется затирать чужую работу, появившуюся после твоего последнего fetch.

Частые вопросы

Что делает git reset без флагов

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

Как отменить последний коммит, но сохранить изменения

git reset --soft HEAD~1 — коммит исчезнет, всё его содержимое останется в индексе. Если нужно только поправить сообщение, есть более прямой путь: git commit --amend.

Чем HEAD~1 отличается от HEAD^

Ничем, в том числе у коммитов слияния: обе записи означают первого родителя. Разница появляется с другими числами: HEAD^2 — второй родитель самого HEAD, а HEAD~2 — два шага по цепочке первых родителей.

Что такое git reset —hard origin/main

Приравнивает локальную ветку к удалённой, выбрасывая все свои коммиты и незакоммиченные правки. Способ сказать «забудь всё, что я тут делал». Перед ним стоит убедиться, что терять действительно нечего — незакоммиченное не вернётся.

Правда ли, что reflog спасёт в любом случае

Нет. reflog помнит перемещения HEAD, то есть коммиты. Правки в рабочем каталоге, которые не были закоммичены, в нём не отражаются вовсе и после --hard теряются насовсем.

Где потренироваться

Заведи пустую папку, сделай git init, три коммита подряд — и повтори все три режима, сверяясь с git status -s и git log --oneline после каждого. Ошибиться там нечем: репозиторий одноразовый, а привычка проверять status после отката останется. Команды, на которых всё это стоит, разобраны в основных командах Git.

Источники