Como recuperar um estoque caiu no Git?
Pergunta
Eu freqüentemente usam git stash
e git stash pop
para salvar e restaurar mudanças em minha árvore de trabalho. Ontem eu tive algumas mudanças em minha árvore de trabalho que eu tinha escondido e estalaram, e depois fiz mais mudanças para a minha árvore de trabalho. Eu gostaria de voltar e alterações escondido revisão de ontem, mas git stash pop
aparece para remover todas as referências ao associado cometer.
Eu sei que se eu usar git stash
então .git / refs / esconderijo contém a referência do commit usado para criar o esconderijo. E .git / logs / refs / esconderijo contém todo esconderijo. Mas essas referências são ido após git stash pop
. Eu sei que a cometer ainda está em meu lugar repositório, mas eu não sei o que era.
Existe uma maneira fácil de recuperar esconderijo de ontem commit referência?
Note que este não é crítica para mim hoje porque eu tenho backups diários e pode voltar para a árvore de trabalho de ontem para obter as minhas alterações. Estou perguntando porque deve haver uma maneira mais fácil!
Solução
Depois de saber o hash do esconderijo cometer você caiu, você pode 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 você estiver pronto, basta soprar o ramo de distância.
Finding o hash
Se você tiver apenas o colocou eo terminal ainda está aberta, você vai ainda tem o valor de hash impresso pelo git stash pop
em tela (graças, Dolda).
Caso contrário, você pode encontrá-lo usando isso para Linux, Unix ou Git Bash para Windows:
git fsck --no-reflog | awk '/dangling commit/ {print $3}'
... ou usando o PowerShell para Windows:
git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}
Isto irá mostrar-lhe todos os commits nas pontas de seu gráfico comprometer que já não são referenciados a partir de qualquer ramo ou etiqueta - cada perdido cometem, incluindo cada esconderijo cometer você já criado, será em algum lugar que o gráfico <. / p>
A maneira mais fácil de encontrar o esconderijo cometer você quer é, provavelmente, para passar essa lista para gitk
:
gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )
... ou veja a resposta de emragins se usando o PowerShell para Windows.
Isto irá lançar um navegador repositório mostrando-lhe cada comprometer no repositório nunca , independentemente de saber se é acessível ou não.
Você pode substituir gitk
lá com algo como git log --graph --oneline --decorate
se preferir um gráfico agradável no console sobre um aplicativo GUI separado.
Para detectar commits stash, olhar para cometer mensagens desta forma:
WIP em somebranch : commithash Alguns velha mensagem de commit
Nota ??em>:. A mensagem de confirmação será apenas nessa forma (começando com "WIP on") se você não forneceu uma mensagem quando você fez git stash
Outras dicas
Se você não fechar o olhar terminal, apenas na saída de git stash pop
e você terá o ID do objeto do cair esconderijo. Ele normalmente se parece com isso:
$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)
(Note que git stash drop
também produz a mesma linha.)
Para obter essa volta stash, git branch tmp 2cae03e
apenas correr, e você vai obtê-lo como um ramo. Para converter isso em um esconderijo, execute:
git stash apply tmp
git stash
Tê-lo como um ramo também permite que você manipulá-lo livremente; por exemplo, para cereja escolher-la ou fundi-lo.
Apenas queria mencionar esta adição à solução aceite. Não ficou imediatamente óbvio para mim a primeira vez que eu tentei este método (talvez deveria ter sido), mas para aplicar o esconderijo do valor de hash, é só usar "git stash de aplicar":
$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Quando eu era novo para git, este não foi claro para mim, e eu estava tentando diferentes combinações de "show git", "git aplicar", "patch", etc.
Para obter a lista de esconderijos que ainda estão em seu repositório, mas não acessível mais:
git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP
Se você deu um título para o seu esconderijo, substitua "WIP" em -grep=WIP
no final do comando com uma parte de sua mensagem, por exemplo, -grep=Tesselation
.
O comando é grepping para "WIP" porque o padrão mensagem de confirmação para um estoque está na forma WIP on mybranch: [previous-commit-hash] Message of the previous commit.
Eu só construiu um comando que me ajudou a encontrar meu dinheiro perdido cometer:
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
Tudo isso lista os objetos no .git / objetos árvore, localiza os que são do tipo cometer, em seguida, mostra um resumo de cada um. A partir deste ponto, foi apenas uma questão de olhar através dos commits para encontrar um "WIP no trabalho: 6a9bb2" apropriado. ( "Trabalho" é o meu ramo, 619bb2 é uma recente commit)
Noto que se eu usar "git stash de aplicar" em vez de "git stash de pop" Eu não teria esse problema, e se eu usar "git stash save Mensagem ", então o poder comprometer ter sido mais fácil de encontrar.
Update: Com a ideia de Nathan, isso se torna mais curto:
for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
git fsck --unreachable | grep commit
deve mostrar o sha1, embora a lista retorna pode ser muito grande. git show <sha1>
vai mostrar se é a cometer quiser.
git cherry-pick -m 1 <sha1>
vai mesclar a cometer para o ramo atual.
Se você quiser restash um estoque perdido, você precisa encontrar o hash de seu esconderijo perdido em primeiro lugar.
Como Aristóteles Pagaltzis sugeriu uma git fsck
deve ajudá-lo.
Pessoalmente eu uso o meu alias log-all
que mostram me cada commit (commits recuperáveis) para ter uma melhor visão da situação:
git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)
Você pode fazer um ainda mais rápido procurar se você está olhando apenas para "WIP em" mensagens.
Uma vez que você sabe que seu sha1, você simplesmente mudar a sua reflog esconderijo para adicionar o antigo esconderijo:
git update-ref refs/stash ed6721d
Você provavelmente vai preferir ter uma mensagem associada de modo a -m
git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d
E você vai mesmo querer usar isso como um apelido:
restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1
Windows PowerShell equivalente usando gitk:
gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })
Há provavelmente uma maneira mais eficiente de fazer isso em um tubo, mas isso faz o trabalho.
Eu gostava abordagem de Aristóteles, mas não o fez como usar gitk ... como eu estou acostumado a usar GIT a partir da linha de comando.
Em vez disso, eu levei os commits pendentes e saída o código em um arquivo DIFF para revisão no meu editor de código.
git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff
Agora você pode carregar o / arquivo resultante diff txt (sua em sua pasta pessoal) em seu editor de txt e ver o código real e resultando SHA.
Em seguida, basta usar
git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Em OSX com v2.6.4 git, eu só correr queda git stash acidentalmente, então eu descobri-lo indo cocho passos abaixo
Se você sabe o nome do esconderijo, em seguida, usar:
$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>
Caso contrário, encontrará ID do resultado por manualmente com:
$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show
Então, quando você encontrar o comprometer-id é só apertar o git stash aplicar {id comprometer-}
Espero que isso ajude alguém rapidamente
Por que as pessoas fazem essa pergunta? Porque eles ainda não conhecem ou entendem a reflog.
A maioria das respostas a esta pergunta dar comandos longos com opções quase ninguém vai se lembrar. Então, as pessoas entram em esta questão e copiar e colar o que eles acham que precisam e esquecê-lo quase imediatamente depois.
Eu aconselharia a todos com esta questão apenas para verificar o reflog (git reflog), não muito mais do que isso. Depois de ver que a lista de todos os commits há cem maneiras de descobrir o que cometem você está procurando e cherry-pick-lo ou criar um núcleo a partir dele. No processo, você terá aprendido sobre os reflog e úteis opções para vários comandos básicos git.
Você pode listar todas as submissões inacessíveis por escrever este comando no Terminal -
git fsck --unreachable
Verifique inacessível comprometer de hash -
git show hash
Finalmente, aplicar se você encontrar o item Stashed -
git stash apply hash
Eu quero adicionar à solução aceita outra boa maneira de passar por todas as mudanças, quando você não quer ter gitk disponíveis ou nenhuma X para a saída.
git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits
for h in `cat tmp_commits`; do git show $h | less; done
Em seguida, você recebe todos os diffs para os hashes exibidos um após o outro. Pressione 'q' para chegar ao próximo diff.
A resposta aceite por Aristóteles vai mostrar todos os commits alcançáveis, incluindo os não-stash-like commits. Para filtrar o ruído:
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
Isso só irá incluir commits que têm exatamente 3 commits pais (que um estoque terá), e cuja mensagem inclui "WIP on".
Tenha em mente que, se você salvou o seu esconderijo com uma mensagem (por exemplo git stash save "My newly created stash"
), este irá substituir o "WIP em ..." default mensagem.
Você pode exibir mais informações sobre cada commit, por exemplo, exibir a mensagem de commit, ou passá-lo para 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
Eu não poderia obter qualquer uma das respostas para funcionar no Windows em uma janela de comando simples (Windows 7 no meu caso). awk
, grep
e Select-string
não foram reconhecidos como comandos. Então, eu tentei uma abordagem diferente:
- primeira corrida:
git fsck --unreachable | findstr "commit"
- copiar a saída para o bloco de notas
- Encontrar substituir "inacessível comprometer" com
start cmd /k git show
será algo parecido com isto:
start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4
start cmd /k git show 44078733e1b36962571019126243782421fcd8ae
start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1
start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e
- salvar como um arquivo .bat e executá-lo
- o script vai abrir um monte de janelas de comando, mostrando cada commit
- Se você encontrou o que você está procurando, run:
git stash apply (your hash)
pode não ser a melhor solução, mas funcionou para mim
O que eu vim aqui procurando como realmente obter a volta stash, independentemente do que eu verifiquei para fora. Em particular, eu tinha escondido alguma coisa, então check-out uma versão mais antiga, então poped, mas o esconderijo foi um não-op nesse ponto época anterior, de modo que o estoque desapareceu; Eu não podia simplesmente fazer git stash
para empurrá-lo de volta na pilha. Isso funcionou para mim:
$ 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.
Em retrospecto, eu deveria ter usado git stash apply
não git stash pop
. Eu estava fazendo um bisect
e tinha um pouco patch que eu queria aplicar a cada passo bisect
. Agora eu estou fazendo isso:
$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
Recuperado lo usando seguintes etapas:
-
Identificar o código de hash esconderijo excluído:
gitk --all $ (git fsck --no-reflog | awk '/ pendurada commit / {print $ 3}')
-
Cherry Escolha o Stash:
cherry-pick git -m 1 $ stash_hash_code
-
Resolver conflitos se houver usando:
git mergetool
Além disso, você pode estar tendo problemas com mensagem de confirmação se você estiver usando gerrit. Por favor Stash suas alterações antes de seguir próximos alternativas:
- Use hard reset para submissão anterior e depois comprometer essa mudança.
- Você também pode esconder a mudança, rebase e recommit.
Eu fiz acidentalmente removido do esconderijo em GitUP aplicativo. basta pressionar Ctrl + Z para desfazer-lo.
Talvez ele ajuda a alguém;)