Como um pouco de explicação adicional, note que git stashfaz dois commits ou três commits. O padrão é dois; você ganha três se usar qualquer grafia das opções --allou --include-untracked.
Esses dois ou três commits são especiais de uma maneira importante: eles não estão em nenhum branch. Git os localiza por meio do nome especial stash. 1 A coisa mais importante, entretanto, é o que o Git permite - e o faz - fazer com esses dois ou três commits. Para entender isso, precisamos olhar o que está nesses commits.
O que há dentro de um esconderijo
Cada commit pode listar um ou mais commits pais . Eles formam um gráfico, onde os commits posteriores apontam para os anteriores. O stash normalmente contém dois commits, que eu gosto de chamar ipara o conteúdo da área de teste / índice e wpara o conteúdo da árvore de trabalho. Lembre-se também de que cada commit contém um instantâneo. Em uma confirmação normal, este instantâneo é feito do índice / conteúdo da área de teste. Portanto, o icommit é de fato um commit perfeitamente normal! Simplesmente não está em qualquer ramo:
...--o--o--o <-- branch (HEAD)
|
i
Se você está fazendo um estoque normal, o git stashcódigo o faz wcopiando todos os seus arquivos da árvore de trabalho rastreados (em um índice auxiliar temporário). Git configura o primeiro pai deste wcommit para apontar para o HEADcommit e o segundo pai para apontar para o commit i. Por último, ele define stashpara apontar para este wcommit:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
Se você adicionar --include-untrackedou --all, o Git fará um commit extra,, uentre fazer ie w. O conteúdo do instantâneo usão aqueles arquivos que não são rastreados, mas não são ignorados ( --include-untracked), ou arquivos que não são rastreados, mesmo que sejam ignorados ( --all). Este ucommit extra não tem pai, e então quando git stashé feito w, ele configura wo terceiro pai para este ucommit, de forma que você obtém:
...--o--o--o <-- branch (HEAD)
|\
i-w <-- stash
/
u
Git também, neste ponto, remove todos os arquivos da árvore de trabalho que terminaram no ucommit (usando git cleanpara fazer isso).
Restaurando um estoque
Ao restaurar um estoque, você tem a opção de usá-lo --indexou não. Isso diz git stash apply(ou qualquer um dos comandos que usa internamente apply, como pop) que deve usar o icommit para tentar modificar seu índice atual. Essa modificação é feita com:
git diff <hash-of-i> <hash-of-i's-parent> | git apply --index
(mais ou menos; há um monte de detalhes essenciais que atrapalham a ideia básica aqui).
Se você omitir --index, git stash applyignora completamente o icommit.
Se o stash tiver apenas dois commits, git stash applyagora pode aplicar o wcommit. Ele faz isso chamando git merge2 (sem permitir que ele comprometa ou trate o resultado como uma mesclagem normal), usando o commit original no qual o stash foi feito ( io pai de, e wo primeiro pai de) como a base de mesclagem, wcomo o --theirscommit, e seu commit atual (HEAD) como o alvo da fusão. Se a fusão for bem-sucedida, tudo estará bem - bem, pelo menos Git pensa assim - e o git stash applypróprio sucesso. Se você costumava git stash popaplicar o stash, o código agora descarta o stash. 3 Se a fusão falhar, o Git declara que a aplicação falhou. Se você usougit stash pop, o código retém o stash e entrega o mesmo status de falha de git stash apply.
Mas se você tiver aquele terceiro commit - se houver um ucommit no stash que você está aplicando - então as coisas mudam! Não há opção de fingir que o ucommit não existe. 4 Git insiste em extrair todos os arquivos de que ucometeu, na obra-árvore atual. Isso significa que os arquivos não devem existir ou ter o mesmo conteúdo do ucommit.
Para fazer isso acontecer, você pode usar git cleanvocê mesmo - mas lembre-se de que arquivos não rastreados (ignorados ou não) não têm outra existência dentro de um repositório Git, portanto, certifique-se de que todos esses arquivos possam ser destruídos! Ou você pode criar um diretório temporário e mover os arquivos para segurança - ou até mesmo fazer outro git stash save -uou git stash save -a, já que esses serão executados git cleanpara você. Mas isso só deixa você com outro uestoque de estilo para lidar mais tarde.
1 Isso é de fato refs/stash. Isso é importante se você criar um branch chamado stash: o nome completo do branch é refs/heads/stash, portanto, eles não estão em conflito. Mas não faça isso: o Git não se importará, mas você se confundirá. :-)
2 Na git stashverdade, o código usa git merge-recursivediretamente aqui. Isso é necessário por vários motivos e também tem o efeito colateral de garantir que o Git não o trate como uma mesclagem quando você resolver conflitos e enviar commit.
3 É por isso que recomendo evitar git stash pop, em favor de git stash apply. Você tem a chance de revisar o que foi aplicado e decidir se foi realmente aplicado corretamente. Se não, você ainda tem seu estoque, o que significa que pode usar git stash branchpara recuperar tudo perfeitamente. Bem, supondo a falta daquele ucommit irritante .
4 Realmente deveria haver: git stash apply --skip-untrackedou algo assim. Também deve haver uma variante que significa colocar todos os uarquivos de commit em um novo diretório , por exemplo git stash apply --untracked-into <dir>, talvez.
git stash show -p | git apply --3