git stash e aplique

Eu sou novo para git e não muito claro sobre como stashing funciona.

Digamos que eu esteja trabalhando no branch master e tente git pull e receber o erro de que minhas alterações locais seriam sobrescritas e precisam ser escondidas ou confirmadas. Se eu não fiz nenhuma das minhas alterações e executar o git stash , faça um git pull e atualize com sucesso, o que acontece quando eu git stash apply ?

Em geral, se alguém modifica arquivos e eu executo git pull , o que acontece quando eu run git stash apply ? ele sobrescreve os arquivos que acabaram de ser atualizados, independentemente de terem ou não sido testados quando eu os escondi? Sobrescreve todos os arquivos que acabei de atualizar com o git pull , com os arquivos que foram armazenados?

Versão rápida “TL; DR”, assim, pode-se voltar mais tarde e estudar mais

git stash trava um stash-bag – essa é uma forma peculiar de uma consolidação de mesclagem que não está em qualquer ramificação – no commit atual do HEAD . Um git stash apply posterior se git stash apply , quando você está em qualquer commit – provavelmente um commit diferente – então tenta restaurar as mudanças git computa olhando o stash-bag pendente e o commit que ele trava.

Quando você terminar as mudanças, você deve usar o git stash drop para liberar o stash-bag do commit que ele estava “stashed on”. (E, git stash pop é apenas uma abreviação de “apply, then automatically drop”. Recomendo manter as duas etapas separadas, no caso de você não gostar do resultado de “apply” e desejar tentar novamente mais tarde).

A versão longa

git stash é realmente bastante complexo.

Tem sido dito que “git faz muito mais sentido quando você entende X” , para muitos valores diferentes de “X”, o que generaliza para “git faz muito mais sentido quando você entende git”. 🙂

Neste caso, para realmente entender o stash , você precisa entender como commits, branches, o índice / staging-area, o espaço de nome de referência do git e mescla todo o trabalho, porque git stash cria um commit merge muito peculiar que é referido por um nome fora dos espaços de nomes usuais – um tipo estranho de mesclagem que não está “em uma ramificação” – e o git stash apply usa o mecanismo de mesclagem do git para tentar “reaplicar” as alterações salvas quando o commit peculiar de mesclagem foi feitas, opcionalmente, preservando a distinção entre alterações em etapas e não organizadas.

Felizmente , você não precisa realmente entender tudo isso para usar o git stash .

Aqui, você está trabalhando em alguma ramificação ( master ) e tem algumas alterações que ainda não estão prontas, então você não quer enviá-las para o branch. 1 Enquanto isso, alguém coloca algo bom – ou, pelo menos, você espera que seja bom – na origin/master no repository remoto, então você quer pegá-los.

Digamos que você e os dois começaram com commits que terminam em - A - B - C , isto é, C é o commit final que você tinha em seu repository quando começou a trabalhar no branch master . O novo “algo bom” compromete, vamos chamar D e E

No seu caso, você está executando git pull e ele falha com o problema “diretório de trabalho não limpo”. Então, você executa o git stash . Isso compromete suas coisas para você, em sua forma estranha e estranha, para que seu diretório de trabalho esteja limpo agora. Agora você pode git pull .

Em termos de desenho de commits (um gráfico como você pega com gitk ou git log --graph ), agora você tem algo assim. O stash é o pequeno bag-of- iw pendurado no commit em que você estava “ligado”, no seu branch master , quando você rodou git stash . (A razão para os nomes i e w é que estes são o “i” ndex / staging-area e “w” ork-tree partes do stash.)

 - A - B - C - D - E < -- HEAD=master, origin/master |\ iw <-- the "stash" 

Este desenho é o que você obtém se você começou a trabalhar no master e nunca fez nenhum commit. O commit mais recente que você teve foi, portanto, C Depois de fazer o stash, git pull foi capaz de adicionar commits D e E ao seu master filial local. O saco de trabalho escondido ainda está pendurado C

Se você fez alguns commits - nós vamos chamá-los de Y , para o commit, e Z apenas para dois commits - o resultado do "stash then pull" é o seguinte:

  .-------- origin/master - A - B - C - D - E - M < -- HEAD=master \ / Y - Z |\ iw <-- the "stash" 

Dessa vez, depois que o stash pendurou sua mochila em Z , a pull - que é apenas fetch depois merge - tinha que fazer uma mesclagem real, em vez de apenas um "avanço rápido". Por isso, faz commit M , o commit da mesclagem. O label de origin/master ainda se refere a commit E , não M Você está agora no master no commit M , que é uma mesclagem de E e Z Você está "à frente" da origin/master .

Em ambos os casos, se você executar agora git stash apply , o script stash (é um script de shell que usa muitos comandos git "encanamento" de baixo nível) efetivamente faz isso:

 git diff stash^ stash > /tmp/patch git apply /tmp/patch 

Isso difere stash , que nomeia w - a parte da "tree de trabalho" do stash - contra o pai correto. Em outras palavras, ele descobre "o que você mudou" entre o commit pai apropriado ( C ou Z , conforme apropriado) e a tree de trabalho stash. Em seguida, aplica as alterações à versão atualmente com check-out, que é E ou M , novamente dependendo de onde você começou.

Aliás, git stash show -p realmente apenas executa o mesmo comando git diff (sem parte > /tmp/patch claro). Sem -p , executa o diff com --stat . Então, se você quiser ver em detalhes o que o git stash apply irá se fundir, use git stash show -p . (Isso não mostrará o que o git stash apply pode tentar aplicar a partir da parte do índice do stash, embora; esta é uma queixa menor que eu tenho com o script stash.)


Em qualquer caso, uma vez que o stash se aplique de forma limpa, você pode usar o git stash drop para remover a referência ao stash-bag, para que ele possa ser coletado como lixo. Até você soltá-lo, ele tem um nome ( refs/stash , também conhecido como stash@{0} ), então fica em torno de "para sempre" ... exceto pelo fato de que se você fizer um novo stash, o script stash "empurra" o stash atual no reflogo stash (para que seu nome se torne stash@{1} ) e faz com que o novo stash use o nome refs/stash . A maioria das inputs de reflogo permanece por 90 dias (você pode configurar isso para ser diferente) e expirar. Stashes não expiram por padrão, mas se você configurar isso de outra forma, um stash "push" pode se perder, então tenha cuidado dependendo de "save forever" se você começar a configurar o git ao seu gosto.

Note que git stash drop "mostra" a pilha stash aqui, renumerando stash@{2} para stash@{1} e fazendo stash@{1} se tornar stash simples. Use git stash list para ver o stash stack.


1 Não é ruim ir em frente e comê-los de qualquer maneira, e depois fazer um git rebase -i posterior git rebase -i para esmagar ou consertar mais segundo, terceiro, quarto, ..., nth commits e / ou rewrite o commit temporário de "checkpoint". Mas isso é independente disso.

2 É um pouco mais complexo, porque você pode usar --index para tentar manter as alterações em etapas, mas, na verdade, se você olhar no script, você verá a seqüência de comando real git diff ... | git apply --index git diff ... | git apply --index . Realmente aplica apenas um diff, neste caso! Eventualmente, ele invoca git merge-recursive diretamente, no entanto, para mesclar na tree de trabalho, permitindo que as mesmas mudanças tenham sido trazidas de outro lugar. Uma aplicação simples do git apply falharia se o seu patch fizesse algo do tipo "coisas boas" comete D e E também.

3 Isso usa a syntax mágica de nomeação dos pais do git, com um pouco de planejamento antecipado dentro do script stash . Como o stash é esse funk merge commit, w tem dois ou até três pais, mas o script stash o configura para que o "first parent" seja o commit original, C ou Z , conforme apropriado. O "segundo pai" stash^2 é o estado do índice no momento do commit, mostrado como i no pequeno stash-bag suspenso, e o "terceiro pai", se existir, é um arquivo não sincronizado e talvez ignorado , do git stash save -u ou git stash save -a .

Observe que, nesta resposta, presumo que você não tenha cuidadosamente colocado parte de sua tree de trabalho e que não esteja usando git stash apply --index para restaurar o índice preparado. Por não fazer nada disso, você torna o commit muito redundante, de modo que não precisamos nos preocupar com isso durante a etapa de apply . Se você estiver usando apply --index ou equivalente, e tiver itens encenados, você poderá entrar em muito mais casos de canto, onde o stash não será aplicado de forma limpa.

Essas mesmas advertências se aplicam, com mais casos de canto, aos stashes salvos com -u ou -a , que possuem o terceiro commit.

Para esses casos git stash , git stash fornece uma maneira de transformar um stash em um branch completo - mas vou deixar tudo isso para outra resposta.

Geralmente, mudanças não confirmadas são sempre ruins. Ou suas mudanças são boas, depois as cometem ou são ruins do que descartá-las. Fazer quaisquer operações do git enquanto estiver com alterações não confirmadas tende a causar problemas e o git não poderá ajudá-lo, pois o git não sabe sobre nada que você não confirmou.

Dito isto, voltemos à sua pergunta. 😉

Git é geralmente muito esperto. Quando você aplica seu stash, ele tenta mesclar suas alterações com as outras alterações. Na maioria das vezes isso simplesmente funciona.

Se as mudanças realmente conflitarem, porque você mudou as mesmas linhas de uma maneira diferente, o git lhe dirá, e você terá que resolver o conflito sozinho. – Mesmo neste caso, o git irá ajudá-lo com o git mergetool , que irá lançar um comando adequado para mostrar os conflitos e permitir que você os resolva um por um.

o comando stash git lembra de onde vem o stash:

  git stash list 

fora colocado

  stash@{0}: WIP on master.color-rules.0: 35669fb [NEW] another step toward initial cube 

Onde você pode ver em qual SHA1 foi feito. Então, se você git stash, git pull, git stash se aplicam e você tem um conflito, o stash não é descartado (somente se você cair ou se a requisição for bem sucedida). Então você sempre pode obter o SHA1 da lista de stits git e

  git checkout 35669fb git stash apply 

e é garantido que funcione. Eu recomendo usar a opção -b e fornecer um nome de ramificação para essa recuperação.

Dito isto, o meu stream de trabalho favorito é sempre check-out no novo nome “pessoal” para evitar tais problemas