Anulando um pop oculto no Git

257

Coloquei um esconderijo e houve um conflito de mesclagem. Diferente da pergunta que está listada como duplicada, eu já tinha algumas alterações não confirmadas no diretório que queria manter. Não quero apenas fazer desaparecer o conflito de mesclagem, mas também recuperar meu diretório para o estado que estava antes do pop.

Eu tentei git merge --abort, mas o git alegou que nenhuma fusão estava em andamento. Existe uma maneira fácil de abortar um pop sem destruir as alterações que eu originalmente tive no diretório?

Casebash
fonte
Quanto às suas alterações não confirmadas: essas alterações já estão no índice?
Jorgensen
Você pode postar a versão do git que está usando.
Tinman
2
A resposta aceita parece complicada. Eu acho que é uma boa prática geral nunca fazer algo como tentar git stash popum diretório de trabalho imundo. Nesse caso, você pode simplesmente git reset --harde seu estoque ainda está intacto. (Isso é mais ou menos o que @ topic ligada de BradKoch sugere)
Steven Lu
1
@StevenLu, eu concordo, mas o problema pode ocorrer em um diretório de trabalho limpo, se você estiver ocultando as alterações para movê-las para uma ramificação diferente. O stash entra em conflito com confirmações existentes no novo ramo que não existia no antigo.
Jake Stevens-Haas

Respostas:

55

Ok, acho que elaborei o "git stash unapply". É mais complexo do que git apply --reverseporque você precisa de uma ação de mesclagem reversa, caso haja alguma mesclagem feita pelo git stash apply.

A mesclagem reversa requer que todas as alterações atuais sejam inseridas no índice:

  • git add -u

Inverta então o merge-recursiveque foi feito por git stash apply:

  • git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1

Agora você terá apenas as alterações que não são de esconderijo. Eles estarão no índice. Você pode usar git resetpara desestabilizar suas alterações, se quiser.

Dado que o original git stash applyfalhou, presumo que o inverso também pode falhar, pois algumas das coisas que ele deseja desfazer não foram feitas.

Aqui está um exemplo mostrando como a cópia de trabalho (via git status) acaba limpa novamente:

 $ git status
# On branch trunk
nothing to commit (working directory clean)
 $ git stash apply
Auto-merging foo.c
# On branch trunk
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   foo.c
#
no changes added to commit (use "git add" and/or "git commit -a")
 $ git add -u
 $ git merge-recursive stash@{0}: -- $(git write-tree) stash@{0}^1
Auto-merging foo.c
 $ git status
# On branch trunk
nothing to commit (working directory clean)
Ben Jackson
fonte
2
depois de fazer isso, as edições armazenadas anteriormente serão perdidas para sempre ou elas estarão de volta?
Brian H.
7
git stash applynunca cai um esconderijo, e quando uma mesclagem falhar, então git stash poptambém mantém o esconderijo
Xerus
Coisas ruins vão acontecer se houvesse um conflito de mesclagem durante o pop esconderijo originais
3ocene
286

Meu caso de uso: tentei entrar no ramo errado e ter conflitos. Tudo o que preciso é desfazer o pop, mas mantê-lo na lista de stash para que eu possa colocá-lo na ramificação correta. Eu fiz isso:

git reset HEAD --hard
git checkout my_correct_branch
git stash pop

Fácil.

jmoz
fonte
22
Portanto, o estoque que você deseja permanece na lista de itens até que você tenha um pop de sucesso?
Ryan Clark
52
Esta não é uma resposta para a pergunta original, pois limparia as alterações locais que não foram confirmadas.
Dmitry
13
@RyanClark Veja a resposta de DavidG abaixo. Basicamente, sim, ele permanece na lista de stash.
Fkorsa
1
Eu acho que a grande maioria dos usuários que se encontram nessa situação achará essa solução ideal. Não consigo imaginar uma situação em que não tenha confirmado alterações no meu diretório de trabalho antes de tentar stash popentrar nele. Isso soa como uma receita para o desastre.
Shadoninja
2
Agradável. Depois de ler isso, observei os documentos pop do git stash e ele diz "A aplicação do estado pode falhar com conflitos; nesse caso, ele não é removido da lista de stash". Portanto, é por isso que o stash pode ser reaberto após uma redefinição / checkout.
Brady Holt
48

Editar: A partir da git help stashdocumentação na seção pop:

A aplicação do estado pode falhar com conflitos; nesse caso, não é removido da lista stash. Você precisa resolver os conflitos manualmente e chamar o git stash drop manualmente posteriormente.

Se a opção --index for usada, tente restabelecer não apenas as alterações da árvore de trabalho, mas também as do índice. No entanto, isso pode falhar quando você possui conflitos (que são armazenados no índice e, portanto, não é mais possível aplicar as alterações como eram originalmente).

Tente copiar todos os seus repositórios em um novo diretório (para ter uma cópia dele) e execute:

git stash show e salve essa saída em algum lugar, se você se importa.

then: git stash droppara soltar o estoque conflitante, então:git reset HEAD

Isso deve deixar seu repositório no estado em que estava antes (espero que ainda não tenha sido capaz de reprovar seu problema)

===

Estou tentando reprovar seu problema, mas tudo o que recebo quando usa git stash popé:

error: Your local changes to the following files would be overwritten by merge:
...
Please, commit your changes or stash them before you can merge.
Aborting

Em um dir limpo:

git init
echo hello world > a
git add a & git commit -m "a"
echo hallo welt >> a
echo hello world > b
git add b & git commit -m "b"
echo hallo welt >> b
git stash
echo hola mundo >> a
git stash pop

Não vejo o git tentando mesclar minhas alterações, apenas falha. Você tem alguma etapa de reprodução que possamos seguir para ajudá-lo?

DavidG
fonte
Tente ocultar as alterações em um arquivo diferente.
asmeurer
Tentar esconder em um arquivo diferente não funcionou (consulte as novas etapas de reprodução). Eu apenas não posso repro a questão ...
DavidG
1
Esta solução não funciona se houver alterações não confirmadas no diretório de trabalho.
aqui
@here Esta não é uma solução real, é apenas para mostrar que não consigo reproduzir o problema que o OP tem e que ele não forneceu nenhuma etapa para chegar aonde está.
DavidG
1
O cenário real é: 1) Altere o arquivo A. 2) Alterações do stash 3) Faça alterações conflitantes no arquivo A e confirme (por exemplo, altere a mesma linha) 4) Altere o arquivo B 5) Faça 'git stash pop'. Agora você tem conflitos e alterações locais. Geralmente, eles estarão em arquivos diferentes, mas você nunca saberá quais arquivos modificados não conflitantes do stash e quais são as alterações locais não estaduais.
Dmitry
17

Eu sempre usei

git reset --merge

Não me lembro de alguma vez ter falhado.

Kenn Sebesta
fonte
@GauravPaliwal, vou dar uma olhada na próxima vez que eu git reset. Você sabe se é funcionalmente idêntico a git reset --merge?
Kenn Sebesta
5

Se você não precisar se preocupar com outras alterações feitas e quiser voltar ao último commit, poderá:

git reset .
git checkout .
git clean -f
hugo der hungrige
fonte
4

OK, acho que consegui encontrar um fluxo de trabalho que o levará de volta para onde você precisa estar (como se você não tivesse feito o pop).

FAÇA UM BACKUP ANTES !! Não sei se isso funcionará para você, então copie todo o seu repositório, caso não funcione.

1) Corrija os problemas de mesclagem e corrija todo o conflito, selecionando todas as alterações que vêm do patch (no caso de emergências, isso aparece como um.REMOETE (deles)).

git mergetool

2) Confirme essas alterações (elas já serão adicionadas pelo comando mergetool). Dê a ele uma mensagem de confirmação de "mesclagem" ou algo que você se lembre.

git commit -m "merge"

3) Agora você ainda terá as alterações locais sem estágio que você iniciou originalmente, com uma nova confirmação do patch (podemos nos livrar disso mais tarde). Agora confirme suas alterações sem etapas

git add .
git add -u .
git commit -m "local changes"

4) Inverta o patch. Isso pode ser feito com o seguinte comando:

git stash show -p | git apply -R

5) Confirme essas alterações:

git commit -a -m "reversed patch"

6) Livre-se dos consertos de patch / unpatch

git rebase -i HEAD^^^

disso, remova as duas linhas com 'mesclar' e 'patch invertido'.

7) Recupere suas alterações sem alterações e desfaça o commit das 'alterações locais'

git reset HEAD^

Eu o examinei com um exemplo simples e o leva de volta para onde você quer estar - imediatamente antes do stash ser exibido, com as alterações locais e com o stash ainda disponível para pop.

agentgonzo
fonte
Se isso não funcionar, esperamos que você o leve até lá! :-)
agentgonzo
git stash show -p | git apply -Risso não funciona se a git stash applyfusão foi real. Veja minha resposta ...
Ben Jackson
3

Eu resolvi isso de uma maneira um pouco diferente. Aqui está o que aconteceu.

Primeiro, entrei no ramo errado e tive conflitos. O estoque permaneceu intacto, mas o índice estava em resolução de conflitos, bloqueando muitos comandos.

Um simples git reset HEADcancelou a resolução de conflitos e deixou os não comprometidos (e alterações INDESEJADAS ).

Vários git co <filename>reverteram o índice para o estado inicial. Por fim, troquei o branch git co <branch-name>e executei um novo git stash pop, que foi resolvido sem conflitos.

pid
fonte
2

Algumas ideias:

  • Use git mergetoolpara dividir os arquivos de mesclagem em peças originais e novas. Esperemos que um deles seja o arquivo com as alterações que não são de esconderijo.

  • Aplique o diff do stash ao contrário, para desfazer apenas essas alterações. Você provavelmente terá que dividir manualmente os arquivos com os conflitos de mesclagem (que esperamos que o truque acima funcione).

Eu não testei nenhum deles, então não tenho certeza se eles funcionarão.

asmeurer
fonte
1

Eu poderia reproduzir limpo git stash popno diretório "sujo", com alterações não confirmadas, mas ainda não pop que gera um conflito de mesclagem.

Se em conflito de mesclagem o stash que você tentou aplicar não desapareceu, você pode tentar examinar git show stash@{0}(opcionalmente com --oursou --theirs) e comparar com git statise git diff HEAD. Você deve poder ver quais alterações vieram da aplicação de uma ocultação.

Jakub Narębski
fonte
1

Se o DavidG estiver certo de que não colocou o stash por causa do conflito de mesclagem, basta limpar o diretório de trabalho. Rapidamente git committudo o que você gosta. (Você pode resetou squashconfirmar mais tarde, se não tiver terminado.) Depois, com tudo o que você gosta com segurança, git resettudo o mais que foi git stash popdespejado no diretório de trabalho.

Robert
fonte
1

Se não houve alterações faseadas antes do git stash pop, como na pergunta, os dois comandos a seguir devem funcionar.

git diff --name-only --cached | xargs git checkout --ours HEAD
git ls-tree stash@{0}^3 --name-only | xargs rm

O primeiro reverte qualquer mesclagem do stash, bem-sucedida ou não. O segundo exclui todos os arquivos não rastreados introduzidos pelo stash.

De man git stash: The working directory must match the index. Que @DavidG aponta, o stash poperro falhará se houver conflito de arquivos modificados no momento. Como tal, não devemos nos preocupar em desanuviar conflitos de mesclagem, além do retorno a eles HEAD. Todos os arquivos modificados restantes não são relacionados ao stash e foram modificados antes dostash pop

Se houve mudanças em etapas, não estou claro se podemos confiar nos mesmos comandos e você pode tentar a técnica de @Ben Jackson. Sugestões apreciadas ..

Aqui está uma configuração de teste para todos os vários casos https://gist.github.com/here/4f3af6dafdb4ca15e804

# Result:
# Merge succeeded in m (theirs)
# Conflict in b
# Unstaged in a
# Untracked in c and d

# Goal:
# Reverse changes to successful merge m
# Keep our version in merge conflict b
# Keep our unstaged a
# Keep our untracked d
# Delete stashed untracked c
aqui
fonte
Eu acho que essa é a resposta mais correta até agora. Muito bem pensado.
Agent Friday
0

Use git reflogpara listar todas as alterações feitas no seu histórico do git. Copie um ID de ação e digitegit reset ACTION_ID

Fatih Acet
fonte
2
Git não criar uma entrada reflog antes de aparecer (você precisa ter um commit)
Casebash
-1

Estou postando aqui esperando que outras pessoas achem minha resposta útil. Eu tive um problema semelhante ao tentar fazer um stash pop em um galho diferente do que eu havia escondido. No meu caso, eu não tinha arquivos não confirmados ou no índice, mas ainda assim entrei no caso de conflitos de mesclagem (o mesmo caso @pid). Como outros salientaram anteriormente, o pop com falha do git stash realmente reteve o meu stash, então um git rápido redefiniu HEAD mais voltando ao meu ramo original e fazendo o stash a partir daí resolveu o meu problema.

Victor Camacho
fonte