Machen Sie das Zurücksetzen des Git rückgängig - hart mit nicht festgeschriebenen Dateien im Staging-Bereich

109

Ich versuche meine Arbeit wiederzugewinnen. Ich habe es dumm gemacht git reset --hard, aber vorher habe ich nur getan get add .und nicht getan git commit. Bitte helfen Sie! Hier ist mein Log:

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

Ist es git reset --hardin dieser Situation möglich, rückgängig zu machen ?

eistrati
quelle
@ MarkLongair super Mann! Du hast gerade meine Arbeit zurückbekommen! Ich habe ein Python-Skript geschrieben, um Dateien aller Ausgaben zu erstellen! Ich werde das Skript als Antwort hinzufügen
Boy
4
Nicht 'dumm' ... sondern 'naiv' ... weil ich einfach DAS GLEICHE getan habe!
Rosdi Kasim
Könnte immer noch dumm sein ;-)
Duncan McGregor
Hier ist ein großartiger Artikel darüber, wie Sie einige davon rückgängig machen können. Es wird einige manuelle Arbeit erfordern.
Jan Swart
@MarkLongair `` `find .git / properties / -type f -printf '% TY-% Tm-% Td% TT% p \ n' | sort `` `hat bei mir funktioniert. Es werden auch Daten angezeigt. Überprüfen Sie die Blobs ab dem Ende.
ZAky

Antworten:

175

Sie sollten in der Lage sein, alle Dateien wiederherzustellen, die Sie dem Index hinzugefügt haben (z. B. wie in Ihrer Situation mit git add .), obwohl dies möglicherweise ein wenig Arbeit ist. Um dem Index eine Datei hinzuzufügen, fügt git sie der Objektdatenbank hinzu. Dies bedeutet, dass sie wiederhergestellt werden kann, solange die Speicherbereinigung noch nicht erfolgt ist. Ein Beispiel dafür finden Sie in Jakub Narębskis Antwort hier:

Ich habe das jedoch in einem Test-Repository ausprobiert, und es gab ein paar Probleme - --cachedsollte es sein --cache, und ich stellte fest, dass das .git/lost-foundVerzeichnis nicht tatsächlich erstellt wurde . Die folgenden Schritte haben jedoch bei mir funktioniert:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

Das sollte alle Objekte in der Objektdatenbank ausgeben, die mit keinem Verweis, im Index oder über das Reflog erreichbar sind. Die Ausgabe sieht ungefähr so ​​aus:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... und für jeden dieser Blobs können Sie Folgendes tun:

git show 907b308

So geben Sie den Inhalt der Datei aus


Zu viel Output?

Update als Antwort auf den Kommentar von sehe unten:

Wenn Sie feststellen, dass in der Ausgabe dieses Befehls viele Commits und Bäume aufgeführt sind, möchten Sie möglicherweise alle Objekte aus der Ausgabe entfernen, auf die aus nicht referenzierten Commits verwiesen wird. (Normalerweise können Sie über das Reflog ohnehin zu diesen Commits zurückkehren. Wir sind nur an Objekten interessiert, die dem Index hinzugefügt wurden, aber niemals über ein Commit gefunden werden können.)

Speichern Sie zunächst die Ausgabe des Befehls mit:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

Jetzt können die Objektnamen dieser nicht erreichbaren Commits gefunden werden mit:

egrep commit all | cut -d ' ' -f 3

Sie können also nur die Bäume und Objekte finden, die dem Index hinzugefügt, aber zu keinem Zeitpunkt festgeschrieben wurden, mit:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

Das reduziert die Anzahl der Objekte, die Sie berücksichtigen müssen, enorm.


Update: Philip Oakley unten schlägt eine andere Möglichkeit vor, die Anzahl der zu berücksichtigenden Objekte zu verringern, nämlich nur die zuletzt geänderten Dateien unter .git/objects. Sie finden diese mit:

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(Ich habe diesen findAufruf hier gefunden .) Das Ende dieser Liste könnte folgendermaßen aussehen:

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

In diesem Fall können Sie diese Objekte sehen mit:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(Beachten Sie, dass Sie das /am Ende des Pfads entfernen müssen , um den Objektnamen zu erhalten.)

Mark Longair
quelle
Bevor ich meine Antwort veröffentlichte, überlegte ich, genau diese Schritte einzuschließen. Als ich dies jedoch mit einem meiner lokalen Repos versuchte, bekam ich Hunderte von nicht erreichbaren Objekten, und ich konnte keinen angemessenen Ansatz finden, um sie beispielsweise nach dem eingereichten Datum zu sortieren. Nun, das wäre großartig
siehe
3
Ist es nicht möglich, einen Blick auf die Objektzeitstempel für die neuesten Objekte zu werfen, die später als Ihr letztes Commit sind? oder fehlt mir etwas
Philip Oakley
1
@Philip Oakley: Vielen Dank für diesen Vorschlag. Ich habe den Vorschlag hinzugefügt, die zuletzt geänderten Objekte in der Objektdatenbank zu finden.
Mark Longair
2
Alter, das ist großartig! Das hat gerade mein @ $$ gerettet. Und ich habe etwas Git-Wissenschaft gelernt. Beobachten Sie besser, was Sie dem Index hinzufügen ...
Bowsersenior
1
hier niemandem Kalklicht zu stehlen. Aber nur als Hinweis, da ich gerade auf das gleiche Problem gestoßen bin und dachte, ich hätte meine ganze Arbeit verloren. Für Benutzer einer IDE verfügen die IDEs normalerweise über eine Ein-Klick-Wiederherstellungslösung. Im Falle von PHPstorm musste ich nur mit der rechten Maustaste auf das Verzeichnis klicken und Lokalen Verlauf anzeigen auswählen und dann zum letzten gültigen Status zurückkehren.
Shalom Sam
58

Ich habe gerade ein git reset --hardCommit gemacht und ein Commit verloren. Aber ich kannte den Commit-Hash, also konnte ich es tungit cherry-pick COMMIT_HASH wiederherstellen.

Ich habe dies innerhalb weniger Minuten nach dem Verlust des Commits getan, sodass es für einige von Ihnen möglicherweise funktioniert.

Richard Saunders
quelle
5
Danke, danke, danke. Dank dieser Antwort konnte ich einen Tag Arbeit wiedererlangen
Dace
21
Wahrscheinlich ist es erwähnenswert, dass Sie diese Hashes mit git reflogzB git reset --hard-> sehen könnengit reflog ( HEAD @ {1} Hash) und schließlichgit cherry-pick COMMIT_HASH
Javier López,
2
Leute, die dies lesen, achten Sie darauf, dass dies nur für einen einzelnen Commit-Hash funktioniert. Wenn es mehr als einen einzelnen Commit gab, wird das Problem nicht sofort gelöst!
Ain Tohvri
4
Sie können "vorwärts" tatsächlich zurücksetzen. Wenn Sie also mehrere Commits zurückgesetzt haben, sollte ein weiterer 'git reset --hard <commit>' die gesamte Commit-Kette bis zu diesem Punkt wiederherstellen. (da sie noch nicht GC'ed sind)
akimsko
6
Der Einzeiler für die Wiederherstellung verlorener Commits git reset --hard(vorausgesetzt, Sie haben nach diesem Befehl nichts mehr getan) istgit reset --hard @{1}
Ajedi32
13

Dank Mark Longair habe ich meine Sachen zurückbekommen!

Zuerst habe ich alle Hashes in einer Datei gespeichert:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

Als nächstes füge ich sie alle (Entfernen des 'nicht erreichbaren Blobs') in eine Liste ein und füge die Daten alle in neue Dateien ein ... Sie müssen Ihre Dateien auswählen und sie erneut umbenennen, was Sie brauchen ... aber ich brauchte nur einige Dateien ... hoffentlich hilft das jemandem ...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1
Junge
quelle
1
Ja! Genau das brauchte ich. Ich mag das Python-Skript zum Neuerstellen aller Dateien. Die anderen Antworten machten mich nervös, weil ich meine Daten mit der Speicherbereinigung verloren hatte. Das Ablegen der Dateien ist also ein Gewinn für mich :)
Eric Olson
1
Ich habe dieses Skript basierend auf dieser Antwort geschrieben. Funktioniert
sofort
1
Ich finde es einfacher, es so zu automatisieren : mkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh. Dateien werden inlost/*.blob
Matija Nalis
6

Die Lösung von @ Ajedi32 in den Kommentaren hat in genau dieser Situation für mich funktioniert.

git reset --hard @{1}

Beachten Sie, dass alle diese Lösungen davon abhängen, dass es kein git gc gibt, und einige von ihnen möglicherweise eines verursachen. Daher würde ich den Inhalt Ihres .git-Verzeichnisses komprimieren, bevor Sie etwas versuchen, damit Sie einen Snapshot haben, zu dem Sie zurückkehren können, wenn dies nicht der Fall ist Ich arbeite nicht für dich.

Duncan McGregor
quelle
Ich hatte eine Reihe von nicht festgeschriebenen Dateien. Dann habe ich 1 Datei festgeschrieben und eine erstellt git reset --hard, die mein Repo auf unbekannte Weise durcheinander gebracht hat. @Duncan, was macht der @ {1}? und auf welchen Kommentar beziehen Sie sich? Wird a zurückgesetzt git reset?
Alpha_989
Ich denke, dass das @ {1} ein relativer Verweis auf das vorletzte Commit ist, aber ich bin kein Experte und berichte nur, was für mich funktioniert hat
Duncan McGregor
3

Ist auf dasselbe Problem gestoßen, hat aber die Änderungen nicht zum Index hinzugefügt. Alle obigen Befehle brachten mir also nicht die gewünschten Änderungen zurück.

Nach all den oben ausgearbeiteten Antworten ist dies ein naiver Hinweis, aber es kann sein, dass er jemanden rettet, der nicht wie ich zuerst darüber nachgedacht hat.

In meiner Verzweiflung habe ich versucht, in meinem Editor (LightTable) einmal in jedem geöffneten Tab STRG-Z zu drücken. Glücklicherweise wurde die Datei in diesem Tab auf den neuesten Stand vor dem zurückgesetzt git reset --hard. HTH.

olange
quelle
1
Genau die gleiche Situation, es wurde versäumt, den Index hinzuzufügen, und es wurde ein Hard-Reset durchgeführt, und keine der oben genannten Lösungen funktionierte. Dies war ein SMART-Zug, danke, dass ich Strg + Z gemacht und meinen Tag gerettet habe. Mein Editor: SublimeText. Eins von meiner Seite!
Vivek
1

Dies ist wahrscheinlich für die Git-Profis da draußen offensichtlich, aber ich wollte es aufstellen, da ich bei meiner hektischen Suche nicht gesehen habe, dass dies zur Sprache gebracht wurde.

Ich habe einige Dateien git reset --hardbereitgestellt und eine ausgeführt , bin ein wenig ausgeflippt und habe dann festgestellt, dass in meinem Status alle noch bereitgestellten Dateien sowie alle nicht bereitgestellten Löschvorgänge angezeigt wurden.

Zu diesem Zeitpunkt können Sie diese bereitgestellten Änderungen festschreiben, solange Sie ihre Löschvorgänge nicht durchführen. Danach müssen Sie nur noch den Mut aufbringen, dies noch git reset --hardeinmal zu tun , was Sie zu den Änderungen zurückbringt, die Sie vorgenommen und jetzt nur noch begangen haben.

Auch dies ist für die meisten wahrscheinlich nichts Neues, aber ich hoffe, dass es jemand anderem helfen könnte, da es mir geholfen hat und ich nichts gefunden habe, was darauf hindeutet.

ddoty
quelle
1

Meine Güte, ich zog an meinen Haaren, bis ich auf diese Frage und ihre Antworten stieß. Ich glaube, die richtige und prägnante Antwort auf die gestellte Frage ist nur verfügbar, wenn Sie zwei der obigen Kommentare zusammenfassen. Hier ist also alles an einem Ort:

  1. Führen Sie, wie von chilicuil erwähnt, git reflogden Commit-Hash aus, zu dem Sie zurückkehren möchten

  2. Wie von akimsko erwähnt, möchten Sie wahrscheinlich NICHT Kirschbaum pflücken, es sei denn, Sie haben nur ein Commit verloren, also sollten Sie dann laufen git reset --hard <hash-commit-you-want>

Hinweis für egit Eclipse-Benutzer: Ich konnte mit egit keine Möglichkeit finden, diese Schritte in Eclipse auszuführen. Das Schließen von Eclipse, das Ausführen der obigen Befehle in einem Terminalfenster und das erneute Öffnen von Eclipse funktionierten für mich einwandfrei.

Lolo
quelle
0

Die oben genannten Lösungen können funktionieren, es gibt jedoch einfachere Möglichkeiten, dies zu beheben, anstatt das gitkomplexe Rückgängigmachen zu durchlaufen . Ich würde vermuten, dass die meisten Git-Resets für eine kleine Anzahl von Dateien durchgeführt werden. Wenn Sie bereits VIM verwenden, ist dies möglicherweise die zeiteffizienteste Lösung. Die Einschränkung besteht darin, dass Sie bereits ViM'sPersistent-Undo verwenden müssen, was Sie in beiden Fällen verwenden sollten, da Sie damit die unbegrenzte Anzahl von Änderungen rückgängig machen können.

Hier sind die Schritte:

  1. Drücken Sie in vim :und geben Sie den Befehl ein set undodir. Wenn Sie persistent undoin Ihrem eingeschaltet haben .vimrc, wird ein ähnliches Ergebnis wie angezeigt undodir=~/.vim_runtime/temp_dirs/undodir.

  2. Verwenden Sie git login Ihrem Repo , um das letzte Datum / die letzte Uhrzeit herauszufinden, zu der Sie das letzte Commit durchgeführt haben

  3. Navigieren Sie in Ihrer Shell zu Ihrer undodirVerwendung cd ~/.vim_runtime/temp_dirs/undodir.

  4. Verwenden Sie diesen Befehl in diesem Verzeichnis, um alle Dateien zu finden, die Sie seit dem letzten Festschreiben geändert haben

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    Hier ist "2018-03-20 11:24:44" das Datum und die Uhrzeit des letzten Commits. Wenn das Datum, an dem Sie dies getan haben, git reset --hard"2018-03-22" ist, verwenden Sie "2018-03-22" und dann "2018-03-23". Dies ist auf eine Eigenart des Funds zurückzuführen, bei der die untere Grenze inklusive und die obere Grenze exklusiv ist. https://unix.stackexchange.com/a/70404/242983

  5. Als nächstes gehen Sie zu jeder der Dateien, öffnen Sie sie in vim und machen Sie eine "frühere 20m". Weitere Details zu "früher" finden Sie unter "h früher". Hier earlier 20mbedeutet, dass Sie 20 Minuten zurück zum Status der Datei zurückkehren, vorausgesetzt, Sie haben die git hard --reset20 Minuten zurück ausgeführt. Wiederholen Sie dies für alle Dateien, die aus dem findBefehl ausgespuckt wurden . Ich bin sicher, dass jemand ein Skript schreiben kann, das diese Dinge kombiniert.

alpha_989
quelle
0

Ich verwende IntelliJ und konnte einfach jede Datei durchgehen und Folgendes tun:

Edit -> reload from disk

Zum Glück hatte ich gerade ein git statusRecht getan, bevor ich meine Arbeitsänderungen ausgelöscht hatte, sodass ich genau wusste, was ich nachladen musste.

Wald
quelle
0

Wenn Sie sich an Ihre Dateihierarchie erinnern und die Technik von Mark Longair mit Phil Oakley-Modifikation verwenden, erhalten Sie dramatische Ergebnisse.

Wenn Sie die Dateien mindestens zum Repo hinzugefügt, aber nicht festgeschrieben haben, können Sie sie im Wesentlichen durch interaktive Verwendung wiederherstellen git show das Protokoll , das Protokoll überprüfen und die Shell-Umleitung verwenden, um jede Datei zu erstellen (wobei Sie sich Ihren interessierenden Pfad merken).

HTH!

ainthu
quelle