O uso do Git Stash como fluxo de trabalho é um antipadrão?

25

Recentemente, observei como eu e minha equipe usamos o Git e como nossos fluxos de trabalho funcionam. Atualmente, usamos um fluxo de trabalho de ramificação de recursos que parece funcionar bem.

Também vi algumas pessoas em nossa equipe usarem o fluxo de trabalho com base no git stash . O fluxo de trabalho é mais ou menos assim:

  • Trabalhar em uma ramificação principal (como master)
  • Faça confirmações à medida que avança
  • Se você precisar fazer alterações ou alternar ramificações, empurre as alterações não confirmadas para o esconderijo
  • Depois que a atualização estiver concluída, retire as alterações do esconderijo.

Devo mencionar que esse fluxo de trabalho é usado em vez de um fluxo de trabalho da ramificação do recurso. Em vez de pegar uma ramificação e trabalhar nela, os desenvolvedores aqui apenas trabalham em uma única ramificação e empurram / saem da pilha como bem entenderem.

Na verdade, não acho que esse seja um ótimo fluxo de trabalho, e a ramificação seria mais apropriada do que usar o git stash dessa maneira. Consigo ver o valor do git stash como uma operação de emergência, mas não para usá-lo em um fluxo de trabalho diário e regular.

Usar o git stash regularmente seria considerado um anti-padrão? Em caso afirmativo, quais são alguns problemas específicos que podem surgir? Caso contrário, quais são os benefícios?

joshin4colours
fonte
2
Você pode tentar perguntar a seus colegas de trabalho se eles tiveram algum problema com o fluxo de trabalho. Se não o fizeram, eu não consideraria prejudicial.
AlexFoxGill 02/09/2014
@AlexG Esse é um ponto válido. Estou fazendo a pergunta aqui para ver se existem "truques" que as pessoas encontraram.
Joshin4colours #
3
Se entendi direito ... If you need to get changes or switch branches, push your uncommitted changes onto the stash- é exatamente para isso que serve.
3
@ MichaelT Meu ramo local permite tudo. Incondicionalmente.
maaartinus
2
Meu padrão é: -> não use git stash -> use ramos de recursos -> salve o trabalho em andamento com uma mensagem de confirmação de 'wip'. -> rebase interativo no ramo de esmagar o wip de em um commit significativa mas para os seus , locais unpushed únicas mudanças. -> pressione para remoto -> entre no master (e pressione) conforme apropriado para o seu fluxo de trabalho do git.
Michael Durrant

Respostas:

31

Do livro do Git SCM :

Freqüentemente, quando você trabalha em parte de seu projeto, as coisas estão em um estado confuso e você deseja alternar entre ramificações um pouco para trabalhar em outra coisa. O problema é que você não deseja confirmar o trabalho pela metade apenas para voltar a esse ponto mais tarde. A resposta para esse problema é o comando git stash.

O armazenamento em cache leva o estado sujo do seu diretório de trabalho - ou seja, os arquivos rastreados modificados e as alterações faseadas - e o salva em uma pilha de alterações inacabadas que você pode aplicar novamente a qualquer momento.

Dada esta descrição, eu diria que este é um anti-padrão. Uma explicação excessivamente simplificada do Git Stash seria que ele é o "Recortar e colar" do controle de origem. Você pega vários arquivos alterados, "os esconde" em uma caneta de exploração fora do fluxo de trabalho normal de ramificação do Git e depois reaplica essas alterações em um ramo diferente posteriormente.

Voltando um pouco mais longe, comprometer-se a dominar é o anti-padrão aqui. Use ramos. Foi para isso que eles foram projetados.

Realmente se resume a isso:

Você pode martelar um parafuso na parede e ele exibirá uma foto, mas usar uma chave de fenda é o que você deve fazer. Não use um martelo quando a chave de fenda estiver bem ao seu lado.

Sobre como confirmar o código "quebrado"

Enquanto o seguinte é opinião, cheguei a essa opinião por experiência própria.

Comprometa cedo e comprometa-se frequentemente. Confirme o código quebrado que desejar. Veja o seu histórico de consolidação local como "economize pontos" enquanto você se esquiva de algo. Depois de concluir um trabalho lógico, faça um commit. Claro que isso pode quebrar tudo, mas isso não importa, desde que você não force esses commits. Antes de empurrar, rebase e esmague seus commits.

  1. Criar nova ramificação
  2. Hack hack hack
  3. Confirmar código quebrado
  4. Polir o código e fazê-lo funcionar
  5. Confirmar código de trabalho
  6. Rebase e Squash
  7. Teste
  8. Empurre quando os testes estiverem passando

Para o OP, esse encadeamento de mensagens do kernal do Linux pode ser interessante, porque parece que alguns membros da equipe do OP estão usando o Git de maneira semelhante.

@RibaldEddie disse em um comentário abaixo:

Primeiro, um stash não está fora de um "fluxo de trabalho de ramificação", pois sob o capô um stash é apenas outro ramo.

(correndo o risco de provocar a ira de muitas pessoas)

Linus disse:

Com o "git stash", você também pode ter várias coisas ocultas, mas elas não se alinham - são apenas patches aleatórios e independentes que você escondeu porque foram inconvenientes em algum momento.

O que eu acho que o @RibaldEddie está tentando dizer é que você pode usar git stashem um fluxo de trabalho do ramo de recursos - e isso é verdade. Não é o uso git stashdisso que é o problema. É a combinação de comprometer-se a dominar e usar git stash. Este é um anti-padrão.

Esclarecendo git rebase

Do comentário de @ RibaldEddie:

Rebasing é muito mais parecido com copiar e colar e ainda pior modifica o histórico confirmado.

(Ênfase minha)

Modificar o histórico de consolidação não é uma coisa ruim, desde que seja histórico de consolidação local . Se você refazer as confirmações que já enviou, essencialmente tornará órfão qualquer outra pessoa usando sua ramificação. Isto é mau.

Agora, digamos que você fez várias confirmações ao longo de um dia. Alguns commits foram bons. Alguns ... não é tão bom. O git rebasecomando em conjunto com esmagar suas confirmações é uma boa maneira de limpar seu histórico de confirmação local. É bom mesclar um commit para ramificações públicas porque mantém limpo o histórico de commit das ramificações compartilhadas da sua equipe. Após o novo rebasamento, você desejará testar novamente, mas se os testes forem aprovados, você poderá enviar um commit limpo em vez de vários outros sujos.

Há outro encadeamento interessante do Kernel Linux no histórico de confirmação limpa .

Mais uma vez, de Linus:

Quero uma história limpa, mas isso realmente significa (a) história limpa (b).

As pessoas podem (e provavelmente deveriam) refazer suas árvores privadas (seu próprio trabalho). Isso é uma limpeza . Mas nunca o código de outras pessoas. Isso é uma "história de destruição"

Portanto, a parte da história é bastante fácil. Há apenas uma regra principal e um pequeno esclarecimento:

  • Você nunca deve destruir a história de outras pessoas. Você não deve refazer o commit de outras pessoas. Basicamente, se ele não tiver sua assinatura, estará fora dos limites: você não poderá refazê-lo, porque não é seu.

    Observe que isso realmente é sobre a história de outras pessoas , não sobre o código de outras pessoas . Se eles enviaram coisas para você como um patch enviado por e-mail e você aplicou com "git am -s", então é o código deles, mas é o seu histórico.

    Portanto, você pode se interessar pelo assunto "git rebase", mesmo que não tenha escrito o código, desde que o commit em si seja o seu particular.

  • Pequenos esclarecimentos à regra: depois que você publica seu histórico em algum site público, outras pessoas podem usá-lo e, agora, claramente não é mais o seu histórico privado .

    Portanto, o menor esclarecimento é que não se trata apenas de "seu commit", mas também de ser privado da sua árvore, e você ainda não o divulgou.

...

Agora a parte "limpa" é um pouco mais sutil, embora as primeiras regras sejam bastante óbvias e fáceis:

  • Mantenha seu próprio histórico legível

    Algumas pessoas fazem isso apenas pensando primeiro nas coisas e não cometendo erros. mas isso é muito raro e, para o resto de nós, usamos "git rebase" etc. enquanto trabalhamos em nossos problemas.

    Portanto, "git rebase" não está errado. Mas é certo apenas se for a sua própria árvore git PRIVADA.

  • Não exponha sua porcaria.

    Isso significa: se você ainda está na fase "git rebase", não o expande. Se não estiver pronto, você envia correções ou usa árvores git privadas (como uma "substituição de série de correções") sobre as quais você não fala ao público em geral.

(ênfase minha)

Conclusão

No final, o OP tem alguns desenvolvedores fazendo isso:

git checkout master
(edit files)
git commit -am "..."
(edit files)
git stash
git pull
git stash (pop|apply)

Existem dois problemas aqui:

  1. Os desenvolvedores estão se comprometendo a dominar. Bloqueie isso imediatamente. Realmente, este é o maior problema.
  2. Os desenvolvedores estão constantemente usando git stashe git pullno mestre quando deveriam usar ramificações de recursos.

Não há nada de errado em usar git stash- especialmente antes de um pull -, mas usar git stashdessa maneira é um antipadrão quando há melhores fluxos de trabalho no Git.

O uso de git stashum arenque vermelho. Não é esse o problema. Comprometer-se a dominar é o problema.

Greg Burghardt
fonte
4
Uau, isso não está certo. Primeiro, um stash não está fora de um "fluxo de trabalho de ramificação", pois sob o capô um stash é apenas outro ramo. A ideia de que é um fluxo de trabalho copiar e colar não faz sentido. Rebasing é muito mais parecido com copiar e colar e ainda pior modifica o histórico confirmado. Por fim, seu fluxo de trabalho é completamente inútil assim que você precisa compartilhar o trabalho em andamento com os colegas, pois se desfaz assim que você pressiona as alterações. Como isso tem seis votos positivos é incompreensível.
precisa saber é o seguinte
8
@RibaldEddie: Não há nada errado em refazer e reescrever o histórico de confirmação, desde que seja local. Depois de enviar esses commits, seu comentário estará correto, mas reler minha resposta. Ele diz: "Antes de empurrar, refaça e esmague seus commits". Esse é o histórico de consolidação local , que está inteiramente sob seu controle.
Greg Burghardt
1
@RibaldEddie: Esclareci minha resposta. Precisava de alguma limpeza.
Greg Burghardt
Vou fornecer mais contexto em uma resposta própria.
precisa saber é o seguinte
Veja minha resposta abaixo - embora comprometer-se a dominar seja um antipadrão, não é o problema real.
precisa saber é o seguinte
7

Pessoalmente, uso apenas stashinterrupções curtas e inesperadas, como alguém que faz uma pergunta que exige a mudança para um ramo diferente. Faço isso porque esqueci os esconderijos antes, então eles não se aplicavam corretamente. As confirmações regulares nas ramificações dos recursos são muito mais difíceis de esquecer e mais fáceis de mesclar, então agora eu costumo fazer apenas uma confirmação quebrada, depois git reset HEAD~1refizo ou refizo se não quiser mantê-la mais tarde.

No entanto, o melhor do controle de versão distribuído é que as pessoas podem usar seu fluxo de trabalho preferido em seus próprios repositórios, desde que os repositórios compartilhados atendam aos padrões. Eu garantiria que as pessoas não usassem apenas um stashfluxo de trabalho porque não têm treinamento ou conhecimento suficiente de alternativas, mas se ainda escolherem um fluxo de trabalho que você considera subótimo, eu deixaria como está.

Karl Bielefeldt
fonte
1

Acho que a parte da sua pergunta que é um antipadrão é o uso de um único ramo mestre compartilhado . No entanto, se você incluir uma ramificação de desenvolvimento além da ramificação principal e, em seguida, usar stashes para lidar com suas próprias alternâncias de contexto na ramificação de desenvolvimento, isso não seria um antipadrão e reflete muito de perto alguns fluxos de trabalho. descrever por organizações como Etsy e Facebook.

Dito isto, a resposta de @Greg Burghardt acima é um pouco favorável ao chamado fluxo de trabalho git-flow ou feature-branch. Eu defendia uma estratégia semelhante, mas depois de perceber que ela adiciona complexidade desnecessária e cria uma falsa sensação de segurança, não o faço mais. É também uma ressaca dos dias de sistemas de controle de versão não descentralizados, como o subversion.

Primeiramente, como o Git é um sistema descentralizado de controle de versão, diferentemente do subversion, o repositório local de um desenvolvedor é essencialmente um ramo gigante do código em si. O que um indivíduo desenvolve localmente não tem e não deve ter impacto nos outros membros da equipe, a menos que o código quebrado ou com bugs seja enviado para qualquer ramificação compartilhada em um repositório compartilhado.

O comando rebase, no entanto, pode danificar o histórico da ramificação quando houver um conflito de mesclagem em uma das confirmações reproduzidas. De http://ryantablada.com/post/the-dangers-of-rebasing-a-branch

O restante da rebase ocorre sem problemas, todos os testes parecem passar. Um PR é feito.

E, em seguida, é escrito mais um código que depende da propriedade commentsForAllPosts e tudo está quebrado. Mas a quem vamos pedir ajuda? A culpa do git mostra que a linha de código foi escrita apenas pelo engenheiro do lado do servidor e ele levanta as mãos.

Agora, seu engenheiro front-end está de férias, licença médica ou quem sabe. Ninguém pode descobrir como deve ser esse código!

O rebase eliminou a capacidade da equipe de examinar o histórico para descobrir o que deu errado, porque todos os conflitos de mesclagem no ramo filho são mortos e o código original é perdido para sempre.

Se esse mesmo conflito de mesclagem surgisse e a mesclagem fosse usada, a culpa mostraria que essa linha de código foi tocada no processo de mesclagem, na confirmação na ramificação pai e na submissão na ramificação filha. Alguns brincando com as três permutações e você pode recuperar a intenção original de volta à base de código e trabalhar sem uma tonelada de cabeça coçando o dedo apontando. E tudo que você realmente tinha era outro commit

Além disso, um modelo de ramificação múltipla pressupõe que nenhuma ramificação jamais poderia conter alterações de código interdependentes. Quando isso acontece inevitavelmente, o desenvolvedor agora precisa conciliar ainda mais filiais para trabalhar com eficiência.

O anti-padrão fundamental que vejo não está relacionado a ramos versus esconderijos, mas sim mais aos tipos de problemas que algumas pessoas muito inteligentes têm falado há um tempo: você confia no seu código através do uso de testes de unidade e uma boa arquitetura? Você é capaz de fazer alterações incrementais no seu código, de modo que seus desenvolvedores possam raciocinar sobre alterações facilmente e entender o que uma alteração fará? Seus desenvolvedores executam o código uma vez para ver se ele realmente funciona? (Sim, eu já vi isso antes).

Se a resposta a essas perguntas for negativa, não importa quantas ramificações você tem - os desenvolvedores dirão que o código está pronto, funcionando e adequado para produção quando realmente não é, e não há número de ramificações. ajudá-lo quando esse código chegar à produção de qualquer maneira.

RibaldEddie
fonte
2
Seu comentário sobre rebasear com problemas na resolução de mesclagem não é verdadeiro. Uma rebase é o mesmo tipo de mesclagem que uma mesclagem real. O Git está apenas mesclando commit sob o capô. Rebasear e resolver um conflito de mesclagem não danifica o histórico de consolidação. Rebasear e resolver indevidamente um conflito de mesclagem danifica as coisas, o que é igualmente provável com uma mesclagem normal. Eu tenho que concordar que as ramificações de recursos aumentam a complexidade, mas isso pode ser uma complexidade necessária se os desenvolvedores precisarem manipular várias alterações não relacionadas.
Greg Burghardt
Então você está dizendo que as fusões podem destruir a história ?! Parece que é apenas mais uma justificativa para evitar ter muitos galhos!
precisa saber é o seguinte
1
Mesclagens não podem destruir a história. Eu acho que você pode estar entendendo mal como funciona o rebasing e a fusão. Quando um conflito de mesclagem é acionado, cabe ao ser humano resolvê-lo. Se o humano o resolver incorretamente, você não poderá culpar o Git (ou SVN, CVS ou {inserir controle de origem aqui}).
Greg Burghardt
Agora, o que você está dizendo entra em conflito com o que você disse antes. Você leu o artigo que citei? Você entende o contexto em que uma rebase perderá o histórico?
precisa saber é o seguinte
1
Eu li o artigo. "Na Keen, tentamos empurrar nosso código após quase cada confirmação". Isso parece loucura para mim. Ou eles estão fazendo confirmações excessivamente grandes ou estão enviando código que ainda não está pronto. Se você tornar todos os seus commits públicos imediatamente, sim, rebasear causará problemas. É o princípio "Doutor, doutor, dói quando faço isso".
Kyralessa 12/09
1

git stashé uma ferramenta. Por si só, não é um padrão, nem um antipadrão. É uma ferramenta, assim como um martelo é uma ferramenta. Usar um martelo para acionar pregos é um padrão e usar um martelo para acionar parafusos é um antipadrão. Da mesma forma, existem fluxos de trabalho e ambientes onde git stashé a ferramenta correta a ser usada, e fluxos de trabalho e ambientes onde estão incorretos.

O fluxo de trabalho 'todos comprometem e enviam para a linha principal' é aquele que funciona razoavelmente, onde não há alterações de alto risco. É frequentemente visto usado em ambientes svn, onde há um servidor central autorizado que possui o código.

O Git, no entanto, vincula-se a acabar com o único servidor central. Ter todos os desenvolvedores a fazer commit, pull(ou rebasese você estiver em que), pushtodo o tempo podem fazer uma grande bagunça.

Os maiores problemas surgem: você tem algo em andamento que está quebrado e precisa trabalhar em um bug prioritário. Isso significa que você precisa deixar esse trabalho de lado por um tempo, pegar o mais recente, trabalhar nele sem que o trabalho anterior em andamento cause problemas com a compilação que você está tentando fazer.

Para isso, git stashseria a ferramenta adequada para usar.

Há, no entanto, um problema maior oculto no coração desse fluxo de trabalho. É que todas as funções das ramificações de controle de versão estão em uma única ramificação. Linha principal, desenvolvimento, manutenção, acumulação e embalagem estão todos no Master. Isso é um problema. (Consulte Estratégias avançadas de ramificação do SCM para obter mais informações sobre essa abordagem de filiais)

E sim, você disse que não é um ótimo fluxo de trabalho e que há problemas com ele. No entanto, o problema não está na ferramenta git stash. O problema é a falta de ramificação distinta para funções ou políticas incompatíveis .

git stashno entanto, é algo que eu usei quando tive uma situação em que construí um pouco, entrei em um estado difícil que não tinha certeza se era o certo ... então escondi minhas alterações e depois explorou outra abordagem para resolver o problema. Se isso funcionou - ótimo, descarte o material no estoque e continue. Se a outra exploração ficar mais complicada, redefina a alteração anterior e aplique novamente o estoque para trabalhar nisso. A alternativa seria confirmar, finalizar a compra, ramificar e continuar nessa nova ramificação ou voltar e redefini-la. A questão é: vale a pena colocar isso na história quando é apenas algo que eu quero explorar um pouco?

git stashnão é um anti-padrão. Usar git stashcomo alternativa à ramificação enquanto todos se comprometem com o Mestre é um anti-padrão - mas não por causa disso git stash.

Se você ainda não o encontrou, aguarde até ter problemas com as compilações , quando alguém precisar fazer alterações arquiteturais significativas em muitos arquivos (e os conflitos de mesclagem) ou algum código de trabalho em andamento não testado que vaza para produção para este anti-padrão para alcançá-lo.

Comunidade
fonte