it-swarm.it

Annulla git reset --hard con file senza commit nell'area di gestione temporanea

Sto cercando di recuperare il mio lavoro. Ho stupidamente fatto git reset --hard, ma prima ho fatto solo get add . e non ha fatto git commit. Per favore aiuto! Ecco il mio registro:

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

È possibile annullare git reset --hard in questa situazione?

102
eistrati

Dovresti essere in grado di ripristinare tutti i file che hai aggiunto all'indice (ad esempio, come nella tua situazione, con git add .) anche se potrebbe essere un po 'di lavoro. Per aggiungere un file all'indice, git lo aggiunge al database degli oggetti, il che significa che può essere recuperato fino a quando la garbage collection non è ancora avvenuta. C'è un esempio di come fare questo dato in la risposta di Jakub Narębski qui:

Tuttavia, l'ho provato su un repository di test e c'erano un paio di problemi - --cached dovrebbe essere --cache e ho scoperto che in realtà non ha creato il .git/lost-found directory. Tuttavia, i seguenti passaggi hanno funzionato per me:

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

Ciò dovrebbe generare tutti gli oggetti nel database degli oggetti che non sono raggiungibili da alcun riferimento, nell'indice o tramite il reflog. L'output sarà simile al seguente:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... e per ciascuno di quei BLOB, puoi fare:

git show 907b308

Per generare il contenuto del file.


Troppa produzione?

Aggiornamento in risposta al commento di sehe di seguito:

Se scopri che ci sono molti commit e alberi elencati nell'output di quel comando, potresti voler rimuovere dall'output tutti gli oggetti a cui fa riferimento da commit senza riferimento. (In genere puoi tornare a questi commit tramite il reflog comunque - siamo solo interessati agli oggetti che sono stati aggiunti all'indice ma che non possono mai essere trovati tramite un commit.)

Innanzitutto, salva l'output del comando, con:

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

Ora i nomi degli oggetti di questi commit irraggiungibili possono essere trovati con:

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

Quindi puoi trovare solo gli alberi e gli oggetti che sono stati aggiunti all'indice, ma non impegnati in nessun punto, con:

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

Ciò riduce enormemente il numero di oggetti che dovrai considerare.


Aggiornamento: Philip Oakley di seguito suggerisce un altro modo per ridurre il numero di oggetti da considerare, che è quello di considerare file modificati di recente in .git/objects. Puoi trovarli con:

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

(Ho scoperto che find invocazione qui .) La fine di tale elenco potrebbe apparire come:

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

Nel qual caso puoi vedere quegli oggetti con:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(Nota che devi rimuovere il / alla fine del percorso per ottenere il nome dell'oggetto.)

162
Mark Longair

Ho appena fatto un git reset --hard e ho perso un commit. Ma conoscevo l'hash di commit, quindi sono stato in grado di fare git cherry-pick COMMIT_HASH per ripristinarlo.

L'ho fatto in pochi minuti dopo aver perso il commit, quindi potrebbe funzionare per alcuni di voi.

58
Richard Saunders

Grazie a Mark Longair ho recuperato le mie cose!

Per prima cosa ho salvato tutti gli hash in un file:

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

poi li metto tutti (rimuovendo la cosa "blob irraggiungibile") in un elenco e inserisco tutti i dati in nuovi file ... devi scegliere i tuoi file e rinominarli di nuovo di cui hai bisogno ... ma ne avevo bisogno solo alcuni files..hope questo aiuta qualcuno ...

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
13
Boy

La soluzione di @ Ajedi32 nei commenti ha funzionato per me esattamente in questa situazione.

git reset --hard @{1}

Nota che tutte queste soluzioni si basano sul fatto che non esiste git gc e alcune potrebbero causarne una, quindi comprimerei il contenuto della tua directory .git prima di provare qualsiasi cosa in modo da avere un'istantanea su cui tornare se non lavorare per te.

5
Duncan McGregor

Si è verificato lo stesso problema, ma non aveva aggiunto le modifiche all'indice. Quindi tutti i comandi sopra non mi hanno riportato le modifiche desiderate.

Dopo tutte le risposte elaborate sopra, questo è un suggerimento ingenuo, ma potrebbe essere che salverà qualcuno che non ci ha pensato prima, come ho fatto io.

Nella disperazione, ho provato a premere CTRL-Z nel mio editor (LightTable), una volta in ogni scheda aperta - questo per fortuna ha ripristinato il file in quella scheda, al suo ultimo stato prima del git reset --hard. HTH.

3
olange

Questo è probabilmente ovvio per i professionisti git là fuori, ma ho voluto metterlo da parte poiché nella mia ricerca frenetica non ho visto questo sollevato.

Ho messo in scena alcuni file e ho fatto un reset git --hard, un po 'fuori di testa, e poi ho notato che il mio stato mostrava tutti i miei file ancora in scena e tutte le loro eliminazioni non messe in scena.

A questo punto è possibile eseguire il commit di tali modifiche, a condizione che non vengano eliminate le eliminazioni. Dopo questo, devi solo trovare il coraggio di fare "git reset --hard" ancora una volta, il che ti riporterà a quei cambiamenti che avevi messo in scena e che ora hai appena commesso.

Ancora una volta, questo non è probabilmente nulla di nuovo per la maggior parte, ma spero che dal momento che mi ha aiutato e non ho trovato nulla che suggerisca questo, potrebbe aiutare qualcun altro.

1
ddoty

Bontà, ho tirato i capelli fino a quando ho incontrato questa domanda e le sue risposte. Credo che la risposta corretta e concisa alla domanda posta sia disponibile solo se si mettono insieme due dei commenti sopra, quindi qui è tutto in un unico posto:

  1. Come accennato da Chilicuil, esegui 'git reflog' per identificare lì l'hash di commit a cui vuoi tornare

  2. Come accennato da Akimsko, probabilmente NON vorrai scegliere Cherry a meno che tu non abbia perso solo un commit, quindi dovresti eseguire 'git reset --hard

Nota per gli utenti di Egit Eclipse: non sono riuscito a trovare un modo per eseguire questi passaggi all'interno di Eclipse con egit. Chiudere Eclipse, eseguire i comandi sopra da una finestra del terminale e riaprire Eclipse ha funzionato bene per me.

1
Lolo

Sto usando IntelliJ e sono stato in grado di esaminare semplicemente ogni file e fare:

Edit -> reload from disk

Fortunatamente, avevo appena fatto un git status subito prima di aver cancellato i miei cambiamenti di lavoro, quindi sapevo esattamente cosa dovevo ricaricare.

0
Forrest

Le soluzioni di cui sopra possono funzionare, tuttavia, ci sono modi più semplici per recuperare da questo invece di passare attraverso la complessa annullamento di git. Immagino che la maggior parte dei reset di git avvenga su un piccolo numero di file, e se usi già VIM, questa potrebbe essere la soluzione più efficiente in termini di tempo. L'avvertenza è che devi già usare ViM's Persistent-undo, che dovresti usare in entrambi i modi, perché ti dà la possibilità di annullare un numero illimitato di modifiche.

Ecco i passaggi:

  1. In vim premi : E digita il comando set undodir. Se hai persistent undo Attivato nel tuo .vimrc, Verrà visualizzato un risultato simile a undodir=~/.vim_runtime/temp_dirs/undodir.

  2. Nel tuo repository usa git log Per scoprire l'ultima data/ora in cui hai effettuato l'ultimo commit

  3. In Shell, vai su undodir usando cd ~/.vim_runtime/temp_dirs/undodir.

  4. In questa directory utilizzare questo comando per trovare tutti i file che sono stati modificati dall'ultimo commit

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

    Qui "2018-03-20 11:24:44" è la data e l'ora dell'ultimo commit. Se la data in cui hai fatto il git reset --hard È "22-03-2018", usa "22-03-2018", quindi usa "23-03-2018". Ciò è dovuto a una stranezza di find, in cui il limite inferiore è inclusivo e il limite superiore è esclusivo. https://unix.stackexchange.com/a/70404/24298

  5. Successivamente vai a ciascuno dei file aprili in vim e fai un "20m precedenti". Puoi trovare maggiori dettagli su "prima" usando "h prima". Qui earlier 20m Significa tornare allo stato del file 20 minuti indietro, supponendo che tu abbia eseguito il git hard --reset, 20 minuti indietro. Ripeti l'operazione su tutti i file che sono stati eliminati dal comando find. Sono sicuro che qualcuno può scrivere una sceneggiatura che unirà queste cose.

0
alpha_989