Das Reflog als Rettungsanker bei verlorenen Commits
git
HEAD
Git · Reflog · Commit-Recovery · Versionskontrolle
Das Reflog als Rettungsanker bei verlorenen Commits
Wie ihr gelöschte Commits nach reset und Rebase zurückholt

Ein unbedachter git reset --hard oder ein missglückter Rebase fühlt sich nach einem endgültigen Verlust an, ist es aber selten. Das Reflog protokolliert lokal jede Bewegung von HEAD und macht verschwundene Commits meist innerhalb weniger Minuten wieder auffindbar. Dieser Artikel zeigt, wie das Reflog funktioniert, wie lange Einträge erhalten bleiben und wie eine saubere Recovery in der Praxis abläuft.

12 Min. Lesezeit Reflog · HEAD@{n} · ORIG_HEAD Git Recovery · Commit-Wiederherstellung

1. Was das Reflog wirklich ist: Jede HEAD-Bewegung im Blick

Das Reflog ist Gits eingebautes Bewegungsprotokoll für Referenzen wie HEAD und Branch-Zeiger. Anders als die Commit-Historie, die nur die Abstammungslinie der aktuell erreichbaren Commits zeigt, protokolliert das Reflog jede einzelne Bewegung von HEAD chronologisch, unabhängig davon, ob der Zielcommit danach noch über einen Branch erreichbar ist. Jeder git commit, git checkout, git merge, git rebase, git cherry-pick und sogar jeder git reset hinterlässt einen eigenen Eintrag mit Zeitstempel und einer kurzen Beschreibung der Aktion.

Genau diese lückenlose Protokollierung macht das Reflog zum wichtigsten Sicherheitsnetz bei Git. Während ein git reset --hard den Branch-Zeiger sofort auf einen anderen Commit verschiebt und die vorherige Position aus der sichtbaren Historie verschwindet, bleibt der Eintrag im Reflog erhalten. Das Reflog kennt also nicht nur den aktuellen Zustand eines Branches, sondern jeden Zustand, den er in der jüngeren Vergangenheit hatte, und genau darauf basiert die gesamte Recovery-Strategie in diesem Artikel.

2. Reflog vs. Commit-Historie: Lokal, flüchtig und unverzichtbar

Der entscheidende Unterschied zwischen git log und git reflog liegt in der Datenquelle. git log läuft entlang der Eltern-Zeiger der Commit-Objekte und zeigt damit den Abstammungsgraphen, der beim Klonen, Pushen und Fetchen zwischen Repositories geteilt wird. Das Reflog dagegen ist reines lokales Metadaten: Es existiert ausschließlich als Datei im Verzeichnis .git/logs/, etwa .git/logs/HEAD für die HEAD-Referenz und .git/logs/refs/heads/<branch> für einzelne Branches.

Diese lokale Natur hat zwei wichtige Konsequenzen. Erstens wird das Reflog niemals mit git push übertragen und niemals mit git clone kopiert. Ein Kollege, der euer Repository klont, sieht euer Reflog nicht und kann es folglich auch nicht für eine Recovery nutzen. Zweitens ist das Reflog nicht dauerhaft: Es unterliegt einer Ablaufzeit, die in Abschnitt 8 im Detail erklärt wird. Wer verlorene Commits retten will, muss also lokal und innerhalb eines begrenzten Zeitfensters handeln, nicht Wochen später auf einer neuen Maschine.

3. Das Reflog-Format lesen: HEAD@{n}, ORIG_HEAD und Zeitstempel

Jede Zeile im Reflog folgt demselben Muster: eine gekürzte SHA, gefolgt von der Referenz und einem Index in geschweiften Klammern, etwa HEAD@{0} für den aktuellsten Eintrag, HEAD@{1} für den vorherigen Zustand von HEAD und so weiter. Diese Indizes sind relativ und ändern sich mit jeder neuen Aktion, weshalb ein Eintrag, den man retten möchte, am besten sofort mit seiner SHA notiert oder direkt weiterverarbeitet wird, statt sich auf eine später möglicherweise verschobene Nummer zu verlassen.

Neben dem numerischen Index versteht Git auch relative Zeitangaben wie HEAD@{2.hours.ago} oder HEAD@{yesterday}, was besonders hilfreich ist, wenn man den ungefähren Zeitpunkt eines Fehlers kennt, aber nicht die genaue Anzahl der Schritte. Zusätzlich setzt Git vor jedem reset, merge, rebase und am automatisch die spezielle Referenz ORIG_HEAD auf die vorherige Position von HEAD. ORIG_HEAD ist damit ein einstufiges Sicherheitsnetz, das ganz ohne Reflog-Lesen funktioniert, aber eben nur den letzten Schritt zurückgeht.


$ git reflog
a1b2c3d (HEAD -> feature/checkout) HEAD@{0}: commit: Add express checkout button
e4f5a6b HEAD@{1}: commit: Refactor payment step validation
9c8d7e6 HEAD@{2}: checkout: moving from main to feature/checkout
3f2e1d0 (main) HEAD@{3}: commit: Fix currency rounding in cart totals
7b6a5c4 HEAD@{4}: reset: moving to HEAD~2
d0e9f8a HEAD@{5}: commit: WIP tax calculation
b3c2d1e HEAD@{6}: commit: Initial tax calculation draft

# Time-based reference instead of an index
$ git show HEAD@{2.hours.ago}

# ORIG_HEAD points to HEAD before the last reset/rebase/merge
$ git log -1 ORIG_HEAD

4. Der Klassiker: Commit nach git reset --hard wiederherstellen

git reset --hard ist der häufigste Auslöser für scheinbar verlorene Commits. Der Befehl verschiebt den Branch-Zeiger auf den angegebenen Commit und überschreibt sowohl den Index als auch das Arbeitsverzeichnis, ohne Rückfrage. Wichtig zu verstehen: reset --hard löscht keine Commit-Objekte aus der Objektdatenbank. Die vorherigen Commits werden lediglich unerreichbar, weil kein Branch, Tag oder anderer Zeiger mehr direkt auf sie verweist, sie bleiben aber als sogenannte "dangling commits" im Objektspeicher liegen, bis sie irgendwann von der Garbage Collection entfernt werden.

Genau hier setzt die Recovery an: Der Reflog-Eintrag unmittelbar vor dem Reset zeigt exakt auf die alte Position des Branches. Ein einfaches git reset --hard HEAD@{1} stellt den vorherigen Zustand vollständig wieder her, inklusive aller Commits, des Index und des Arbeitsverzeichnisses zum damaligen Zeitpunkt. Wer unsicher ist, ob der gefundene Eintrag wirklich der richtige ist, sollte vorher mit git show oder git log HEAD@{1} gegenprüfen, bevor der nächste harte Reset erneut Daten überschreibt.


# Working tree before the mistake: 3 commits on top of main
$ git log --oneline -4
c4d5e6f (HEAD -> feature/pricing) Add tiered discount logic
b3c4d5e Fix rounding edge case
a2b3c4d Add unit tests for discount calculator
9f8e7d6 (main) Merge pull request #142

# Accidental hard reset, discarding all three commits
$ git reset --hard HEAD~3
HEAD is now at 9f8e7d6 Merge pull request #142

# The commits are gone from the branch, but not from the object database
$ git reflog
9f8e7d6 (HEAD -> feature/pricing) HEAD@{0}: reset: moving to HEAD~3
c4d5e6f HEAD@{1}: commit: Add tiered discount logic
b3c4d5e HEAD@{2}: commit: Fix rounding edge case
a2b3c4d HEAD@{3}: commit: Add unit tests for discount calculator

# Restore the branch to the state right before the reset
$ git reset --hard HEAD@{1}
HEAD is now at c4d5e6f Add tiered discount logic

5. Rebase geht schief: verlorene Commits nach interaktivem Rebase retten

Ein interaktiver Rebase mit git rebase -i schreibt Commits um und erzeugt dabei neue SHAs, selbst wenn sich der Inhalt kaum ändert. Wird beim Squashen, Reordern oder Droppen ein Commit versehentlich verworfen oder eine falsche Reihenfolge gewählt, wirken die ursprünglichen Commits aus Sicht von git log komplett verschwunden. Tatsächlich erzeugt jeder einzelne Schritt eines Rebase einen eigenen Reflog-Eintrag: rebase (start) markiert den Beginn, rebase (pick) und rebase (squash) jeden verarbeiteten Commit, und rebase (finish) den Abschluss.

Für die Recovery bedeutet das: Im Reflog lässt sich der komplette Rebase-Ablauf Schritt für Schritt nachvollziehen, einschließlich der Zwischenzustände, die im finalen Ergebnis gar nicht mehr sichtbar sind. Ein verworfener Commit lässt sich gezielt mit git cherry-pick <sha> auf den aktuellen Branch übertragen, ohne den gesamten Rebase rückgängig zu machen. Soll dagegen der komplette Zustand vor dem Rebase wiederhergestellt werden, reicht git reset --hard ORIG_HEAD, solange seitdem kein weiterer Reset oder Rebase stattgefunden hat, der ORIG_HEAD überschrieben hätte.


# Interactive rebase squashes and reorders 4 commits into 2
$ git rebase -i HEAD~4

# After the rebase, git log only shows the rewritten history
$ git log --oneline -2
7a8b9c0 (HEAD -> feature/api-client) Add retry logic and tests
5d6e7f8 (main) Implement base API client

# The reflog still holds every intermediate step of the rebase
$ git reflog
7a8b9c0 HEAD@{0}: rebase (finish): returning to refs/heads/feature/api-client
7a8b9c0 HEAD@{1}: rebase (pick): Add retry logic and tests
1e2f3a4 HEAD@{2}: rebase (squash): Add retry logic and tests
9b8c7d6 HEAD@{3}: rebase (start): checkout HEAD~4
c3d4e5f HEAD@{4}: commit: Add retry configuration constants

# Recover the commit that was dropped during the squash
$ git cherry-pick c3d4e5f

6. Gelöschte Branches und verwaiste Commits wiederfinden

git branch -D löscht ausschließlich den Branch-Zeiger, niemals die zugehörigen Commit-Objekte. Wurde der gelöschte Branch zuvor mindestens einmal ausgecheckt, finden sich seine Commits im Reflog von HEAD, konkret in den checkout-Einträgen, die den Wechsel auf und von diesem Branch dokumentieren. Wichtig ist dabei eine Einschränkung: Das Löschen eines Branches entfernt auch dessen eigene Reflog-Datei unter .git/logs/refs/heads/<branch>, weshalb git reflog show <branchname> nach dem Löschen nicht mehr funktioniert. Das globale HEAD-Reflog bleibt davon jedoch unberührt.

Die Wiederherstellung läuft in zwei Schritten ab: Zuerst wird über git reflog, kombinierbar mit grep nach dem Branch-Namen, die letzte bekannte SHA des gelöschten Branches ermittelt. Danach erzeugt git branch <neuer-name> <sha> einen neuen Branch-Zeiger genau an dieser Stelle, ohne dass ein einziger Commit neu geschrieben werden muss. Wurde der Branch dagegen nie lokal ausgecheckt, etwa weil er nur über git fetch bekannt war, hilft nur die Objektdatenbank direkt über git fsck --lost-found, was deutlich unzuverlässiger ist als das Reflog.


# Accidental branch deletion before merging
$ git branch -D hotfix/payment-timeout
Deleted branch hotfix/payment-timeout (was 4f5e6d7).

# The branch ref is gone, but HEAD's reflog still knows the tip commit
$ git reflog | grep hotfix
4f5e6d7 HEAD@{2}: checkout: moving from hotfix/payment-timeout to main

# Recreate the branch pointing at the last known commit
$ git branch recovery/hotfix-payment-timeout 4f5e6d7
$ git checkout recovery/hotfix-payment-timeout

7. Praktischer Recovery-Workflow: reflog, Diff-Prüfung, Wiederherstellung

Ein zuverlässiger Recovery-Workflow folgt immer demselben Muster, unabhängig von der konkreten Fehlerursache. Erster Schritt ist git reflog beziehungsweise das ausführlichere git reflog show HEAD, um die chronologische Liste aller HEAD-Bewegungen zu sehen. Zweiter Schritt ist die Identifikation des richtigen Eintrags anhand von Zeitstempel und Aktionsbeschreibung, etwa reset: moving to HEAD~3 oder rebase (start), die schon im Klartext verraten, was passiert ist.

Vor jeder destruktiven Aktion sollte der gefundene Commit mit git show <sha> oder git log -p -1 <sha> geprüft werden, um sicherzustellen, dass es wirklich der gesuchte Zustand ist. Erst danach folgt die eigentliche Wiederherstellung, und hier gibt es drei gängige Wege: git reset --hard HEAD@{n} für eine vollständige Rückkehr zu diesem Zustand, git cherry-pick <sha> für die Übernahme eines einzelnen Commits in die aktuelle Historie, oder als sicherste Variante git branch recovery-branch <sha>, das den gefundenen Zustand als neuen Branch sichert, ohne den aktuellen Branch überhaupt anzufassen.

8. Grenzen und Ablauf: reflog-Expiry und gc.reflogExpire konfigurieren

Das Reflog ist kein dauerhaftes Archiv. Git unterscheidet zwischen zwei Ablaufzeiten: Reflog-Einträge, die noch über einen Branch oder Tag erreichbar sind, verfallen standardmäßig nach 90 Tagen, gesteuert über gc.reflogExpire. Einträge, die zu keinem erreichbaren Commit mehr gehören, sogenannte unreachable Entries, etwa nach einem überschriebenen Reset oder Rebase, verfallen bereits nach 30 Tagen über gc.reflogExpireUnreachable. Nach Ablauf dieser Fristen entfernt git gc, das regelmäßig automatisch im Hintergrund läuft, sowohl den Reflog-Eintrag als auch das zugehörige Objekt endgültig aus dem Repository.

Für die Praxis bedeutet das ein reales, aber großzügiges Zeitfenster: Die meisten verlorenen Commits lassen sich problemlos wiederherstellen, solange seit dem Fehler nicht mehr als 30 Tage vergangen sind. Wer dieses Fenster bewusst verlängern möchte, etwa in einem Repository mit besonders wertvoller Historie, kann beide Werte in der lokalen oder globalen .git/config anpassen. Ein Wert von never deaktiviert den Ablauf komplett, was allerdings die Repository-Größe über Zeit spürbar wachsen lässt.


[gc]
    ; Reflog entries still reachable from a ref expire after 90 days by default
    reflogExpire = 90.days.ago
    ; Entries no longer reachable from any ref expire after 30 days by default
    reflogExpireUnreachable = 30.days.ago
    auto = 6700

[core]
    ; Keep an explicit safety margin before any automatic pruning
    logAllRefUpdates = true

9. Reflog im Vergleich: riskante vs. sichere Recovery-Strategien

Die folgende Übersicht stellt für fünf typische Fehlerszenarien den riskanten, oft aus Panik gewählten Reflex einer sicheren, reflog-basierten Vorgehensweise gegenüber.

Szenario Riskanter Reflex Sichere Vorgehensweise
Commit nach reset --hard verloren git log durchsuchen, zeigt nur die aktuelle Historie git reflog show HEAD, dann reset --hard HEAD@{n}
Branch versehentlich gelöscht Branch als endgültig verloren abschreiben git reflog + git branch <name> <sha>
Falscher amend überschreibt Commit Force-Push versuchen, um es zu "reparieren" git reflog, alten Commit per cherry-pick zurückholen
Rebase mit falschem Ergebnis Commits manuell von Hand neu schreiben git reset --hard ORIG_HEAD oder HEAD@{n}
Force-Push überschreibt Remote-Branch Hoffen, dass ein Kollege eine lokale Kopie hat Eigenes Reflog prüfen, künftig push --force-with-lease

In allen fünf Fällen gilt dieselbe Grundregel: Vor jeder weiteren destruktiven Aktion zuerst das Reflog prüfen, den passenden Zustand identifizieren und erst danach handeln. Wer stattdessen sofort mit einem weiteren reset --hard oder Force-Push reagiert, riskiert, dass der eigentlich rettbare Zustand überschrieben wird, bevor er wiederhergestellt werden konnte.

Mironsoft

Git-Workflows, Recovery-Playbooks und CI/CD-Absicherung für Magento- und Hyvä-Teams

Git-Workflows, die eurem Team Sicherheit geben?

Wir richten robuste Git-Branching-Strategien, Pre-Commit-Hooks und CI/CD-Absicherungen für eure Magento- und Hyvä-Projekte ein, damit ein einzelner Fehlgriff nie zum Datenverlust wird.

Git-Workflow-Audit

Branching-Strategie, Hook-Konfiguration und Recovery-Playbooks für euer Team

Onboarding & Schulung

Praxisnahe Git-Trainings zu Reflog, Rebase und sicherem Force-Push

CI/CD-Absicherung

Automatisierte Backups, geschützte Branches und Force-Push-Regeln in der Pipeline

10. Zusammenfassung

Das Reflog löst ein Kernproblem im Git-Alltag: Der Unterschied zwischen einem echten Datenverlust und einem schnell behebbaren Fehler liegt fast immer im Wissen um git reflog. Jede Bewegung von HEAD wird lokal protokolliert, unabhängig davon, ob der Zielcommit danach noch über einen Branch erreichbar ist. Ein git reset --hard, ein misslungener interaktiver Rebase oder ein versehentlich gelöschter Branch sind damit selten endgültig, solange man innerhalb des Ablauffensters von 90 beziehungsweise 30 Tagen reagiert.

Der entscheidende Unterschied zwischen einer stressfreien und einer chaotischen Recovery liegt in der Reihenfolge: erst git reflog lesen, den richtigen Eintrag mit git show verifizieren, und erst danach mit reset --hard, cherry-pick oder einem neuen Recovery-Branch handeln. Wer diese Reihenfolge einhält und die Reflog-Ablaufzeiten kennt, verliert in der Praxis so gut wie nie einen Commit unwiederbringlich.

Das Reflog als Rettungsanker - Das Wichtigste auf einen Blick

Reflog verstehen

Protokolliert jede HEAD-Bewegung lokal, unabhängig von der sichtbaren Commit-Historie in git log.

Nach reset --hard

git reflog zeigt die vorherige Position, git reset --hard HEAD@{1} stellt sie vollständig wieder her.

Nach Rebase

Jeder Rebase-Schritt hinterlässt einen Eintrag. cherry-pick oder reset --hard ORIG_HEAD retten den alten Zustand.

Ablaufzeit beachten

90 Tage für erreichbare, 30 Tage für unreachable Einträge, konfigurierbar über gc.reflogExpire.

11. FAQ: Das Reflog als Rettungsanker bei verlorenen Commits

1Was ist der Unterschied zwischen git reflog und git log?
git log zeigt die Abstammungslinie erreichbarer Commits und wird beim Klonen und Pushen geteilt. git reflog protokolliert chronologisch jede Bewegung von HEAD lokal, existiert nur in .git/logs und wird niemals übertragen.
2Wie lange bleiben Reflog-Einträge standardmäßig erhalten?
Erreichbare Einträge verfallen nach 90 Tagen über gc.reflogExpire, nicht mehr erreichbare bereits nach 30 Tagen über gc.reflogExpireUnreachable. Danach entfernt git gc Eintrag und Objekt endgültig.
3Kann ich einen Commit nach git reset --hard wirklich wiederherstellen?
In den allermeisten Fällen ja. reset --hard löscht keine Commit-Objekte, sondern verschiebt nur den Branch-Zeiger. Der vorherige Zustand lässt sich über git reflog finden und mit reset --hard HEAD@{1} wiederherstellen.
4Was ist ORIG_HEAD und wann wird es gesetzt?
Eine spezielle Referenz, die Git automatisch vor jedem reset, merge, rebase und am auf die vorherige Position von HEAD setzt. Einstufiges Sicherheitsnetz, geht aber nur einen Schritt zurück.
5Funktioniert die Reflog-Recovery auch nach einem gelöschten Branch?
Ja, sofern der Branch vorher mindestens einmal ausgecheckt wurde. Seine Commits finden sich in den checkout-Einträgen des HEAD-Reflogs und lassen sich mit git branch <name> <sha> wiederherstellen.
6Wird das Reflog beim Pushen an ein Remote-Repository mit übertragen?
Nein. Das Reflog ist reine lokale Metadaten im eigenen .git-Verzeichnis. Es wird weder gepusht noch geklont noch gefetcht, Recovery funktioniert daher nur auf der Maschine des Fehlers.
7Was bedeutet eine Zeitangabe wie HEAD@{2.hours.ago}?
Git akzeptiert statt eines Index auch relative Zeitangaben. HEAD@{2.hours.ago} referenziert den Stand von HEAD vor zwei Stunden, hilfreich bei bekanntem Fehlerzeitpunkt ohne genaue Schrittzahl.
8Wie ändere ich die Standard-Ablaufzeit des Reflogs?
Über gc.reflogExpire und gc.reflogExpireUnreachable in der lokalen oder globalen .git/config, etwa git config gc.reflogExpireUnreachable 90.days.ago. Der Wert never deaktiviert den Ablauf vollständig.
9Was ist der Unterschied zwischen git reset --hard HEAD@{n} und git cherry-pick?
reset --hard HEAD@{n} setzt den kompletten Branch inklusive Arbeitsverzeichnis zurück. git cherry-pick überträgt nur einen einzelnen Commit als neue Änderung, ohne die übrige Historie zu verändern.
10Was passiert, wenn ein Commit bereits aus dem Reflog gefallen ist?
Dann bleibt nur git fsck --lost-found, das verwaiste, noch nicht entfernte Objekte sucht. Deutlich unzuverlässiger als das Reflog und nur nutzbar, solange git gc noch nicht bereinigt hat.