git reflog: Verlorene Commits und Branches wiederherstellen
git reflog ist ein lokales Protokoll, in dem Git jede Bewegung von HEAD und Branches festhält: Commits, reset, checkout, rebase, merge. Suchen Sie in git reflog den Eintrag vor dem Fehler, etwa HEAD@{2}, und stellen Sie den Zustand mit git reset --hard HEAD@{2} wieder her oder legen Sie mit git branch rescue HEAD@{2} einen Branch an.
git reflog ist das Referenz-Log (reference log): Git trägt darin jede Bewegung von HEAD und den Branch-Spitzen ein. Commit gemacht, Branch gewechselt, reset, rebase oder merge ausgeführt – im Reflog erscheint eine Zeile mit dem Hash, auf den die Referenz nach der Operation zeigte. Deshalb lässt sich selbst ein Commit, auf den kein Branch mehr verweist, finden und zurückholen.
Das Wichtigste vorab: Das Reflog ist lokal. Es existiert nur in Ihrem Klon, wird nicht auf den Server gepusht und kommt bei git clone nicht mit. Dafür rettet es Sie im eigenen Repository vor fast jedem Fehler mit der Historie.
Die Ausgabe von git reflog lesen
git reflog
3c4d5e6 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
9a8b7c6 HEAD@{1}: commit: E-Mail-Validierung hinzufügen
1f2e3d4 HEAD@{2}: commit: Konfiguration in eigene Datei auslagern
3c4d5e6 (HEAD -> main) HEAD@{3}: checkout: moving from feature to main
7b6a5c4 (feature) HEAD@{4}: commit: Entwurf Login-Formular
Jede Zeile ist ein Zustand von HEAD, der neueste steht oben:
- Hash – der Commit, auf den HEAD nach der Operation zeigte;
- HEAD@{n} – „wo HEAD vor n Bewegungen war“; das ist eine vollwertige Referenz, die Sie an beliebige Befehle übergeben können;
- Beschreibung – Art der Operation (commit, reset, checkout, rebase, merge, pull, cherry-pick, commit (amend)) und Details.
Im Beispiel sieht man: Zwei Commits – „Konfiguration auslagern“ und „Validierung hinzufügen“ – wurden gemacht, danach hat reset HEAD~2 den Branch zurückgesetzt. In git log tauchen sie nicht mehr auf, im Reflog sind sie noch da.
Reflog eines bestimmten Branches
Jeder Branch hat sein eigenes Log. Es ist kürzer und nützlicher, wenn Sie oft zwischen Branches gewechselt haben:
git reflog show feature
git reflog show origin/main # wie sich der Remote-Branch bei fetch verändert hat
Zeitbasierte Referenzen
Statt einer Nummer können Sie einen Zeitpunkt angeben – Git findet heraus, wohin die Referenz damals zeigte:
git show main@{yesterday}
git diff main@{"2 hours ago"} main
git log -g --since="1 day ago" --oneline
Die Option -g (--walk-reflogs) von git log läuft das Reflog entlang statt der Elternbeziehungen und erlaubt die gewohnten Formate und Filter. Zur Anzeige von Datumsangaben eignet sich git reflog --date=iso.
Commits nach git reset --hard wiederherstellen
Die häufigste Situation: Sie haben git reset --hard auf den falschen Commit ausgeführt, und die Arbeit ist „weg“. Tatsächlich sind die Commits nicht verschwunden – sie sind nur von keinem Branch aus mehr sichtbar.
- Öffnen Sie das Log:
git reflog. - Suchen Sie die Zeile direkt vor
reset: moving to ...– das ist der Zustand vor dem Fehler. Im Beispiel oben ist dasHEAD@{1}mit dem Hash9a8b7c6. - Prüfen Sie, ob es der richtige ist:
git log --oneline -5 HEAD@{1}. - Setzen Sie den Branch zurück:
git reset --hard HEAD@{1}.
git reset --hard HEAD@{1}
HEAD is now at 9a8b7c6 E-Mail-Validierung hinzufügen
Vorsicht: Nach jeder Operation verschieben sich die Nummern. Was eben HEAD@{1} war, ist nach dem reset HEAD@{2}. Zuverlässiger ist es daher, den Hash zu kopieren statt der Nummer.
Wichtige Einschränkung: Das Reflog kennt nur Committetes. Nicht committete Änderungen, die mit reset --hard oder git checkout -- datei gelöscht wurden, holt das Reflog nicht zurück. Ein wenig Hoffnung bleibt, wenn die Dateien bereits im Index waren – dazu weiter unten.
Einen gelöschten Branch wiederherstellen
Sie haben einen Branch mit git branch -D feature gelöscht, und darin lagen noch nicht gepushte Commits. Das Log des Branches selbst wird mit ihm gelöscht, das Log von HEAD bleibt aber – und HEAD stand auf seinen Commits, als Sie darin gearbeitet haben.
git reflog | grep feature
7b6a5c4 HEAD@{4}: commit: Entwurf Login-Formular
3c4d5e6 HEAD@{3}: checkout: moving from feature to main
# Der letzte Commit des Branches ist der, von dem aus Sie ihn verlassen haben
git branch feature 7b6a5c4
Suchen Sie die Zeile checkout: moving from feature to ...: Der Hash im vorhergehenden Eintrag (weiter unten in der Liste) ist der letzte Zustand des Branches. Übrigens gibt Git den Hash beim Löschen selbst aus: Deleted branch feature (was 7b6a5c4). – ist das Terminal noch offen, genügt der.
Ein missglücktes Rebase rückgängig machen
Rebase schreibt Commits um, und wenn Sie dabei Konflikte falsch gelöst haben, kann das Ergebnis schlechter sein als vorher. Das Reflog bewahrt den Zustand vor Beginn des Rebase.
git reflog
e1f2a3b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e1f2a3b HEAD@{1}: rebase (pick): Entwurf Login-Formular
c4d5e6f HEAD@{2}: rebase (start): checkout main
7b6a5c4 HEAD@{3}: commit: Entwurf Login-Formular
git reset --hard HEAD@{3}
Sie brauchen den Eintrag vor rebase (start). Es gibt auch eine Abkürzung: Vor riskanten Operationen (rebase, merge, reset) speichert Git die vorherige Position in der Referenz ORIG_HEAD, sodass direkt nach dem Rebase git reset --hard ORIG_HEAD funktioniert. ORIG_HEAD wird aber von der nächsten solchen Operation überschrieben, das Reflog hingegen bewahrt die gesamte Historie.
Commit vor git commit --amend zurückholen
--amend verändert einen Commit nicht, sondern erzeugt an seiner Stelle einen neuen. Der alte bleibt im Reflog:
git reflog
5a6b7c8 HEAD@{0}: commit (amend): E-Mail-Validierung hinzufügen
9a8b7c6 HEAD@{1}: commit: E-Mail-Validierung hinzufügen
git diff HEAD@{1} HEAD # was amend geändert hat
git reset --soft HEAD@{1} # alten Commit zurückholen, die amend-Änderungen bleiben im Index
Sicher wiederherstellen: Branch statt reset
Wenn Sie nicht sicher sind, welcher Commit der richtige ist, führen Sie reset --hard nicht blind aus. Legen Sie einen Rettungs-Branch an – er macht nichts kaputt und schützt den gefundenen Commit vor dem Löschen:
git branch rescue HEAD@{5}
git log --oneline rescue
git switch rescue
Anschließend können Sie die Commits aus rescue per cherry-pick übernehmen oder mergen und den Branch löschen, sobald alles wieder an seinem Platz ist.
Wie lange wird das Reflog aufbewahrt?
Einträge halten nicht ewig. Aufgeräumt werden sie von git gc, das Git regelmäßig selbst startet. Die Fristen bestimmen diese Einstellungen:
| Einstellung | Standard | Bedeutung |
|---|---|---|
gc.reflogExpire | 90 Tage | Frist für Einträge mit Commits, die von aktuellen Branches aus erreichbar sind |
gc.reflogExpireUnreachable | 30 Tage | Frist für Einträge mit Commits, die von keinem Branch aus erreichbar sind – also genau die „verlorenen“ |
gc.pruneExpire | 2 Wochen | Nach welcher Zeit unerreichbare Objekte selbst gelöscht werden, auf die nichts mehr verweist |
Praktische Konsequenz: Ein verlorener Commit ist über das Reflog garantiert etwa einen Monat lang erreichbar. Schieben Sie die Wiederherstellung nicht auf und führen Sie weder git gc --prune=now noch git reflog expire --expire=now --all manuell aus, bevor Sie das Gesuchte zurückgeholt haben.
Wenn das Reflog nicht hilft: git fsck
Manchmal steht ein Commit nicht im Reflog: etwa wenn ihn git stash erzeugt hat und der Stash danach mit git stash drop gelöscht wurde, oder wenn die Änderungen nur im Index lagen. Dann suchen Sie nach „hängenden“ (dangling) Objekten:
git fsck --lost-found
dangling commit 2b3c4d5e6f...
dangling blob 8e9f0a1b2c...
git show 2b3c4d5e6f # Inhalt ansehen
git branch recovered 2b3c4d5e6f
Hängende Commits aus einem gelöschten Stash tragen die Nachricht „WIP on ...“. Hängende Blobs sind Dateiinhalte, die irgendwann per git add hinzugefügt, aber nie committet wurden. Einen Dateinamen haben sie nicht, Sie müssen sie am Inhalt erkennen – dafür lässt sich so manchmal Arbeit nach einem reset --hard retten. Mit der Option --lost-found kopiert Git sie zusätzlich nach .git/lost-found/.
Was das Reflog nicht kann
- Es funktioniert nicht rechnerübergreifend. Hat ein Kollege per Force-Push Ihre Commits auf dem Server überschrieben, suchen Sie sie im Reflog von jemandem, der sie lokal hatte – etwa per
git reflog show origin/mainbei jedem, der vor dem Überschreiben ein fetch gemacht hat. - Es sieht nichts, was nicht committet ist. Die beste Absicherung vor einer riskanten Operation ist deshalb ein Commit oder
git stash. - Es speichert die Historie nicht ewig. Die Fristen stehen oben, außerdem beginnt ein frischer Klon mit einem leeren Log.
Praxis: Absicherung vor riskanten Operationen
Das Reflog rettet Sie, aber die Wiederherstellung damit erfordert Sorgfalt: Sie müssen unter Dutzenden ähnlicher Einträge den richtigen finden. Viel einfacher ist es, vorher eine Markierung zu setzen. Legen Sie vor einem Rebase, einem größeren reset oder Experimenten mit der Historie einen temporären Branch oder Tag an:
git branch backup/before-rebase
git rebase -i main
# etwas ist schiefgelaufen
git reset --hard backup/before-rebase
# alles hat geklappt
git branch -D backup/before-rebase
Das kostet einen Befehl und erspart die Suche nach dem Hash. Das Reflog bleibt die Notlösung für den Fall, dass Ihnen die Absicherung zu spät eingefallen ist.
Den richtigen Eintrag schnell finden
In einem langen Log hilft Filtern. Suchen Sie nach Art der Operation oder nach dem Text der Commit-Nachricht und sehen Sie sich in Zweifelsfällen den Inhalt an:
git reflog | grep -E "reset|rebase \(start\)"
git log -g --grep="Validierung" --oneline
git show --stat HEAD@{7}
git log -g mit --grep durchsucht die Nachrichten der Commits im Reflog, und git show --stat zeigt, welche Dateien betroffen sind – daran erkennen Sie den gesuchten Commit leicht, selbst wenn die Nachricht so nichtssagend war wie „wip“.
Fazit
git reflog ist das Sicherheitsnetz von Git: Alles, was in den letzten Wochen in Ihrem Repository committet wurde, lässt sich finden und zurückholen. Das Vorgehen ist immer gleich: Log öffnen, den Eintrag vor dem Fehler suchen, ihn mit git log oder git show prüfen und dann einen Branch anlegen oder reset --hard ausführen. Die Arbeit mit restore, reset, revert und das Wiederherstellen nach Fehlern werden im Git-Kurs praktisch geübt.
Häufige Fragen
Was ist der Unterschied zwischen git reflog und git log?
git log folgt den Elternbeziehungen der Commits und zeigt die Historie eines Branches. git reflog zeigt, wohin sich HEAD und die Branches in Ihrem Repository bewegt haben, einschließlich Commits, die in keiner Branch-Historie mehr vorkommen.
Landet das Reflog auf GitHub oder GitLab?
Nein, das Reflog liegt nur im lokalen Ordner .git/logs und wird weder bei push noch bei clone übertragen.
Kann man das Reflog leeren?
Ja, mit git reflog expire --expire=now --all und anschließendem git gc --prune=now – danach lassen sich verlorene Commits aber nicht mehr wiederherstellen.
Wo liegt das Reflog physisch?
In den Textdateien .git/logs/HEAD und .git/logs/refs/heads/branch-name, mit einer Zeile pro Bewegung.
Funktioniert das Reflog in einem Bare-Repository auf dem Server?
Standardmäßig ist das Reflog in Bare-Repositories deaktiviert (core.logAllRefUpdates), auf dem Server können Sie sich also nicht darauf verlassen.
Dieses Thema wird im Kurs „Git vom Anfänger zum versierten Entwickler“ ausführlich mit Praxis behandelt.
Zum Kurs