open atlas
↑ К треку
Git: от нуля до сеньора GIT · 04 · 03

Reflog и восстановление

git reflog локально записывает каждое перемещение HEAD — коммиты, reset, checkout, rebase — чтобы найти и вернуть работу после плохого reset --hard или удалённой ветки. Записи истекают примерно через 90 дней; git fsck --lost-found находит висячие коммиты как крайнее средство.

GIT Middle ◷ 16 min
Уровень
ОсновыJuniorMiddleSenior

Ты запускаешь git reset --hard, чтобы прибраться, и через мгновение понимаешь, что только что выбросил три коммита законченной работы. git log не показывает их следа — новичку они кажутся пропавшими навсегда, и наступает паника. Они не пропали. Git ведёт личный журнал всех мест, где когда-либо был HEAD, и этот журнал — reflog — это то, как ты пройдёшь прямо назад к «потерянному» коммиту.

К концу этого урока ты сможешь читать reflog, находить коммит, исчезнувший из git log, и восстанавливать его — и будешь доверять тому, что закоммиченная работа почти никогда не теряется по-настоящему.

Цель

После этого урока ты сможешь использовать git reflog, чтобы найти коммит, отброшенный плохим reset или удалённой веткой, восстановить его через git reset --hard <sha> или git switch -c и объяснить, когда записи reflog истекают и как git fsck находит висячие коммиты как крайнее средство.

1

Reflog записывает каждое перемещение HEAD, локально, даже перемещения, переписывающие историю. Каждый раз, когда HEAD меняется — коммит, reset, checkout/switch, merge, каждый шаг rebase, — git дописывает строку в reflog со старой позицией, новой позицией и тем, что это вызвало:

git reflog
# a1b2c3d HEAD@{0}: reset: moving to HEAD~3
# 9f8e7d6 HEAD@{1}: commit: Add retry logic
# 1234abc HEAD@{2}: commit: Add backoff
# ...

HEAD@{0} — это где ты сейчас; HEAD@{1} — одно перемещение назад, и так далее. Что важно, 9f8e7d6 всё ещё в списке, хотя git log его больше не показывает, — reflog помнит позицию до reset.

2

Чтобы восстановиться после плохого reset --hard, найди в reflog SHA до сброса и сбрось обратно на него. Строка reflog прямо над записью reset — это коммит, на котором ты был до того, как всё сломал:

git reflog
# ... HEAD@{0}: reset: moving to HEAD~3   <- ошибка
# 9f8e7d6 HEAD@{1}: commit: Add retry logic  <- куда ты хочешь вернуться
git reset --hard 9f8e7d6      # или: git reset --hard HEAD@{1}

Твои три коммита снова появляются в git log, а рабочее дерево восстановлено. Ты отменил отмену. (Используй синтаксис HEAD@{1} или сырой SHA — оба называют один и тот же коммит.)

3

Reflog также спасает удалённую ветку — восстанови её в новую ветку. Когда ты удаляешь ветку, её коммиты становятся без ссылок, но всё ещё существуют в хранилище объектов, а reflog сохранил SHA вершины:

git reflog                       # найди последний коммит удалённой ветки, напр. c0ffee1
git switch -c rescue c0ffee1     # пересоздай ветку, указывающую на него

git switch -c rescue c0ffee1 создаёт свежую ветку rescue на том коммите, вытягивая «потерянную» линию работы обратно в названное, безопасное место. Оттуда можно переименовать или влить её, как будто ничего не было.

Почему это работает

Почему работа вообще переживает reset --hard? Потому что git почти никогда не удаляет объекты коммитов немедленно. Reset просто двигает указатель; коммит, на который он указывал, становится «недостижимым», но остаётся в .git/objects. Reflog держит на него ссылку, что и позволяет тебе его найти, и не даёт сборщику мусора его удалить. Вот почему «я это закоммитил» — волшебная фраза: как только работа в коммите, какой-то указатель почти всегда всё ещё ведёт к нему обратно.

4

Записи reflog истекают, и git fsck — это крайнее средство, когда на коммит не указывает ни один reflog. Записи reflog хранятся не вечно: достижимые записи истекают примерно через 90 дней, а записи для недостижимых коммитов — примерно через 30 дней (gc.reflogExpire / gc.reflogExpireUnreachable). После этого git gc может наконец удалить объекты. Если коммит выпал из reflog, но сборка мусора его ещё не убрала, его всё равно можно поискать:

git fsck --lost-found      # перечисляет висячие коммиты, недостижимые ни из одной ссылки
git show <dangling-sha>    # осмотри кандидата перед восстановлением

fsck сообщает о висячих коммитах — объектах, на которые не указывает ни одна ссылка. Это грязнее, чем reflog (нет полезных сообщений, только SHA), но может всплыть работа, которую reflog уже забыл.

Разбор примера

Вернуть два коммита, которые коллега «удалил» жёстким reset.

Коллега запустил git reset --hard origin/main, чтобы отбросить запоротый мерж, не сообразив, что на ветке были два его собственных незапушенных коммита. git log показывает, что они пропали. Ты их восстанавливаешь:

git reflog
# e1e1e1e HEAD@{0}: reset: moving to origin/main
# bbbb222 HEAD@{1}: commit: Cache invalidation fix   <- нужен этот
# aaaa111 HEAD@{2}: commit: Add cache layer
git switch -c recovered-work bbbb222
git log --oneline -2
# bbbb222 Cache invalidation fix
# aaaa111 Add cache layer

Оба коммита живы на новой ветке recovered-work. На самом деле ничего не удалялось — reset лишь сдвинул указатель ветки, а reflog запомнил ровно, где она была. Коллега черри-пикает или вливает эти коммиты обратно, куда они и должны попасть, потеряв ноль работы.

Частая ошибка

Reflog локален и на каждый клон — он живёт в .git/logs/ и никогда не пушится и не фетчится. Поэтому он не спасёт работу, существовавшую только на машине, которой у тебя больше нет, а свежий git clone начинает с пустым reflog. Заверение «закоммиченная работа почти никогда не теряется» предполагает, что исходный репозиторий (с его reflog и хранилищем объектов) всё ещё существует. Следствие: пушь важную работу на remote, потому что это единственная копия, которую reflog тебе не вернёт.

Проверь себя
Викторина

Ты запустил `git reset --hard HEAD~3`, и три твоих коммита исчезли из `git log`. Какой самый прямой способ вернуть их?

Итог

git reflog — это локальный журнал git всех перемещений HEAD: коммитов, reset, checkout, rebase. Когда reset --hard или удалённая ветка заставляют работу исчезнуть из git log, reflog всё ещё ссылается на потерянный коммит: найди его SHA и git reset --hard <sha> или git switch -c rescue <sha>, чтобы восстановить. Записи истекают (~90 дней достижимые, ~30 недостижимые), после чего git gc может удалить объекты; git fsck --lost-found — это крайний поиск висячих коммитов. Главное заверение: как только работа закоммичена, она почти никогда не теряется по-настоящему, — но reflog локален, так что пушь всё, что не можешь позволить себе потерять. Дальше ты начнёшь намеренно переписывать историю через git commit --amend и интерактивный rebase.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources2
expand
  1. 01
  2. 02

Trademarks belong to their respective owners. Editorial reference only.