Come recuperare una scorta abbandonata in Git?


1738

Uso frequentemente git stashe git stash popper 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. Vorrei tornare indietro e rivedere le modifiche apportate ieri, ma git stash popsembra rimuovere tutti i riferimenti al commit associato.

So che se uso git stashallora .git / refs / stash contiene il riferimento del commit utilizzato per creare lo stash. E .git / logs / refs / stash contiene l'intera scorta. Ma quei riferimenti sono andati dopo git stash pop. So che il commit è ancora nel mio repository da qualche parte, ma non so cosa fosse.

C'è un modo semplice per recuperare il riferimento al commit stash di ieri?

Si noti che questo non è fondamentale per me oggi perché ho backup giornalieri e posso tornare all'albero di lavoro di ieri per ottenere le mie modifiche. Lo sto chiedendo perché ci deve essere un modo più semplice!


74
Nota per il futuro: se non vuoi perdere le tue scorte ogni volta che puoi git stash pop, puoi git stash applyinvece farlo . Fa la stessa cosa, tranne per il fatto che non rimuove il riferimento allo stash applicato.
Kevin,

3
Ho provato tutto qui, non sono riuscito a trovare una scorta che era già stata spuntata. Sono così felice per jetbrains.com/help/idea/local-history.html
Juan Mendes,


Ho avuto questo problema Per aggiornare il mio repo, mi sono imbattuto git stash, git pull -r upstream, git push -f origin, git stash pop, e pop, ha detto "fatale: log per arbitri / scorta è vuoto". 😲 Ho provato un sacco di queste risposte, niente ha funzionato. Quando ho guardato in .git / refs / stash , lo SHA era lì. Forse un problema con la marcatura di un'unità di rete Windows per la sincronizzazione offline? 🤷‍♂️
brianary

Risposte:


2786

Una volta che conosci l'hash del commit stash che hai lasciato cadere, puoi applicarlo come stash:

git stash apply $stash_hash

In alternativa, puoi creare un ramo separato per esso con

git branch recovered $stash_hash

Dopodiché, puoi fare quello che vuoi con tutti gli strumenti normali. Quando hai finito, soffia via il ramo.

Trovare l'hash

Se l'hai appena spuntato e il terminale è ancora aperto, avrai comunque il valore di hash stampato git stash popsullo 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 usando Powershell per Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}

Questo ti mostrerà tutti i commit sui suggerimenti del tuo grafico di commit che non sono più referenziati da alcun ramo o tag - ogni commit perso, incluso ogni commit stash che hai mai creato, sarà da qualche parte in quel grafico.

Il modo più semplice per trovare il commit stash che desideri è probabilmente passare l'elenco a gitk:

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

... o vedi la risposta da emragins se usi Powershell per Windows.

Questo avvierà un browser di repository che mostra ogni singolo commit nel repository di sempre , indipendentemente dal fatto che sia raggiungibile o meno.

Puoi sostituirlo gitkcon qualcosa di simile git log --graph --oneline --decoratese preferisci un bel grafico sulla console su un'app GUI separata.

Per individuare i commit di stash, cerca i messaggi di commit di questo modulo:

        WIP su somebranch : commithash Alcuni vecchi messaggi di commit

Nota : il messaggio di commit sarà in questo formato (a partire da "WIP on") se non hai fornito un messaggio quando lo hai fatto git stash.


49
Jaydel mi tolse le parole dalla bocca. Questo post mi ha salvato il lavoro :) Vorrei solo aggiungere: ricordare la data in cui hai lavorato su ciò che hai perso rende più facile sfogliare Gitk per quello che stai cercando.
Sridhar Sarnobat,

4
@Codey: perché PowerShell. Non so se MsysGit spedisca un binario AWK. Google mi dice che qualcosa di simile %{ $_.Split(' ')[2]; }dovrebbe fare l'equivalente di {print $3}quel awkcomando in PowerShell, ma non ho un sistema Windows per testarlo, e hai ancora bisogno di un equivalente per la /dangling commit/parte. Comunque, corri git fsck --no-refloge guarda l'output. Volete gli hash dalle righe "dangling commit <commitID>".
Aristotele Pagaltzis,

7
Vale la pena ricordare che il messaggio di commit avrà la stringa "WIP" solo se non hai fornito il tuo messaggio durante lo stash (cioè facendo git stash save "<message>").
Samir Aguiar,

12
Se sai quando è avvenuta la caduta, puoi utilizzare questo one-liner per ottenere l'elenco dei commit penzolanti aumentando il tempo: git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sortl'ultima voce è probabilmente quella che desideri stash apply.
ris8_allo_zen0,

3
git stash apply {ref}ripristinato una scorta abbandonata! gitè così bello che dovrebbe essere illegale!
Tom Russell,

707

Se non hai chiuso il terminale, guarda l'output da git stash pope avrai l'ID oggetto della scorta rilasciata. Normalmente si presenta così:

$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Nota che git stash dropproduce anche la stessa linea.)

Per recuperare quella scorta, corri git branch tmp 2cae03ee la otterrai come un ramo. Per convertirlo in uno stash, esegui:

git stash apply tmp
git stash

Avendolo come un ramo ti permette anche di manipolarlo liberamente; ad esempio, per selezionarlo o unirlo alla ciliegia.


54
È anche possibile fare git stash apply commitidquindi git stashper ottenere una nuova scorta.
Matthew Flaschen,

32
Nota che se git unisce automaticamente lo stash e ha dei conflitti, non ti mostrerà l'hash.
James,

31
@James: Poi di nuovo, se quei conflitti sono il risultato dell'esecuzione git stash pop, non lascerà cadere nemmeno la scorta, quindi normalmente non è un problema.
Dolda 2000,

2
Non c'era SHA nel mio output pop gash stash. :(
Getta via l'account

2
@Honey: questo è il punto git stash pop. Se vuoi applicare lo stash senza lasciarlo cadere, usa git stash applyinvece. Inoltre, se si desidera applicare una modifica a più rami, è possibile selezionare anche il commit.
Dolda 2000,

271

Volevo solo menzionare questa aggiunta alla soluzione accettata. Non è stato immediatamente ovvio per me la prima volta che ho provato questo metodo (forse avrebbe dovuto essere), ma per applicare lo stash dal valore hash, basta usare "git stash applicare":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Quando ero nuovo di Git, questo non mi era chiaro e stavo provando diverse combinazioni di "git show", "git apply", "patch", ecc.


3
Nota che questo vale (duh!) Lo stash sull'albero di lavoro corrente. Se l'albero è sporco, potresti voler utilizzare prima un ramo temporaneo o uno stash, applicare lo stash dallo SHA-1, stash di nuovo e quindi far apparire il secondo all'ultimo stash (chiamato stash @ {1}).
musiKk,

111

Per ottenere l'elenco degli stash 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" -grep=WIPalla fine del comando con una parte del tuo messaggio, ad es -grep=Tesselation.

Il comando sta eseguendo il grepping per "WIP" perché il messaggio di commit predefinito per uno stash è nel modulo WIP on mybranch: [previous-commit-hash] Message of the previous commit.


1
echo 'git fsck - irraggiungibile | commit di grep | cut -d "" -f3 | xargs git log --merges --no-walk --grep = WIP '> / usr / local / bin / git-stashlog; chmod a + rx / usr / local / bin / git-stashlog # git stashlog
Erik Martino

Oppure puoi aggiungerlo a .gitconfig come alias (precede il comando con a !).
asmeurer il

Ho salvato la mia pancetta - beh, non proprio, ma mi ha salvato la ricodifica dei giorni di lavoro - apprezzato - dato che ho lasciato cadere solo di recente ho appena scelto il SHA superiore dall'output del tuo comando - quindi .... git stash applica SHA ... come menzionato in altre risposte - molti grazie
danday74 l'

75

Ho appena creato un comando che mi ha aiutato a trovare il mio commit stash perso:

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 era solo una questione di esaminare gli commit per trovare un "WIP on work: 6a9bb2" appropriato ("work" è il mio ramo, 619bb2 è un commit recente).

Noto che se uso "git stash apply" invece di "git stash pop" non avrei questo problema, e se uso "git stash save message " 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

41

git fsck --unreachable | grep commitdovrebbe mostrare sha1, anche se l'elenco che restituisce potrebbe essere abbastanza grande. git show <sha1>mostrerà se è il commit che desideri.

git cherry-pick -m 1 <sha1> unirà il commit sul ramo corrente.


37

Equivalente di Windows PowerShell usando gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

C'è probabilmente un modo più efficiente per farlo in una pipe, ma questo fa il lavoro.


1
Sono molto grato per la tua risposta
Виталий Шебаниц

32

Se vuoi ridisporre una scorta perduta, devi prima trovare l'hash della scorta perduta.

Come suggerì Aristotele Pagaltzis, git fsckdovresti aiutarti.

Personalmente uso il mio log-allalias 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 messaggi "WIP on".

Una volta che conosci sha1, devi semplicemente cambiare il reflog di stash per aggiungere il vecchio stash:

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 persino usarlo come alias:

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1

2
Comunque -d\\ dovrebbe essere -d\ (o anche più chiaro -d' ')
joeytwiddle

Si è verificato un errore: "fatale: argomento ambiguo 'penzoloni': revisione sconosciuta o percorso non nell'albero di lavoro."
Daniel Ryan,

devi anche racchiudere il comando secondario tra virgolette git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721
Andrei Shostik,

18

Mi è piaciuto l'approccio di Aristotele, ma non mi è piaciuto usare GITK ... dato che sono abituato a usare GIT dalla riga di comando.

Invece, ho preso le commit penzolanti e l'output del 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 (nella sua cartella home) nel tuo editor txt e vedere il codice effettivo e SHA risultante.

Quindi basta usare

git stash apply ad38abbf76e26c803b27a6079348192d32f52219

17

Puoi elencare tutti i commit non raggiungibili scrivendo questo comando nel terminale -

git fsck --unreachable

Verifica hash commit non raggiungibile -

git show hash

Infine, applica se trovi l'elemento nascosto -

git stash apply hash

15

Perché le persone fanno questa domanda? Perché non conoscono ancora o comprendono il reflog.

La maggior parte delle risposte a questa domanda fornisce lunghi comandi con opzioni che quasi nessuno ricorderà. Quindi la gente viene a questa domanda e copia incolla tutto ciò di cui pensa di aver bisogno e lo dimentica quasi subito dopo.

Consiglierei a tutti con questa domanda di controllare semplicemente il reflog (git reflog), non molto di più. Una volta visualizzato l'elenco di tutti i commit, ci sono centinaia di 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 a vari comandi git di base.


1
Ciao robby Questo è rilevante se stavi lavorando, sei stato messo da parte e hai bisogno di riprendere da dove avevi interrotto un paio di settimane fa solo per scoprire che non riesci a trovare il tuo lavoro nascosto - probabilmente si è perso da qualche parte in quell'altra roba che stavano facendo. reflog è ottimo se è storia recente, ma non per lacune di lunga durata.
emragins

1
Ehi emragins, sono d'accordo, ma questo era esattamente il caso d'uso dell'OP. Non so con certezza come si comporterebbero gli altri comandi pubblicati qui, ma il mio geuss sarebbe che smetterebbero anche di funzionare una volta ripulito il riferimento al suo commit nascosto.
RobbyD,

1
Hmm ... lo scenario sopra è stato ciò che mi ha portato a questa domanda, e so che sono state almeno un paio di settimane se non addirittura più vicine ad un mese tra quando (inconsapevolmente) ho perso la mia scorta e quando sono stato in grado di recuperarla.
emragins

15

In OSX con git v2.6.4, ho appena eseguito git stash drop per errore, quindi l'ho trovato andando attraverso 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 del risultato manualmente con:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Quindi quando trovi il commit-id basta premere il git stash applicare {commit-id}

Spero che questo aiuti qualcuno rapidamente


12

Voglio aggiungere alla soluzione accettata un altro buon modo per passare attraverso tutte le modifiche, quando o non hai Gitk disponibile o nessuna 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 al diff successivo.


12

Non ho potuto ottenere nessuna delle risposte per funzionare su Windows in una semplice finestra di comando (Windows 7 nel mio caso). awk, grepE Select-stringnon sono stati riconosciuti come comandi. Quindi ho provato un approccio diverso:

  • prima corsa: git fsck --unreachable | findstr "commit"
  • copia l'output sul blocco note
  • trova sostituire "commit non raggiungibile" 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


Puoi usare git bash anche su Windows. In git bash hai tutti gli strumenti (unixoid) della riga di comando di cui hai bisogno.
Adrian W,

10

La risposta accettata da Aristotele mostrerà tutti i commit raggiungibili, inclusi i commit non-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 commit che hanno esattamente 3 commit parent (che uno stash avrà) e il cui messaggio include "WIP on".

Tieni presente che se hai salvato la scorta con un messaggio (ad es. git stash save "My newly created stash"), Questo sostituirà il messaggio predefinito "WIP su ...".

È possibile visualizzare ulteriori 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

6

Il mio preferito è questo one-liner:

git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )

Questa è sostanzialmente la stessa idea di questa risposta, ma molto più breve. Naturalmente, puoi ancora aggiungere --graphper ottenere una visualizzazione ad albero.

Quando hai trovato il commit nell'elenco, fai domanda con

git stash apply THE_COMMIT_HASH_FOUND

Per me, l'utilizzo --no-reflogsha rivelato la voce persa, ma --unreachable(come si trova in molte altre risposte) no.

Eseguilo su git bash quando sei in Windows.

Riconoscimenti: i dettagli dei comandi precedenti sono tratti da https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf


5

Recuperato utilizzando i seguenti passaggi:

  1. Identifica il codice hash stash eliminato:

    gitk --all $ (git fsck --no-reflog | awk '/ dangling commit / {print $ 3}')

  2. Cherry Pick the Stash:

    git cherry-pick -m 1 $ stash_hash_code

  3. Risolvi i conflitti se presenti utilizzando:

    git mergetool

Inoltre potresti avere problemi con il messaggio di commit se stai usando gerrit. Si prega di riporre le modifiche prima di seguire le prossime alternative:

  1. Utilizzare il ripristino hardware per il commit precedente e quindi consigliare questa modifica.
  2. Puoi anche riporre la modifica, rifare la richiesta e ricominciare.

@ miva2 la tua modifica ha rimosso il link alla risposta più corretta in questa domanda. Aggiunta del collegamento nel commento stackoverflow.com/questions/89332/…
Abhijeet

4

Quello che sono venuto qui a cercare è come recuperare effettivamente la scorta, indipendentemente da ciò che ho verificato. In particolare, avevo nascosto qualcosa, poi ho verificato una versione precedente, quindi l'ho pop-up, ma la scorta era una no-op in quel momento precedente, quindi la scorta è scomparsa; Non potevo fare semplicemente git stashper 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.

Col senno di poi, avrei usato git stash applynon git stash pop. Stavo facendo una bisecte avevo una piccola patch che volevo applicare ad ogni bisectpasso. 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.

è una risposta o la continuazione della domanda?
Alex Brown,

Un po 'di entrambi. Ho trovato questa pagina perché ho perso una scorta e stavo cercando di recuperarla. Il caso d'uso per me sta facendo una bisect in cui voglio applicare una modifica prima di testare ad ogni passaggio. Ho imparato a mie spese che non puoi semplicemente fare pop, test, stash, bisect perché questo può lasciare un commit diverso sulla stash, quindi stash apply.
Ben
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.