Программирование и IT

git reset

Команда двигает указатель текущей ветки на другой коммит. Насколько сильно при этом пострадает твоя работа, решает один флаг: `--soft` не трогает ничего кроме истории, `--mixed` очищает индекс, `--hard` переписывает файлы на диске. Третий режим — единственная команда git, которая уничтожает несохранённое безвозвратно.

Обновлено
В этой статье

Три области, которые двигает команда#

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

  1. История — где стоит указатель ветки и, вместе с ним, HEAD.
  2. Индекс — заготовка следующего коммита, то, что попало под git add.
  3. Рабочая копия — файлы, которые лежат в папке и открыты в редакторе.

Команда всегда двигает первый слой. Дальше — по флагу:

Флаг История Индекс Файлы на диске
--soft двигает не трогает не трогает
--mixed (по умолчанию) двигает сбрасывает не трогает
--hard двигает сбрасывает перезаписывает

Первые два режима безопасны: твои изменения остаются в файлах, меняется лишь
то, как git на них смотрит. Третий стирает правки, которых нет ни в одном
коммите. Восстановить их нельзя ничем — ни журналом ссылок, ни корзиной.

Синтаксис#

git reset --soft HEAD~1      # снять последний коммит, изменения оставить в индексе
git reset HEAD~1             # то же, но изменения останутся только в файлах
git reset --hard HEAD~1      # снять коммит и стереть его изменения полностью
git reset report.md          # убрать один файл из индекса (правки целы)
git reset --hard origin/main # привести ветку к состоянию серверной
git reset --merge            # прервать начатое слияние, сохранив чужие правки

Запись HEAD~1 означает «родитель текущего коммита», HEAD~3 — «на три
коммита назад». Вместо неё можно подставить любой хеш.

Пример: переделать последний коммит#

Коммит сделан рано: забыли файл, неудачное сообщение, лишняя отладочная
строка. Снимаем его, сохранив всё сделанное в индексе:

$ git reset --soft HEAD~1
$ git status -sb
## main
M  parser.py
A  tests/test_parser.py
$ git add utils/dates.py
$ git commit -m "разбор дат с тестом"

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

Пример: убрать лишнее из индекса#

$ git add .
$ git status -s
A  .env
M  app.py
$ git reset .env
$ git status -s
M  app.py
?? .env

Файл вышел из заготовки коммита и снова стал неотслеживаемым, содержимое на
диске не изменилось. Современный синоним того же действия —
git restore --staged .env; он читается однозначнее и не умеет случайно
двигать ветку.

Опасный режим: --hard#

$ git status -s
 M invoice.py
$ git reset --hard HEAD
HEAD is now at 7c4e1a2 отчёт по проводкам
$ git status -s
$

Правка в invoice.py уничтожена. Не «перенесена», не «спрятана» — её больше
нет нигде, потому что она ни разу не попадала в базу объектов git. То же
произойдёт со всеми несохранёнными изменениями во всех файлах, если выполнить
команду без пути.

Три привычки, которые снимают почти весь риск:

  • перед --hard всегда смотреть git status -s — если там есть строки,
    сначала спрятать их (git stash push -m "на всякий случай", подробности —
    в разборе git stash) или закоммитить;
  • для отката одного файла пользоваться git restore <файл>, а не сбросом
    всей ветки;
  • не подставлять --hard в команды из интернета не глядя: чаще всего там, где
    советуют его, достаточно --soft или --mixed.

Что делать, если коммиты пропали#

Здесь хорошая новость: коммиты, в отличие от несохранённых правок, почти
всегда возвращаются. Их не удаляли, на них просто перестали указывать.

$ git reflog
7c4e1a2 HEAD@{0}: reset: moving to HEAD~2
91b0d3f HEAD@{1}: commit: выгрузка в 1С
5ee8a41 HEAD@{2}: commit: сверка остатков
$ git reset --hard 91b0d3f
HEAD is now at 91b0d3f выгрузка в 1С

Журнал ссылок хранит каждое положение HEAD — по умолчанию несколько
недель, и записи о ставших недостижимыми коммитах живут не меньше тридцати
дней. Достаточно найти строку до злополучного сброса и вернуть ветку
на неё. Если не хочется двигать текущую ветку, можно создать на найденном
коммите новую: git switch -c rescue 91b0d3f.

reset или revert#

Разница в том, кто увидит отмену.

git reset убирает коммит из ветки: в истории его больше нет. Годится, пока
коммит живёт только у тебя. После отправки на сервер такой сброс требует
форсированной отправки, а git push --force затирает всё, что коллеги успели
залить, — если уж без него никак, бери git push --force-with-lease, он
откажется работать при неожиданных изменениях на сервере.

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

План по этапам

  1. Посмотреть, что не сохраненоgit status -s перед любым сбросом: строки в выводе — это то, что может исчезнуть.
  2. Выбрать режим--soft — переделать коммит, --mixed — пересобрать индекс, --hard — только при чистом статусе.
  3. Указать точкуHEAD~1, хеш коммита или origin/main; проверить выбор через git log --oneline.
  4. Выполнить и проверитьПосле сброса снова git status -s и git log --oneline: ожидаемая вершина и ожидаемые файлы.
  5. Если промахнулисьgit reflog, найти строку до сброса, вернуться туда — коммиты восстановимы, несохранённые правки нет.

Начать изучать эту тему у себя

План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.

Начать план

Проверь себя

1.Какой режим git reset уничтожает незакоммиченные правки в файлах?

2.Коммит уже отправлен в общую ветку и его нужно отменить. Что выбрать?

3.Что делает git reset --soft HEAD~1?

4.Ветка сброшена на два коммита назад по ошибке, коммиты нужны обратно. С чего начать?

Источники

Было полезно?

Ещё темы

Программирование и IT Как отменить коммит Вопрос «как отменить коммит» на деле скрывает четыре разных: поправить последний, снять его и переделать, стереть несколько, отменить давний коммит в середине истории. Решение зависит в первую очередь от одного обстоятельства — видел ли этот коммит кто-нибудь, кроме тебя. Программирование и IT Как создать репозиторий с нуля Репозиторий — это папка проекта плюс скрытая папка `.git`, в которой хранится вся история. Создаётся одной командой, но между ней и первым осмысленным коммитом есть несколько шагов, которые потом трудно переделать: имя ветки, список игнорируемых файлов, привязка к серверу. Программирование и IT Команды git Команд в git больше сотни, но в обычный день используют около двадцати. Здесь они собраны по порядку применения — от первой копии репозитория до отмены неудачного шага, с одной строкой пояснения на каждую. Программирование и IT git push Пока ты не выполнил git push, твои коммиты существуют только на твоём диске. Команда отправляет их в удалённый репозиторий и сдвигает там указатель ветки — поэтому она же чаще всего и отказывает, если кто-то успел отправить своё раньше. Программирование и IT git rebase Команда переносит коммиты твоей ветки так, будто ты начал работу не от старого состояния, а от текущего. История становится линейной, но коммиты при этом создаются заново — с новыми хешами. Отсюда главное ограничение: переносить можно только то, чем не пользуется никто другой. Программирование и IT git merge Слияние переносит работу из одной ветки в другую. Команда git merge берёт указанную ветку и соединяет её историю с текущей — иногда просто сдвигая указатель, иногда создавая отдельный коммит, а иногда останавливаясь и прося помощи.

Ещё сценарии