Что делает команда git reflog: полный практический гид

Введение

Иногда в Git всё идёт не по плану.
Удалили не ту ветку. Сделали git reset --hard не туда.
После rebase “пропали” коммиты.

Кажется, что проект сломался и всё пропало.

Чаще всего это не так.
В большинстве таких ситуаций помогает одна команда:

git reflog

Разберёмся, что она делает, как её читать и как с её помощью спасать “потерянные” коммиты и ветки.


Что такое git reflog простыми словами

Git хранит не только коммиты, но и историю того, куда указывал HEAD и ветки в прошлом.

Эта история и есть reflog (reference log — журнал ссылок).

Если по-простому:

git reflog показывает, как со временем перемещались HEAD и ветки: какие коммиты были “последними” до всех ваших команд.

Ключевые моменты:

  • reflogлокальный.
    Его видно только в вашем репозитории, он не передаётся по git push.
  • В нём отражаются почти любые действия, которые двигают HEAD или ветки:
    • commit, commit --amend
    • merge
    • rebase
    • reset
    • checkout
    • pull, fetch + merge/rebase
  • По записям reflog можно:
    • найти “потерянный” коммит;
    • восстановить удалённую ветку;
    • откатить неудачный reset или rebase.

Базовое использование git reflog

Самый простой вариант:

git reflog

Пример вывода (упрощённый):

4f3c9a1 HEAD@{0}: commit: Fix login bug
a1b2c3d HEAD@{1}: commit: Add login form
9e8d7c6 HEAD@{2}: reset: moving to HEAD~1
123abcd HEAD@{3}: checkout: moving from main to feature/login
...

Разберём одну строку:

4f3c9a1 HEAD@{0}: commit: Fix login bug
  • 4f3c9a1 — сокращённый хеш коммита.
  • HEAD@{0} — текущая позиция HEAD (самое “свежее” состояние).
  • commit — тип действия.
  • Fix login bug — сообщение коммита или комментарий к действию.

Важно:

  • HEAD@{0} — это текущее состояние.
  • HEAD@{1} — что было перед этим.
  • HEAD@{2} — ещё раньше, и так далее.

Как работает синтаксис HEAD@{…} и веток

Git позволяет ссылаться на прошлые состояния с помощью специального синтаксиса.

HEAD@{N} — “как было N шагов назад”

Примеры:

git show HEAD@{1}
git show HEAD@{5}

Это покажет коммиты, на которые указывал HEAD 1 и 5 шагов назад.

Можно сделать reset на это состояние:

git reset --hard HEAD@{1}

Так можно отменить последний шаг, если он оказался неудачным.

HEAD@{<дата>} — “как было в момент времени”

Работают и такие варианты:

git show HEAD@{"yesterday"}
git show HEAD@{"2025-12-01 15:00:00"}

Git попытается найти состояние HEAD, актуальное на эту дату и время.

<ветка>@{N} — история конкретной ветки

Работает не только с HEAD, но и с ветками:

git reflog feature/login

Покажет reflog именно ветки feature/login.

Обращение к состоянию ветки:

git show feature/login@{2}

Это полезно, если в одной ветке происходило много rebase и reset.


Типичные сценарии использования git reflog

1. Откат неудачного git reset --hard

Один из самых частых случаев.

Сценарий:

  1. Сделали: git reset --hard HEAD~3
  2. Поняли, что удалили нужные коммиты из текущей ветки.
  3. Паника.

Что делать:

  1. Смотрим reflog: git reflog В списке будет строка перед reset, примерно так: a1b2c3d HEAD@{1}: reset: moving to HEAD~3 9e8d7c6 HEAD@{2}: commit: Add payment integration Нас интересует состояние до reset — как правило, это HEAD@{1} или HEAD@{2}, нужно посмотреть по контексту.
  2. Откатываемся обратно: git reset --hard HEAD@{1}

И всё: состояние ветки вернулось к тому виду, который был до неудачного reset.


2. Восстановление удалённой ветки

Сценарий:

git branch -D feature/login

Потом понимаем, что ветка ещё нужна.

Шаги:

  1. Смотрим reflog HEAD или main: git reflog Находим строку, где checkout переключался на feature/login
    или где видно нужный коммит по сообщению. Например: d4e5f6a HEAD@{7}: checkout: moving from main to feature/login Хеш d4e5f6a — это нужный коммит.
  2. Создаём ветку снова от этого коммита: git checkout -b feature/login d4e5f6a

Ветка восстановлена.


3. Восстановление коммита после git commit --amend

Сценарий:

  1. Был коммит A.
  2. Сделали git commit --amend → получили новый коммит B.
  3. Потом оказалось, что изменения из A всё-таки нужны.

В reflog это выглядит примерно так:

1111111 HEAD@{1}: commit (amend): Fix login bug
2222222 HEAD@{2}: commit: Fix login bug

Где:

  • 1111111 — новый коммит (после amend);
  • 2222222 — старый коммит (до amend).

Можно:

git show HEAD@{2}
# или
git checkout 2222222

Если нужно вернуть именно этот коммит в текущую ветку:

git cherry-pick 2222222

4. Поиск потерянных коммитов после git rebase

После rebase история переписывается: появляются новые коммиты с новыми хешами, старые как будто “пропадают”.

На самом деле старые коммиты ещё какое-то время живут в объектной базе и доступны через reflog.

Шаги:

  1. Смотрим: git reflog
  2. Ищем строку, где упомянут rebase: abcd123 HEAD@{3}: rebase finished: returning to refs/heads/feature/login efgh456 HEAD@{4}: rebase: checkout feature/login ijkl789 HEAD@{5}: commit: Old commit before rebase
  3. Нужный старый коммит можно:
    • посмотреть: git show ijkl789
    • вернуть в историю: git cherry-pick ijkl789

5. Вернуться к состоянию “как было полчаса назад”

Можно использовать время в ссылках reflog.

Пример:

git show HEAD@{"30 minutes ago"}

Если состояние устраивает, можно:

git reset --hard HEAD@{"30 minutes ago"}

Или, если нужен другой момент:

git show HEAD@{"2025-12-09 14:30:00"}

Рабочий алгоритм: что делать, если “всё пропало”

Можно запомнить простой чек-лист.

  1. Не паниковать. Не делать новых коммитов и gc.
    Пока вы не сделали лишних действий, Git проще “отмотать”.
  2. Посмотреть reflog: git reflog
  3. Найти строку до опасной команды (reset, rebase, commit --amend, branch -D, checkout).
  4. Определить нужное состояние:
    • по хешу;
    • по сообщению коммита;
    • по описанию действия.
  5. Вернуться:
    • через git reset --hard <hash> — если хотите переписать ветку;
    • через git checkout -b <branch> <hash> — если хотите создать новую ветку от того состояния;
    • через git cherry-pick <hash> — если нужно вернуть только один коммит.

Ограничения и настройки reflog

Reflog — не вечный

Записи в reflog:

  • хранятся ограниченное время;
  • со временем Git их удаляет.

По умолчанию (если настройки не менялись):

  • достижимые объекты (на которые ведут ветки и теги) — около 90 дней;
  • недостижимые — около 30 дней.

Эти значения можно настроить в .gitconfig:

[gc]
  reflogExpire = 90 days
  reflogExpireUnreachable = 30 days

Но важно помнить: reflog — не архив навсегда. Часто он выручает, но полагаться на него как на полноценный бэкап нельзя.

Reflog локален

Каждый локальный репозиторий имеет свой reflog:

  • ваш;
  • у коллеги;
  • на CI-сервере;
  • на удалённой машине.

Если вы удалили ветку на своей машине, на удалённом сервере её reflog может быть другим.
Через обычный git reflog удалённого репозитория не посмотреть.


Полезные команды рядом с git reflog

git show с reflog-ссылками

Посмотреть состояние в прошлый момент:

git show HEAD@{1}
git show main@{2}

git diff между состояниями

Посмотреть, что изменилось между двумя шагами:

git diff HEAD@{2} HEAD@{0}

Можно увидеть, что именно вы сделали за последние 2 шага.

git log -g — лог по reflog

Похож на обычный git log, но использует историю reflog:

git log -g

Можно сочетать с фильтрами:

git log -g --oneline --grep="login"

Так проще искать “потерянный” коммит по сообщению.


Заключение

git reflog — это “чёрный ящик” Git.
Он записывает, куда указывал HEAD и ветки после ваших действий.

Пока этот журнал жив:

  • можно отменить неудачный git reset --hard;
  • вернуть ветку после git branch -D;
  • достать старый коммит после commit --amend или rebase;
  • откатиться к состоянию “как было час назад”.

Главное — помнить несколько вещей:

  • почти всегда “потерянный” коммит можно найти в reflog;
  • git reflog — локальный инструмент, он не заменяет бэкапы и не живёт вечно;
  • чем меньше лишних действий после ошибки, тем проще всё восстановить.

Полезная привычка для разработчика:
как только кажется, что “всё пропало”, первым делом набрать:

git reflog

И очень часто оказывается, что всё ещё можно исправить одной-двумя командами.