Come recuperare una scorta caduta in Git?
Domanda
Lo uso spesso git stash
E git stash pop
per salvare e ripristinare le modifiche nel mio albero di lavoro.Ieri ho apportato alcune modifiche al mio albero di lavoro che avevo nascosto e spuntato, quindi ho apportato ulteriori modifiche al mio albero di lavoro.Mi piacerebbe tornare indietro e rivedere le modifiche nascoste di ieri, ma git stash pop
sembra rimuovere tutti i riferimenti al commit associato.
Lo so se lo uso git stash
Poi .git/refs/stash contiene il riferimento del commit utilizzato per creare lo stash.E .git/logs/refs/stash contiene tutta la scorta.Ma quei riferimenti se ne vanno git stash pop
.So che il commit è ancora nel mio repository da qualche parte, ma non so cosa fosse.
Esiste un modo semplice per recuperare il riferimento al commit stash di ieri?
Tieni presente che questo non è fondamentale per me oggi perché ho backup giornalieri e posso tornare all'albero di lavoro di ieri per ottenere le modifiche.Lo chiedo perché ci deve essere un modo più semplice!
Soluzione
Una volta che conosci l'hash del commit stash che hai rilasciato, puoi applicarlo come stash:
git stash apply $stash_hash
Oppure puoi creare un ramo separato con
git branch recovered $stash_hash
Successivamente, puoi fare quello che vuoi con tutti i normali strumenti.Quando hai finito, soffia via il ramo.
Trovare l'hashish
Se l'hai appena aperto e il terminale è ancora aperto, lo farai hanno ancora il valore hash stampato da git stash pop
sullo schermo (grazie, Dolda).
Altrimenti, puoi trovarlo usando questo per Linux, Unix o Git Bash per Windows:
git fsck --no-reflog | awk '/dangling commit/ {print $3}'
...o utilizzando Powershell per Windows:
git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}
Questo ti mostrerà tutti i commit alle estremità del tuo grafico dei commit a cui non fa più riferimento alcun ramo o tag: ogni commit perso, incluso ogni commit stash che tu abbia mai creato, sarà da qualche parte in quel grafico.
Il modo più semplice per trovare lo stash commit che desideri è probabilmente passare quell'elenco gitk
:
gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
...o vedere la risposta di emragins se si utilizza Powershell per Windows.
Questo avvierà un browser del repository che ti mostrerà ogni singolo commit nel repository di sempre, indipendentemente dal fatto che sia raggiungibile o meno.
Puoi sostituire gitk
lì con qualcosa del genere git log --graph --oneline --decorate
se preferisci un bel grafico sulla console rispetto a un'app GUI separata.
Per individuare i commit stash, cerca i messaggi di commit di questo modulo:
WIP attivato qualche ramo: commithash Qualche vecchio messaggio di commit
Nota:Il messaggio di commit sarà in questo formato (iniziando con "WIP attivo") solo se non hai fornito un messaggio quando lo hai fatto git stash
.
Altri suggerimenti
Se non hai chiuso il terminale, guarda l'output di git stash pop
e avrai l'ID oggetto della scorta caduta.Normalmente appare così:
$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)
(Notare che git stash drop
produce anche la stessa riga.)
Per recuperare quella scorta, corri git branch tmp 2cae03e
, e lo otterrai come ramo.Per convertirlo in una scorta, esegui:
git stash apply tmp
git stash
Averlo come ramo permette anche di manipolarlo liberamente;ad esempio, per selezionarlo o unirlo.
Volevo solo menzionare questa aggiunta alla soluzione accettata.Non mi è stato subito chiaro la prima volta che ho provato questo metodo (forse avrebbe dovuto esserlo), ma per applicare lo stash dal valore hash, basta usare "git stash apply":
$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Quando ero nuovo a Git, questo non mi era chiaro e stavo provando diverse combinazioni di "git show", "git apply", "patch", ecc.
Per ottenere l'elenco delle scorte che sono ancora nel tuo repository, ma non più raggiungibili:
git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP
Se hai dato un titolo alla tua scorta, sostituisci "WIP" in -grep=WIP
alla fine del comando con una parte del tuo messaggio, ad es. -grep=Tesselation
.
Il comando sta cercando "WIP" perché il messaggio di commit predefinito per uno stash è nel modulo WIP on mybranch: [previous-commit-hash] Message of the previous commit.
Ho appena creato un comando che mi ha aiutato a trovare il mio commit stash perduto:
for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less
Questo elenca tutti gli oggetti nell'albero .git/objects, individua quelli di tipo commit, quindi mostra un riepilogo di ciascuno.Da questo punto è stata solo questione di esaminare i commit per trovare un "WIP on work" appropriato:6a9bb2" ("lavoro" è il mio ramo, 619bb2 è un commit recente).
Noto che se utilizzo "git stash apply" invece di "git stash pop" non avrei questo problema, e se utilizzo "git stash save Messaggio" allora il commit potrebbe essere stato più facile da trovare.
Aggiornamento:Con l'idea di Nathan, questo diventa più breve:
for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
git fsck --unreachable | grep commit
dovrebbe mostrare sha1, anche se l'elenco restituito potrebbe essere piuttosto lungo. git show <sha1>
mostrerà se è il commit che desideri.
git cherry-pick -m 1 <sha1>
unirà il commit nel ramo corrente.
Se vuoi ripristinare una scorta perduta, devi prima trovare l'hash della scorta perduta.
Come suggeriva Aristotele Pagaltzis a git fsck
dovrebbe aiutarti.
Personalmente utilizzo il mio log-all
alias che mi mostra ogni commit (commit recuperabili) per avere una visione migliore della situazione:
git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)
Puoi effettuare una ricerca ancora più veloce se stai cercando solo i messaggi "WIP attivo".
Una volta che conosci il tuo sha1, cambi semplicemente il reflog della tua scorta per aggiungere la vecchia scorta:
git update-ref refs/stash ed6721d
Probabilmente preferirai avere un messaggio associato, quindi a -m
git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d
E vorrai anche usarlo come alias:
restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1
Equivalente a Windows PowerShell utilizzando gitk:
gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })
Probabilmente esiste un modo più efficiente per farlo in un unico tubo, ma questo fa il lavoro.
Mi è piaciuto l'approccio di Aristotele, ma non mi è piaciuto usare GITK...poiché sono abituato a usare GIT dalla riga di comando.
Invece, ho preso i commit penzolanti e ho generato il codice in un file DIFF per la revisione nel mio editor di codice.
git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff
Ora puoi caricare il file diff/txt risultante (si trova nella tua cartella home) nel tuo editor txt e vedere il codice effettivo e lo SHA risultante.
Quindi basta usare
git stash apply ad38abbf76e26c803b27a6079348192d32f52219
In OSX con git v2.6.4, ho appena eseguito git stash drop accidentalmente, poi l'ho trovato seguendo i passaggi seguenti
Se conosci il nome della scorta, usa:
$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>
altrimenti troverai l'ID dal risultato manualmente con:
$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show
Quindi, quando trovi l'id commit, premi semplicemente git stash applica {commit-id}
Spero che questo aiuti qualcuno rapidamente
Perché le persone fanno questa domanda?Perché ancora non conoscono né capiscono il reflog.
La maggior parte delle risposte a questa domanda forniscono comandi lunghi con opzioni che quasi nessuno ricorderà.Quindi le persone entrano in questa domanda e copiano e incollano tutto ciò di cui pensano di aver bisogno e lo dimenticano quasi immediatamente dopo.
Consiglierei a tutti coloro che hanno questa domanda di controllare semplicemente il reflog (git reflog), non molto di più.Una volta visualizzato l'elenco di tutti i commit, ci sono cento modi per scoprire quale commit stai cercando e selezionarlo o creare un ramo da esso.Nel processo avrai imparato a conoscere il reflog e le opzioni utili per vari comandi git di base.
Puoi elencare tutti i commit irraggiungibili scrivendo questo comando nel terminale -
git fsck --unreachable
Controlla l'hash di commit irraggiungibile -
git show hash
Infine fai domanda se trovi l'oggetto nascosto -
git stash apply hash
Voglio aggiungere alla soluzione accettata un altro buon modo per eseguire tutte le modifiche, quando non hai gitk disponibile o non hai X per l'output.
git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits
for h in `cat tmp_commits`; do git show $h | less; done
Quindi ottieni tutte le differenze per quegli hash visualizzati uno dopo l'altro.Premi 'q' per passare alla differenza successiva.
La risposta accettata da Aristotele mostrerà tutti i commit raggiungibili, inclusi i commit non simili a stash.Per filtrare il rumore:
git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
--grep="WIP on" --min-parents=3 --max-parents=3
Ciò includerà solo i commit che hanno esattamente 3 commit principali (che avrà uno stash) e il cui messaggio include "WIP on".
Tieni presente che se hai salvato la tua scorta con un messaggio (ad es. git stash save "My newly created stash"
), questo sovrascriverà il messaggio predefinito "WIP su...".
Puoi visualizzare più informazioni su ciascun commit, ad es.visualizzare il messaggio di commit o passarlo a git stash show
:
git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
--grep="WIP on" --min-parents=3 --max-parents=3 | \
xargs -n1 -I '{}' bash -c "\
git log -1 --format=medium --color=always '{}'; echo; \
git stash show --color=always '{}'; echo; echo" | \
less -R
Non sono riuscito a far funzionare nessuna delle risposte su Windows in una semplice finestra di comando (Windows 7 nel mio caso). awk
, grep
E Select-string
non sono stati riconosciuti come comandi.Quindi ho provato un approccio diverso:
- prima corsa:
git fsck --unreachable | findstr "commit"
- copiare l'output nel blocco note
- find sostituisci "commit irraggiungibile" con
start cmd /k git show
sarà simile a questo:
start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4
start cmd /k git show 44078733e1b36962571019126243782421fcd8ae
start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1
start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e
- salva come file .bat ed eseguilo
- lo script aprirà una serie di finestre di comando, mostrando ogni commit
- se hai trovato quello che stai cercando, esegui:
git stash apply (your hash)
potrebbe non essere la soluzione migliore, ma ha funzionato per me
Quello che sono venuto qui a cercare è come recuperare effettivamente la scorta, indipendentemente da ciò che ho controllato.In particolare, avevo nascosto qualcosa, poi ho controllato una versione precedente, quindi l'ho aperta, ma la scorta non era operativa in quel momento precedente, quindi la scorta è scomparsa;Non potevo semplicemente farlo git stash
per rimetterlo in pila.Questo ha funzionato per me:
$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179)
$ git checkout 27f6bd8ba3c
$ git reset HEAD^ # Make the working tree differ from the parent.
$ git stash # Put the stash back in the stack.
Saved working directory and index state WIP on (no branch): c2be516 Some message.
HEAD is now at c2be516 Some message.
$ git checkout somethingOld # Now we are back where we were.
In retrospettiva, avrei dovuto usarlo git stash apply
non git stash pop
.Stavo facendo un bisect
e avevo una piccola patch che volevo applicare ad ogni bisect
fare un passo.Ora sto facendo questo:
$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
Recuperato utilizzando i seguenti passaggi:
Identificare il codice hash stash eliminato:
gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
Cherry Pick the Stash:
git cherry-pick -m 1 $stash_hash_code
Risolvi eventuali conflitti utilizzando:
git mergetool
Inoltre potresti riscontrare problemi con il messaggio di commit se stai utilizzando gerrit.Metti da parte le modifiche prima di seguire le alternative successive:
- Utilizzare l'hard reset al commit precedente e quindi eseguire nuovamente il commit di questa modifica.
- Puoi anche nascondere la modifica, effettuare il rebase e il reimpegno.
Ho rimosso accidentalmente la scorta nell'app GitUP.Basta premere Ctrl+Z per annullarlo.
Magari aiuta qualcuno ;)