Программирование и IT
git reset
Команда двигает указатель текущей ветки на другой коммит. Насколько сильно при этом пострадает твоя работа, решает один флаг: `--soft` не трогает ничего кроме истории, `--mixed` очищает индекс, `--hard` переписывает файлы на диске. Третий режим — единственная команда git, которая уничтожает несохранённое безвозвратно.
В этой статье
Три области, которые двигает команда#
Чтобы режимы перестали путаться, полезно держать в голове три слоя.
- История — где стоит указатель ветки и, вместе с ним,
HEAD. - Индекс — заготовка следующего коммита, то, что попало под
git add. - Рабочая копия — файлы, которые лежат в папке и открыты в редакторе.
Команда всегда двигает первый слой. Дальше — по флагу:
| Флаг | История | Индекс | Файлы на диске |
|---|---|---|---|
--soft |
двигает | не трогает | не трогает |
--mixed (по умолчанию) |
двигает | сбрасывает | не трогает |
--hard |
двигает | сбрасывает | перезаписывает |
Первые два режима безопасны: твои изменения остаются в файлах, меняется лишь
то, как git на них смотрит. Третий стирает правки, которых нет ни в одном
коммите. Восстановить их нельзя ничем — ни журналом ссылок, ни корзиной.
Синтаксис#
Запись HEAD~1 означает «родитель текущего коммита», HEAD~3 — «на три
коммита назад». Вместо неё можно подставить любой хеш.
Пример: переделать последний коммит#
Коммит сделан рано: забыли файл, неудачное сообщение, лишняя отладочная
строка. Снимаем его, сохранив всё сделанное в индексе:
## main
История стала короче на коммит, содержимое никуда не делось. Для случая, когда
нужно только дописать файл или поправить сообщение, есть более короткий путь —
git commit --amend, он разобран среди способов
отменить коммит.
Пример: убрать лишнее из индекса#
Файл вышел из заготовки коммита и снова стал неотслеживаемым, содержимое на
диске не изменилось. Современный синоним того же действия —
git restore --staged .env; он читается однозначнее и не умеет случайно
двигать ветку.
Опасный режим: --hard#
Правка в invoice.py уничтожена. Не «перенесена», не «спрятана» — её больше
нет нигде, потому что она ни разу не попадала в базу объектов git. То же
произойдёт со всеми несохранёнными изменениями во всех файлах, если выполнить
команду без пути.
Три привычки, которые снимают почти весь риск:
- перед
--hardвсегда смотретьgit status -s— если там есть строки,
сначала спрятать их (git stash push -m "на всякий случай", подробности —
в разборе git stash) или закоммитить; - для отката одного файла пользоваться
git restore <файл>, а не сбросом
всей ветки; - не подставлять
--hardв команды из интернета не глядя: чаще всего там, где
советуют его, достаточно--softили--mixed.
Что делать, если коммиты пропали#
Здесь хорошая новость: коммиты, в отличие от несохранённых правок, почти
всегда возвращаются. Их не удаляли, на них просто перестали указывать.
Журнал ссылок хранит каждое положение HEAD — по умолчанию несколько
недель, и записи о ставших недостижимыми коммитах живут не меньше тридцати
дней. Достаточно найти строку до злополучного сброса и вернуть ветку
на неё. Если не хочется двигать текущую ветку, можно создать на найденном
коммите новую: git switch -c rescue 91b0d3f.
reset или revert#
Разница в том, кто увидит отмену.
git reset убирает коммит из ветки: в истории его больше нет. Годится, пока
коммит живёт только у тебя. После отправки на сервер такой сброс требует
форсированной отправки, а git push --force затирает всё, что коллеги успели
залить, — если уж без него никак, бери git push --force-with-lease, он
откажется работать при неожиданных изменениях на сервере.
git revert не трогает существующую историю, а добавляет новый коммит с
обратными изменениями. Отправляется обычным git push, не мешает никому и
поэтому остаётся единственным правильным способом отменить то, что уже ушло в
общую ветку. Стоит запомнить пару: личное — сброс, общее — обратный коммит.
Та же граница проходит через работу с
переносом коммитов.
План по этапам
- Посмотреть, что не сохраненоgit status -s перед любым сбросом: строки в выводе — это то, что может исчезнуть.
- Выбрать режим--soft — переделать коммит, --mixed — пересобрать индекс, --hard — только при чистом статусе.
- Указать точкуHEAD~1, хеш коммита или origin/main; проверить выбор через git log --oneline.
- Выполнить и проверитьПосле сброса снова git status -s и git log --oneline: ожидаемая вершина и ожидаемые файлы.
- Если промахнулисьgit reflog, найти строку до сброса, вернуться туда — коммиты восстановимы, несохранённые правки нет.
Начать изучать эту тему у себя
План ляжет в твой репозиторий: отмечай этапы, веди конспект — история изменений покажет, как ты продвинулся.
Проверь себя
1.Какой режим git reset уничтожает незакоммиченные правки в файлах?
2.Коммит уже отправлен в общую ветку и его нужно отменить. Что выбрать?
3.Что делает git reset --soft HEAD~1?
4.Ветка сброшена на два коммита назад по ошибке, коммиты нужны обратно. С чего начать?
Источники
-
Справка git resetРазбор режимов с таблицами состоянийбесплатно
-
Pro Git, русское изданиеГлава «Инструменты Git» — раздел про reset подробно, с картинками трёх областейбесплатно
-
Справка git reflogКак найти коммит после неудачного сбросабесплатно
Было полезно?