it-swarm.it

Vieni a recuperare una scorta abbandonata a Git?

Uso frequentemente 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 nascosto, 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.

So che se uso git stash allora .git/refs/stash contiene il riferimento del commit usato per creare lo stash. E .git/logs/refs/stash contiene l'intero stash. 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 di stash di ieri?

Nota che questo non è critico per me oggi perché ho dei backup giornalieri e posso tornare all'albero di lavoro di ieri per ottenere i miei cambiamenti. Sto chiedendo perché ci deve essere un modo più semplice!

1489
Greg Hewgill

Una volta che conosci l'hash del commit dello stash che hai abbandonato, puoi applicarlo come scorta:

git stash apply $stash_hash

Oppure, puoi creare un ramo separato con esso

git branch recovered $stash_hash

Dopo di ciò, puoi fare quello che vuoi con tutti gli strumenti normali. Quando hai finito, basta soffiare via il ramo.

Trovare l'hash

Se lo hai appena estratto e il terminale è ancora aperto, ti verrà il valore hash è ancora stampato da git stash pop sullo schermo (grazie, Dolda).

Altrimenti puoi trovarlo usando 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 ai vertici del tuo grafico di commit che non sono più referenziati da alcun ramo o tag - ogni commit perso, incluso ogni commit di stash che tu abbia mai creato, sarà da qualche parte in quel grafico.

Il modo più semplice per trovare il commit dello stash che desideri è probabilmente passare tale lista a gitk:

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

... o vedi la risposta dalle emragine se usi Powershell per Windows.

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

È possibile sostituire gitk con qualcosa come git log --graph --oneline --decorate se si preferisce un grafico Nice sulla console su un'applicazione GUI separata.

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

WIP on somebranchcommithash Qualche vecchio messaggio di commit

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

2402

Se non hai chiuso il terminale, guarda l'output da git stash pop e avrai l'ID dell'oggetto della scorta abbandonata. Normalmente assomiglia a questo:

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

(Si noti che git stash drop produce anche la stessa riga.)

Per recuperare questo, basta eseguire git branch tmp 2cae03e e lo si otterrà come ramo. Per convertirlo in una scorta, esegui:

git stash apply tmp
git stash

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

664
Dolda2000

Volevo solo menzionare questa aggiunta alla soluzione accettata. Non è stato subito 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 apply":

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

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

252
Wade

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

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

79
Senthil A Kumar

Ho appena creato un comando che mi ha aiutato a trovare il mio commit 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

Elenca tutti gli oggetti nell'albero .git/objects, individua quelli che sono di tipo commit, quindi mostra un riepilogo di ciascuno di essi. Da questo punto era solo questione di guardare attraverso i commit per trovare un appropriato "WIP on work: 6a9bb2" ("work" è il mio ramo, 619bb2 è un commit recente).

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

git fsck --unreachable | grep commit dovrebbe mostrare lo sha1, anche se la lista che restituisce potrebbe essere abbastanza grande. git show <sha1> mostrerà se è il commit che vuoi.

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

38
Nathan Jones

Se vuoi rimettere a posto una scorta perduta, devi prima trovare l'hash della tua scorta perduta.

Come ha suggerito Aristotele Pagaltzis, un git fsck dovrebbe aiutarti.

Personalmente uso il mio alias log-all che mi mostra ogni commit (commit recuperabile) 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 eseguire una ricerca ancora più rapida se stai cercando solo i messaggi "WIP on".

Una volta che conosci il tuo sha1, devi semplicemente cambiare il tuo reflog di scorta per aggiungere la vecchia scorta:

git update-ref refs/stash ed6721d

Probabilmente preferirai avere un messaggio associato quindi un -m

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

E vorresti anche usarlo come alias:

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

Equivalente a Windows PowerShell utilizzando gitk:

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

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

19
emragins

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

Invece, ho preso i commit penzoloni 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 è possibile caricare il file diff/txt risultante (nella sua cartella home) nell'editor txt e vedere il codice effettivo e lo SHA risultante.

Quindi basta usare

git stash apply ad38abbf76e26c803b27a6079348192d32f52219
16
Shaheen Ghiassy

In OSX con git v2.6.4, ho appena eseguito git stash drop accidentalmente, 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 dal risultato manualmente con:

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

Quindi quando trovi che commit-id ha appena premuto git stash, applica {commit-id}

Spero che questo aiuti qualcuno velocemente

12
Can Tecim

Perché le persone fanno questa domanda? Perché loro non sanno ancora o capiscono il reflog.

La maggior parte delle risposte a questa domanda danno lunghi comandi con opzioni che quasi nessuno ricorderà. Quindi le persone entrano in questa domanda e copiano incolla quello che pensano di aver bisogno e lo dimenticano 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 un centinaio di modi per scoprire quale commit stai cercando e per 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.

11
RobbyD

Voglio aggiungere alla soluzione accettata un altro buon modo per passare attraverso tutte le modifiche, quando 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 tra gli hash visualizzati uno dopo l'altro. Premere 'q' per passare alla successiva diff.

10
Phil

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

  • prima esecuzione: git fsck --unreachable | findstr "commit"
  • copia l'output nel blocco note
  • trova "sostituisci il 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 un file .bat ed eseguilo
  • lo script aprirà un gruppo 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

9
kromakollision

Puoi elencare tutti i commit irraggiungibili scrivendo questo comando nel terminale -

git fsck --unreachable

Verifica hash commit non raggiungibile -

git show hash

Finalmente si applica se trovi l'oggetto nascosto -

git stash apply hash
9
Vivek Kumar

La risposta accettata da Aristotele mostrerà tutti i commit raggiungibili, compresi 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

Questo includerà solo i commit che hanno esattamente 3 commit padre (che avrà uno stash) e il cui messaggio include "WIP on".

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

Puoi visualizzare più informazioni su ciascun commit, ad es. mostra il messaggio di commit o passa 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
9
Brad Feehan

Recuperato usando i seguenti passaggi:

  1. Identifica il codice hash cancellato:

    gitk - tutti $ (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 in caso di utilizzo di:

    git mergetool

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

  1. Utilizzare hard reset per il commit precedente e quindi ricominciare questa modifica.
  2. È inoltre possibile memorizzare la modifica, rebase e recommit.
4
Abhijeet

Quello che sono venuto a cercare qui è come recuperare la scorta, indipendentemente da ciò che ho controllato. In particolare, avevo nascosto qualcosa, poi ho estratto una versione precedente, poi l'ho inserita, ma il magazzino era un no-op in quel momento precedente, quindi la scorta scomparve; Non potevo semplicemente fare 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/[email protected]{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 usare git stash apply not git stash pop. Stavo facendo un bisect e avevo una piccola patch che volevo applicare ad ogni passo bisect. 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.
4
Ben