Quais modelos de ramificação do Git funcionam para você?

379

Atualmente, nossa empresa está usando um modelo de ramificação simples de tronco / release / hotfixes e gostaria de receber conselhos sobre quais modelos de ramificação funcionam melhor para sua empresa ou processo de desenvolvimento.

  1. Fluxos de trabalho / modelos de ramificação

    Abaixo estão as três principais descrições que eu já vi, mas elas se contradizem parcialmente ou não vão longe o suficiente para resolver os problemas subseqüentes nos quais encontramos (conforme descrito abaixo). Portanto, nossa equipe, até o momento, adota soluções não tão boas. Você está fazendo algo melhor?

  2. Mesclando vs rebasing (emaranhado x histórico seqüencial)

    Deve-se pull --rebaseesperar com a mesclagem de volta à linha principal até que sua tarefa seja concluída? Pessoalmente, eu me inclino para a mesclagem, pois isso preserva uma ilustração visual de em que base uma tarefa foi iniciada e finalizada, e eu até prefiro merge --no-ffpara esse fim. No entanto, há outras desvantagens. Muitos também não perceberam a propriedade útil da mesclagem - que não é comutativa (mesclar um ramo de tópico no mestre não significa mesclar o mestre no ramo de tópico).

  3. Estou procurando um fluxo de trabalho natural

    Às vezes, erros acontecem porque nossos procedimentos não capturam uma situação específica com regras simples. Por exemplo, uma correção necessária para liberações anteriores deve, obviamente, ser baseada suficientemente a jusante para que seja possível mesclar a montante em todas as ramificações necessárias (o uso desses termos é claro o suficiente?). No entanto, acontece que uma correção entra no mestre antes que o desenvolvedor perceba que deveria ter sido colocado mais a jusante, e se isso já foi enviado (ainda pior, mesclado ou algo com base nele), a opção restante é escolher a cereja, com seus perigos associados. Que regras simples como essas você usa?Também está incluído o constrangimento de uma ramificação de tópico, excluindo necessariamente outras ramificações de tópico (supondo que elas sejam ramificadas a partir de uma linha de base comum). Os desenvolvedores não querem concluir um recurso para iniciar outro, sentindo que o código que acabaram de escrever não existe mais

  4. Como evitar a criação de conflitos de mesclagem (devido à seleção de cereja)?

    O que parece uma maneira certa de criar um conflito de mesclagem é escolher entre ramificações, elas nunca poderão ser mescladas novamente? A aplicação do mesmo commit na reversão (como fazer isso?) Em qualquer ramo possivelmente resolveria essa situação? Esse é um dos motivos pelos quais não me atrevo a pressionar por um fluxo de trabalho amplamente baseado em mesclagem.

  5. Como se decompor em ramos tópicos?

    Percebemos que seria incrível montar uma integração finalizada das ramificações de tópicos, mas muitas vezes o trabalho de nossos desenvolvedores não está claramente definido (às vezes, tão simples quanto "bisbilhotar") e se algum código já tiver entrado em um tópico "misc", não pode ser retirado de lá novamente, de acordo com a pergunta acima? Como você trabalha para definir / aprovar / formar / liberar suas ramificações de tópicos?

  6. É claro que procedimentos adequados, como revisão de código e graduação, seriam adoráveis.

    Mas simplesmente não podemos manter as coisas emaranhadas o suficiente para gerenciar isso - alguma sugestão? ramos de integração, ilustrações?

Abaixo está uma lista de perguntas relacionadas:

Verifique também o que o Plastic SCM escreve sobre o desenvolvimento orientado a tarefas e, se o Plastic não for sua escolha, estude o modelo de ramificação do nvie e seus scripts de suporte .

HiQ CJ
fonte
2
Hah, obrigado, de fato tem ... Eu realmente li a maior parte disso ... coisas :-). É algo pelo qual sou conhecido - não me contentar com a solução medíocre, mas continue procurando o perfeito. Muitas vezes isso é um erro, mas, neste caso, muita coisa está em jogo e as soluções disponíveis são muito bagunçadas ou ruins que eu preciso continuar procurando. Então, decidi listar todos os problemas que tenho com ele.
HiQ CJ
O blog Plastic SCM lançou sua opinião na discussão, é pelo menos perspicaz: codicesoftware.blogspot.com/2010/08/…
HiQ CJ
11
Você tem que ter cuidado ao usar "merge --no-ff", verificar isso para algumas ressalvas sandofsky.com/blog/git-workflow.html
Doppelganger
11
@ Doppelganger Eu estaria interessado em saber como especificamente o --no-ff está contribuindo para o problema descrito no link que você postou. Para mim, o problema descrito é o fracasso do bisect com confirmações do ponto de verificação e o fracasso da culpa do git em ajudar nesse caso - mas não vejo como "--no-ff" muda alguma coisa, em vez de não usá-la. O autor reclama que uma mesclagem com --no-ff não modifica um arquivo - mas sem ele, o arquivo também não seria modificado, você também veria o commit mais antigo em seu histórico, certo?
codeling
Outro modelo de ramificação: modelo de cacto barro.github.io/2016/02/… , modelo de linha principal bitsnbites.eu/a-stable-mainline-branching-model-for-git . Esse modelo de ramificação oferece outra abordagem além do gitflow.
Mathieu Momal 15/09/17

Respostas:

91

O recurso mais preocupante que os novos desenvolvedores do DVCS precisam entender é o processo de publicação :

  • você pode importar (buscar / puxar) qualquer repositório remoto necessário
  • você pode publicar (enviar) para qualquer repo (nu) que desejar

A partir disso, você pode respeitar algumas regras para facilitar suas perguntas:

Agora:

Modelos de fluxos de trabalho / ramificação :

cada fluxo de trabalho existe para oferecer suporte a um processo de gerenciamento de liberação e é adaptado para cada projeto.
O que posso acrescentar ao fluxo de trabalho que você menciona é: cada desenvolvedor não deve criar um ramo de recurso, apenas um ramo "dev atual", porque a verdade é: o desenvolvedor geralmente não sabe exatamente o que seu ramo produzirá: um recurso, vários (porque acabou sendo um recurso muito complexo), nenhum (porque não está pronto a tempo do lançamento), outro recurso (porque o original havia "se transformado"), ...

Somente um "integrador" deve estabelecer ramificações oficiais de recursos em um repositório "central", que pode ser buscado pelos desenvolvedores para refazer / mesclar a parte do trabalho que se encaixa nesse recurso.

Mesclagem vs rebasing (emaranhado x histórico seqüencial) :

Gosto da minha resposta que você mencionou (" Descrição do fluxo de trabalho para uso do git para desenvolvimento interno ")

Estou procurando um fluxo de trabalho natural :

para correções, ele pode ajudar a associar cada correção a um ticket de um rastreamento de bug, o que ajuda o desenvolvedor a lembrar onde (por exemplo, em qual filial, ou seja, uma filial dedicada "para correções") ele / ela deve cometer essas modificações.
Então, hooks podem ajudar a proteger um repositório central contra ataques de correções de bugs não validadas ou de ramos dos quais não se deve pressionar. (nenhuma solução específica aqui, tudo isso precisa ser adaptado ao seu ambiente)

Como evitar a criação de conflitos de mesclagem (devido à seleção de cereja)?

Como afirmou Jakub Narębski em sua resposta , a colheita de cerejas deve ser reservada para situações raras em que é necessária.
Se a sua configuração envolve muitas escolhas (ou seja, "não é raro"), algo está errado.

A aplicação do mesmo commit seria revertida (como fazer isso?)

git revert deve cuidar disso, mas isso não é o ideal.

Como se decompor em ramos tópicos?

Desde que um ramo ainda não tenha sido enviado a todos os lugares, um desenvolvedor deve reorganizar seu histórico de confirmações (quando finalmente vê o desenvolvimento assumir uma forma mais definitiva e estável) em:

  • vários ramos, se necessário (um por recurso claramente identificado)
  • um conjunto coerente de confirmações em uma ramificação (consulte Recortando check-ins do Git )

Procedimentos adequados, como revisão de código e graduação?

O repositório de ramificações de integração (em uma integração dedicada) pode ajudar o desenvolvedor a:

  • rebase seu desenvolvimento sobre esse ramo de integração remota (pull --rebase)
  • resolver localmente
  • empurrar o desenvolvimento para esse repositório
  • verifique com o integrador que não resulta em confusão;)
VonC
fonte
@UncleCJ: como você pode ver, isso não é exatamente uma resposta final à sua "pergunta final";)
VonC
Eu entendo, e eu tenho um bom senso de ironia bem, é ok ;-)
HiQ CJ
3
O @UncleCJ upstream é exatamente de onde você sai regularmente, do meu post, onde quer que todos os commits acabem (a versão de lançamento ou tronco na linguagem SVN). A jusante está todo mundo abaixo deles. Enviar material upstream é o processo de mesclá-lo no repositório de lançamento (como linux-2.6) e downstream são as mudanças de lá para fora, ou do seu repositório, como o gerente de desenvolvimento de um recurso para seus minions ... equipe média.
2
@UncleCJ: "Eu ainda acho complicado aparar seus checkins para obter um histórico estritamente sequencial": é mais fácil com o Git1.7 e seu rebase --interactive --autosquashque se moverá automaticamente todos os commit com o mesmo início de outra mensagem de commit. Se essas confirmações usarem um número de ticket (por exemplo), mesmo que as correções relacionadas a essa permissão não tenham sido feitas seqüencialmente no momento, o autosquash permitirá uma rápida reordenação dessas confirmações.
VonC 12/04
11
@UncleCJ: "histórico estritamente sequencial (isso é necessário ou não ?!)": nem sempre é necessário, mas ajuda a manter o rastreamento de dependências funcionais ( stackoverflow.com/questions/881092/… ) e conflitos semânticos ( stackoverflow.com/questions / 2514502 /… )
VonC
21

Eu acho que posso estar errado, e que uma das coisas mais incompreendidas sobre o git é sua natureza distribuída. Isso torna muito diferente dizer subversão da maneira que você pode trabalhar, embora possa imitar o comportamento do SVN, se desejar. O problema é praticamente qualquer fluxo de trabalho, o que é ótimo, mas também enganoso.

Se eu entendo direito o desenvolvimento do kernel (vou me concentrar nisso), todo mundo tem seu próprio repositório git para desenvolver o kernel. Existe um repositório, linux-2.6.git, que o Torvalds cuida, que atua como repositório de lançamento. As pessoas clonam daqui se desejam começar a desenvolver um recurso no ramo "release".

Outros repositórios fazem algum desenvolvimento. A idéia é clonar do linux-2.6, ramificar quantas vezes você quiser até um ponto em que você tenha um recurso "novo" em funcionamento. Então, quando estiver pronto, você poderá disponibilizá-lo para alguém considerado confiável, que puxará esse ramo do seu repositório para o deles e o mesclará no mainstream. No kernel do linux, isso acontece em vários níveis (tenentes de confiança) até atingir o linux-2.6.git, momento em que se torna "o kernel".

Agora aqui é onde fica confuso. Os nomes das filiais não precisam ser consistentes entre os repositórios. Para que eu possa git pull origin master:vanilla-codeobter uma ramificação do originmestre da ramificação no meu repositório chamada vanilla-code. Desde que eu saiba o que está acontecendo, isso realmente não importa - ele é distribuído no sentido de que todos os repositórios são pares e não apenas compartilhados em vários computadores como o SVN.

Então, com tudo isso em mente:

  1. Eu acho que cabe a cada programador como eles fazem suas ramificações. Tudo o que você precisa é de um repositório central para gerenciar lançamentos, etc. O tronco pode ser head. As versões podem ser tags ou ramificações e os hotfixes provavelmente são ramificações em si. Na verdade, eu provavelmente faria lançamentos como ramificações para que você possa continuar corrigindo-os.
  2. Eu iria mesclar e não me recuperar. Se por exemplo você tomar um repositório, cloná-lo, ramo e fazer alguma dev, em seguida, puxe a partir do originque você deve, em seu repositório, provavelmente, fazer um outro ramo e mesclar o mais recente masterem yourbranchpara que outra pessoa pode puxar suas alterações com o mínimo de esforço possível. Raramente há uma necessidade de me refazer verdadeiramente, na minha experiência.
  3. Eu acho que é um caso de entender como o Git funciona e o que ele pode fazer. Demora um pouco e muita comunicação - só comecei a entender o que realmente estava acontecendo quando comecei a usar o git com outros desenvolvedores e, mesmo agora, algumas coisas que não tenho certeza.
  4. Conflitos de mesclagem são úteis. Eu sei, você sabe que você quer que tudo funcione, mas o fato são as alterações de código e você precisa mesclar os resultados em algo que funcione. Os conflitos de mesclagem são de fato apenas mais programação. Eu nunca encontrei uma explicação fácil para o que fazer com eles, então aqui está: observe os arquivos que possuem conflitos de mesclagem, altere-os para o que deveriam ser git add .e , em seguida git commit.
  5. Seja como for. Como eu disse, cada repositório git de cada usuário é seu e os nomes das filiais não precisam ser os mesmos . Se você tivesse um repositório temporário, por exemplo, poderia aplicar um esquema de nomenclatura, mas não precisa para cada desenvolvedor, apenas no repositório de release.
  6. Este é o estágio de mesclagem. Você se une apenas às ramificações de lançamento etc. quando considera o código a ser revisado / passa no teste de qualidade.

Espero que ajude. Percebo que o VonC acabou de postar uma explicação muito parecida ... Não consigo digitar rápido o suficiente!

Edite algumas idéias adicionais sobre como usar o git em um ambiente comercial, pois isso parece relevante para o OP nos comentários:

  • O repositório de versões, como chamaremos product.git, é acessível por vários programadores seniores / pessoas técnicas responsáveis ​​por realmente cuidar do produto. Eles são análogos ao papel dos mantenedores no OSS.
  • Esses programadores provavelmente também lideram em parte o desenvolvimento de novas versões, para que eles também possam se codificar e manter repositórios de varios. Eles podem gerenciar repositórios temporários para recursos realmente novos e também podem ter seus próprios repositórios.
  • Abaixo deles estão os programadores responsáveis ​​pelo desenvolvimento de bits individuais. Por exemplo, alguém pode ser responsável pelo trabalho da interface do usuário. Eles, portanto, gerenciam o repositório UI.git.
  • Abaixo deles estão os programadores que desenvolvem os recursos como seu trabalho diário.

Então o que acontece? Bem, todo mundo puxa no início de cada dia a partir da fonte "upstream", isto é, o repositório de lançamento (que provavelmente também conterá o material mais recente do desenvolvimento dos dias anteriores). Todo mundo faz isso diretamente. Isso irá para um ramo em seu repositório, provavelmente chamado de "mestre" ou talvez se você me chamou de "mais recente". O programador fará algum trabalho. Esse trabalho pode ser algo sobre o qual eles não têm certeza; portanto, eles fazem uma filial, fazem o trabalho. Se não funcionar, eles podem excluir a ramificação e voltar. Se isso acontecer, eles terão que se unir ao ramo principal no qual estão trabalhando no momento. Diremos que este é um programador de interface do usuário trabalhando latest-ui, git checkout latest-uiseguido porgit merge abc-ui-mywhizzynewfeature. Ele então diz ao seu líder técnico (o líder da interface do usuário) ei, eu concluí essa tarefa, retire-me. O líder da interface do usuário sim git pull user-repo lastest-ui:lastest-ui-suchafeature-abc. O lead da interface do usuário, então, olha para esse ramo e diz: na verdade, é muito bom, eu o mesclarei ui-latest. Ele pode então dizer a todos que estão abaixo dele para puxá-lo em seus ui-latestgalhos ou em qualquer nome que eles deram a eles, para que o recurso seja explorado pelos desenvolvedores. Se a equipe estiver satisfeita, o líder da interface do usuário poderá solicitar que o líder de teste o retire e mescle as alterações. Isso se propaga a todos (a jusante da mudança) que o testam e enviam relatórios de bugs, etc. Finalmente, se o recurso passa no teste, etc., um dos principais líderes técnicos pode fundi-lo na cópia de trabalho atual do programa, momento em que todas as alterações são propagadas novamente. E assim por diante.

Não é uma maneira "tradicional" de trabalhar e foi projetada para ser "orientada por pares" e não "hierárquica" como SVN / CVS. Em essência, todos têm acesso confirmado, mas apenas localmente. É o acesso ao repositório e qual repositório você designa como o repositório de liberação que permite usar a hierarquia.


fonte
Muito obrigado pela sua extensa resposta (e votos), vou ler mais um pouco para extrair informações úteis. No entanto, somos uma empresa, não um comitê de desenvolvimento OSS ;-), e eu tenho que ajudar meus desenvolvedores com diretrizes mais claras do que "mexer como quiser no seu próprio repositório". Vamos ver aonde esse post leva, sinto um bom momento, continue!
HiQ CJ
@VonC Thanks. @UncleCJ true, mas você tem, tenho certeza, tem gerenciadores de lançamento etc. Qualquer pessoa com acesso ao repositório pode fazer isso. Quanto ao desenvolvimento, por que não dar aos desenvolvedores a liberdade, dentro da razão, de se ramificar? Desde que você tenha algum protocolo para concordar com mesclagens e o (s) seu (s) repositório (s) central (ais) sejam nomeados como desejar, não há problema. Dito isto, um esquema de nomenclatura comum não é uma má idéia. Costumo usar sub-ramificações de versão-versão-recurso para ramificações pessoais e versão para ramificações.
@UncleCJ Adicionei um exemplo de como isso pode funcionar em uma empresa. São essencialmente as funções do OSS substituídas pelos gerentes, mas você entendeu. Ele tem o benefício adicional sobre o SVN de que seus desenvolvedores também podem trabalhar offline (eles só precisam da rede para puxar / empurrar) e acho que facilita o teste de recursos, se você o implementar bem.
Uau, esse é realmente um ótimo exemplo, podemos começar a usar algo assim para a graduação. Eu não quis dizer tanto que, como não estamos fazendo OSS, todos precisam ser regulados, na verdade somos uma equipe bastante pequena e plana, mas temos que tentar colaborar com eficiência em um cronograma apertado e também aprender em equipe. . É por isso que estou aqui fazendo essas perguntas estúpidas para poder ajudar o resto da equipe mais tarde :-). Também percebi no #git que a linha de base mal definida, combinada com a pressão para diminuir os prazos de entrega, está nos fazendo tropeçar ... voltaremos mais tarde.
HiQ CJ
Isso é justo o suficiente - eu estive lá recentemente, e foi exatamente como eu peguei esse exemplo, tentando e falhando muito ... e também adaptando-me às formas de trabalho de alguns projetos do OSS. Eu acho que o verdadeiro problema é que não importa como você ramifica e onde estão seus repositórios ... você pode defini-los da maneira que quiser, o que foi realmente um choque para mim! Mas permite que você faça algumas coisas interessantes. Enfim, boa sorte e divirta-se!
9

Um modelo que eu usei com bons resultados é o seguinte:

Um repositório "abençoado" que todos enviam e recebem de / para, basicamente uma topologia cliente-servidor.

Como não há ramificação principal, nenhum desenvolvedor pode inserir nenhum código na "linha principal".

Todos os desenvolvimentos acontecem em tópicos. Nós colocamos nomes no namespace para detectar facilmente quem é responsável por ele: jn / newFeature ou jn / issue-1234

Há também um mapeamento quase 1 para 1 entre filiais e cartões kanban / scrum no quadro branco.

Para liberar um ramo, ele é enviado para o repo abençoado e o cartão Kanban é movido para pronto para revisão.

Então, se a ramificação for aceita pela revisão, ela é candidata a uma liberação.

Uma liberação acontece quando um conjunto de ramificações aceitas é mesclado e marcado com um número de versão.

Ao levar a nova tag para o repo abençoado, há uma nova base possível para novos recursos.

Para evitar conflitos de mesclagem, os desenvolvedores devem atualizar (mesclar) suas ramificações não lançadas na tag de lançamento mais recente.

John Nilsson
fonte
2

Pessoalmente, tento manter apenas o código pronto para lançamento na ramificação principal.

Quando trabalho em um novo recurso ou correção de bug, faço isso em uma ramificação. Eu também teste unitário no ramo. Se tudo der certo, só então eu mesclar / refazer o master.

Também tento usar convenções comuns de nomenclatura de ramificações, como:

  • bugfix / recursive_loop
  • bugfix / sql_timeout
  • feature / new_layout
  • feature / Enhanced_search
xero
fonte