Modelo de branches no GitLab
O git mudou a forma com a qual os desenvolvedores pensam a respeito de merges e branches. No mundo de código centralizado, merges e branches sempre foram considerados assustadores (cuidados com os conflitos do merge) e algo que era feito apenas uma vez em um longo tempo. Porém com o git essas ações são extremamente baratas e simples, e são consideradas o cerne do dia-a-dia de trabalho, de verdade. Como uma consequência de sua simplicidade e natureza repetitiva, criar branches e merges não é algo que se deva ter medo. As ferramentas de versionamento dispõem de toda a assistência necessária para realizar as tarefas de criação de branches e merges. Mais informações sobre o git e ferramentas de controle de código podem ser facilmente encontradas na web.
Após essa breve introdução sobre a ferramenta, vamos entrar de cabeça no modelo de desenvolvimento. O modelo aqui apresentado é essencialmente nada mais que um conjunto de procedimentos que cada membro da equipe tem que seguir a fim de chegar a um processo de desenvolvimento de software gerenciado.
Índice
Descentralizado, mas centralizado
O modelo de repositório utilizado para o gerenciamento de branches considera que temos apenas um único, e verdadeiro, repositório central. Note que este repositório é apenas considerado central (pois como o git é uma ferramenta de controle descentralizado, não existe um repositório central a nível técnico). Vamos nos referir a este repositório principal como origin, pois é um nome familiar a todos os usuários do git.
Cada desenvolvedor efetua pull e pushes no origin. Mas, além dessa relação centralizada comum, cada desenvolvedor deve também efetuar um pull das modificações realizadas por outros desenvolvedores de outras equipes. Por exemplo, isto pode ser útil quando um ou mais desenvolvedores estão trabalhando em uma nova grande funcionalidade, antes de enviar as alterações em progresso diretamente para o repositório origin prematuramente. Na figura ao lado, existem equipes de Alice e Bob, Alice e David, e Clair e David. Tecnicamente isto significa nada mais que Alice definiu um git remote (chamado bob) apontando para o repositório da equipe de Bob, e vice-versa.
O branch principal
No núcleo, o padrão de desenvolvimento é muito inspirado por modelos existentes no mercado. O repositório central detém dois branches principais, com uma vida útil infinita:
- master
- develop
O brach master no origin deve ser familiar para todo usuário git. Paralelamente ao branch master outro branch existe, chamado develop. Consideraremos o origin/master como main branch, onde o seu conteúdo sempre reflete o status de pronto para produção. Consideraremos o origin/develop como o principal branch onde o código fonte sempre reflete um estado com as mudanças de desenvolvimento mais recente entregues para a próxima sprint. Podemos chama-lo também de integration branch. É neste branch onde qualquer rotina noturna automática de build é executada. Quando o código-fonte no develop branch chega a um ponto estável e está pronto para ser lançado todas as alterações devem sofrer o merge com o branch master de alguma forma e, em seguida, ser marcado com um número de versão. Como isto é feito em detalhes será discutido mais adiante.
Portanto, cada vez que as alterações são mescladas de volta para o master', esta é uma nova versão de produção por definição. Devemos ser muito rigorosos com isso, de modo que, teoricamente, nós poderíamos usar um script automático para realizar o deploy e roll-out de nosso software para os nossos servidores de produção automaticamente toda vez que houve uma consolidação no master.
Os branches de suporte
Ao lado do master e develop branches, o nosso modelo de desenvolvimento utiliza uma variedade de branches de apoio para auxiliar o desenvolvimento paralelo entre os membros da equipe, facilitando o rastreamento de recursos, o preparo para versões de produção e para auxiliar na solução rápida de problemas na produção. Ao contrário dos branches principais, estes sempre tem um tempo de vida limitado, uma vez que eles serão removidos eventualmente. Os diferentes tipos de branches que podemos utilizar são:
- Feature branches
- Release branches
Cada um desses ramos têm um propósito específico e estão vinculados a regras restritas sobre quais branches podem ser seu branch de origem e quais branches devem ser considerados seu destino para receber o seu conteúdo por merge.
De nenhuma maneira estes branches são considerados especiais do ponto de vista técnico. Os seus tipos são classificados pela forma como os mesmos são utilizados.
Feature branches
- Pode ser um branch a partir de:
- developer
- Deve ser feito 'merge de volta para:
- developer
- Convenção de nomenclatura para o branch:
- qualquer coisa, exceto master, developer, release-*, ou hotfix-*
Feature branches (às vezes chamados de topic branches) são usados para desenvolver novas funcionalidades para a próxima spirnt ou um lançamento futuro. Ao iniciar o desenvolvimento de uma feature, a versão alvo na qual esse recurso será incorporado pode muito bem ser desconhecida até esse ponto. A essência de um feature branch é que ele existe enquanto a feature está em desenvolvimento, a qual eventualmente acabará por ser feito o merge de volta para o develop (para adicionar definitivamente a nova feature na versão de release) ou descartados (no caso de uma experiência decepcionante).
Feaure branches normalmente existem apenas no developer, nunca no origin.
Criando um feature branch
Quando começar a trabalhar em uma nova feature, ramifica-se à partir do develop bracnh:
$ git checkout -b myfeature develop Switched to a new branch "myfeature"
Incorporando uma feature concluída no develop
Features finalizadas devem ter seu merge realizado no branch develop para definitivamente serem incorporadas na próxima versão de release.
$ git checkout develop Switched to branch develop $ git merge --no-ff myfeature Updating ea1b82a..05e9557 (Summary of changes) $ git branch -d myfeature Deleted branch myfeature (was 05e9557). $ git push origin develop
A flag --no-ff
faz com que o processo de merge sempre crie um novo commit object, mesmo que o merge pudesse ser feito no modelo fast-forward. Isso evita a perda de informações sobre a existência histórica de um feature branch' e agrupa todos os commits que, juntos, criam a nova feature. Compare:
No caso da direita, é impossível ver o histórico de commits objects que juntos implementaram a nova feature, você teria que manualmente ler todas as mensagens de commits. Neste caso, reverter a implementação de uma "feature" (grupo de commits) é uma verdadeira dor de cabeça, contudo, torna-se muito simples quando a flag --no-ff
é usada.
Sim, isto vai criar alguns commits objects vazios, mas o ganho é muito superior a este detalhe.