Stellen Sie Dateien wieder her, die zum Index hinzugefügt, dann aber durch einen Git-Reset entfernt wurden

Ich habe einige Dateien zum Index hinzugefügt, aber dann habe ich sie versehentlich mit git reset --hard . Wie kann ich sie wiederherstellen? Hier ist was passiert ist:

  1. Ich habe alle Dateien mit git add . hinzugefügt git add .
  2. Ich habe mich dann verpflichtet
  3. Als ich den Status überprüfte, gab es immer noch Dateien, die nicht vom Commit aus dem Add enthalten waren, was seltsam war
  4. Ich habe die nicht aufgezeichneten Dateien erneut hinzugefügt und es funktionierte dieses Mal
  5. Aber ich wollte, dass alles in einem einzigen Commit war, also habe ich nachgeschaut, wie ich das, was ich gerade begangen habe, aufheben kann
  6. Ich habe git reset --hard HEAD^ – schlechte Idee offensichtlich, alle Dateien wurden gelöscht
  7. also habe ich git reflog um herauszufinden, wo ich git reflog habe
  8. Dann habe ich git reflog ______ , um zu meinem letzten Commit zurückzukehren.
  9. dann habe ich git reset HEAD , um das Commit zu deaktivieren (was ich eigentlich hätte tun sollen), aber die Dateien, die ich nach dem Commit hinzugefügt habe (siehe oben), waren noch immer weg.

Wie bekomme ich diese Dateien zurück?

Erstellen Sie zunächst eine vollständige Sicherung Ihres Git-Repositorys!

Wenn du git add eine Datei git add , erstellt git einen Blob aus dem Inhalt dieser Datei und fügt ihn seiner Objektdatenbank hinzu ( .git/objects/??/* ).

Lassen Sie uns einen Blick auf Ihre Befehle casting:

Ich habe alle Dateien mit git add hinzugefügt.

 $ git add . 

Dadurch werden alle Dateien, die im aktuellen Verzeichnis und seinen Unterverzeichnissen enthalten sind, der Objektdatenbank von Git hinzugefügt. Nicht aufgezeichnete Dateien, die Mustern aus .gitignore Dateien entsprechen, werden nicht hinzugefügt. Baumdateien werden ebenfalls geschrieben. Bitte sehen Sie das Ende meiner Antwort.

Ich habe mich dann verpflichtet

 $ git commit -m'added all files' 

Dadurch wird ein neues Commit-Objekt in die Objektdatenbank geschrieben. Dieses Commit verweist auf einen einzelnen Baum. Der Baum verweist auf Blobs (Dateien) und andere Bäume (Unterverzeichnisse).

Als ich den Status überprüfte, gab es immer noch Dateien, die nicht vom Commit aus dem Add enthalten waren, was seltsam war

 $ git status 

Ich kann mir zwei Szenarien vorstellen, in denen das passiert: etwas, das deine Dateien verändert hat oder neue Dateien wurden hinter deinem Rücken hinzugefügt.

Ich habe die nicht aufgezeichneten Dateien erneut hinzugefügt und es funktionierte dieses Mal

 $ git add . 

Ich nehme an, dass Sie denselben Befehl zum add wie in Schritt 1 erneut verwendet haben.

Aber ich wollte, dass alles in einem einzigen Commit war, also habe ich nachgeschaut, wie ich das, was ich gerade begangen habe, aufheben kann

Ich werde Ihnen am Ende dieser Antwort einen besseren Weg zeigen, bei dem der Benutzer keinen potenziell gefährlichen reset

Ich habe git reset –hard HEAD ^ – schlechte Idee offensichtlich, alle Dateien wurden gelöscht

 $ git reset --hard HEAD^ 

Dieser Befehl wird Ihren aktuellen Arbeitsbaum und Index so einstellen, dass er genau auf dem Commit HEAD^ (dem zweitletzten Commit) liegt. Mit anderen Worten, es werden alle lokalen nicht festgeschriebenen Änderungen verworfen und der Verzweigungszeiger um einen Commit zurückbewegt. Es werden nicht nachverfolgte Dateien nicht berührt.

also habe ich git reflog benutzt, um herauszufinden, wo ich aufgehört habe

 $ git reflog 

Dies zeigt die letzten Commits, die kürzlich git reflog HEAD wurden (identisch mit git reflog HEAD ). Wenn Sie einen Zweignamen angeben, werden Ihnen die letzten Commits angezeigt, auf die dieser Zweig zuletzt verweist.

Dann habe ich git reflog __ benutzt , um zu meinem letzten Commit zurückzukehren.

Ich bin mir nicht sicher. git reflog ist (meistens) ein schreibgeschützter Befehl und kann nicht verwendet werden, um zu Commits zurückzukehren. Sie können es nur verwenden, um eine Verzweigung (oder HEAD ) zu finden, auf die verwiesen wird.

dann habe ich git reset HEAD benutzt, um das Commit zu deaktivieren (was ich eigentlich hätte tun sollen), aber die Dateien, die ich nach dem Commit hinzugefügt habe (siehe oben), waren noch immer weg. $ git reset HEAD

Dadurch wird diese Festschreibung nicht deaktiviert, aber alle gestaffelten (nicht festgeschriebenen) Änderungen werden aus dem Index entfernt. Ursprünglich (1. Schritt) wollten Sie git reset HEAD^ (oder git reset --mixed HEAD^ ) sagen – dies wird Ihren Arbeitsbaum unberührt lassen, aber den Index so setzen, dass er dem Baum entspricht, auf den das durch HEAD^ Commit zeigt .


Um Ihre Dateien zurück zu bekommen, müssen Sie nun git fsck --full --unreachable --no-reflog . Es scannt alle Objekte in der Objektdatenbank von Git und führt eine Erreichbarkeitsanalyse durch. Sie möchten nach blob Objekten suchen. Es sollte auch ein tree Objekt geben, das den Zustand nach dem zweiten git add .

git cat-file -p druckt den git cat-file -p , damit Sie überprüfen können, ob Sie die richtigen Objekte haben. Bei Blobs können Sie die IO-Umleitung verwenden, um den Inhalt in den korrekten Dateinamen zu schreiben. Für Bäume müssen Sie git-Befehle ( git read-tree ) verwenden. Wenn es nur ein paar Dateien sind, ist es besser, sie direkt in Dateien zu schreiben.


Ein paar Hinweise hier:

Wenn Sie Dateien zum letzten Commit hinzufügen (oder seine Commit-Nachricht bearbeiten möchten), können Sie einfach git commit --amend . Es ist im Grunde ein Wrapper um git reset --soft HEAD^ && git commit -c HEAD@{1} .

Außerdem ist es fast nie eine gute Idee, git add . . Normalerweise möchten Sie es nur beim ersten Mal verwenden, wenn Sie ein neues Repository erstellen. Bessere Alternativen sind git add -u , git commit -a , mit dem alle Änderungen an den verfolgten Dateien durchgeführt werden. Um neue Dateien zu verfolgen, geben Sie sie besser explizit an.

Ich hatte ein ähnliches Problem, aber ich hatte viele hängende Blobs und Bäume in meinem Repo, so dass ich am Ende mit grep den Output aller baumelnden Blobs filterte und diejenigen druckte, die übereinstimmten. Angenommen, ${UNIQUE_CODE} ist ein Code, der für die Dateien im Index eindeutig ist, dann sollten Sie die Hashwerte der gesuchten Blobs erhalten:

 for b in $(git fsck --lost-found | grep blob | awk '{print $3}'); do git cat-file -p $b | grep -q ${UNIQUE_CODE} && echo $b; done