Como recuperar um esconderijo caído em Git?

Eu utilizo frequentemente git stash e git stash pop para salvar e restaurar as mudanças na minha árvore de trabalho. Ontem eu tive algumas mudanças na minha árvore de trabalho que eu tinha escondido e popped, e então eu fiz mais mudanças na minha árvore de trabalho. I'gostaria de voltar e rever ontem's stashed changes, mas git stash pop aparece para remover todas as referências ao commit associado.

Eu sei que se eu utilizar git stash então .git/refs/stash contém a referência do commit utilizado para criar o stash. E .git/logs/refs/stash contém o stash inteiro. Mas essas referências desaparecem após o git stash pop. Eu sei que o commit ainda está em algum lugar no meu repositório, mas eu não sei'não sei o que foi.

Existe uma maneira fácil de recuperar ontem's stash commit reference?

Note que isso é't crítico para mim hoje porque eu tenho backups diários e posso voltar para ontem's working tree para obter minhas mudanças. I'estou perguntando porque deve haver uma maneira mais fácil!

Solução

Assim que souberes que o hash do compromisso de esconderijo que deixaste cair, podes aplicá-lo como um esconderijo:

git stash apply $stash_hash

Ou, você pode criar um ramo separado para ele com

git branch recovered $stash_hash

Depois disso, você pode fazer o que quiser com todas as ferramentas normais. Quando terminares, basta rebentar com o ramo.

Finding the hash

Se você acabou de colocá-lo e o terminal ainda estiver aberto, você ainda terá o valor do hash impresso por git stash pop na tela (obrigado, Dolda).

Caso contrário, você pode encontrá-lo usando isto para Linux, Unix ou Git Bash para Windows:

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

...ou usando Powershell para Windows:

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

Isto irá mostrar-te todos os commits nas dicas do teu gráfico de commit que já não são referenciados a partir de nenhum ramo ou tag - cada commit perdido, incluindo cada commit do stash que já criaste, estará algures nesse gráfico.

A maneira mais fácil de encontrar o stash commit que você quer é provavelmente passar essa lista para a gitk:

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

...ou ver a resposta do emragins se estiver usando Powershell para Windows.

Isto irá lançar um navegador de repositório que lhe mostrará todos os commits individuais no repositório de sempre, independentemente de ser alcançável ou não.

Você pode substituir o gitk por algo como `git log --graph --oneline --decorate' se preferir um gráfico bonito no console em vez de um aplicativo GUI separado.

Para ver os commits, procure por mensagens de commit desta forma:

        WIP on somebranch: commithash Some old commit message

Nota: A mensagem de compromisso só estará nesta forma (começando com "WIP on") se você não forneceu uma mensagem quando você fez git stash.

Comentários (23)

Acabei de construir um comando que me ajudou a encontrar o meu compromisso perdido:

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

Isto lista todos os objetos na árvore .git/objects, localiza os que são do tipo commit, depois mostra um resumo de cada um. A partir deste ponto era apenas uma questão de olhar através dos commits para encontrar um &quot apropriado; WIP sobre o trabalho: 6a9bb2" ("work" é o meu ramo, 619bb2 é um commit recente).

Observo que se eu usar "git stash apply" ao invés de "git stash pop" eu não teria esse problema, e se eu usar "git stash save message" então o commit pode ter sido mais fácil de encontrar.

Atualização: Com a idéia de Nathan's, isto se torna mais curto:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
Comentários (0)

O 'git fsck --unreachable | grep commit' deve mostrar o sha1, embora a lista que ele retorna possa ser bem grande. O git show mostrará se é o commit que você quer.

O "git cherry-pick -m 1 ` irá fundir o compromisso no ramo atual.

Comentários (1)