Modelo de gerência de branchs de sucesso para GIT

Neste post eu apresento o modelo de desenvolvimento que eu apresentei para alguns dos meus projetos (tanto no trabalho, como privados) cerca de um ano atrás, e que acabou sendo muito bem sucedido. Eu não vou falar sobre qualquer um dos detalhes dos projetos, mas apenas sobre a estratégia de ramificação e release.

Os Branches principais

O modelo proposto contem dois branhces principais com vida útil infinita: develop e master. O branch master (que na máquina de cada desenvolvedor deve ser similar) e, paralelo a ele, há a o develop.

Consideramos o origin/master como sendo o branch principal onde seu HEAD sempre vai refletir o estado do código do projeto em produção.

Consideramos que o origin/develop como sendo o branch principal, onde modificações realizadas na fase de desenvolvimento serão enviadas para o próximo release. Alguns chamam este de “branch de integração”. É desse branch, também, que automaticamente serão geradas as versões nightly.

Quando o código desse branch chegar ao nível estável, todas as alterações devem ser mergeadas de volta ao master e , sem seguida, taggeada com o número daquela versão. Como isso será feito será discutido depois.

Branches de apoio

Depois dos banches master e develop, nosso modelo de desenvolvimento usa uma variedade de outros branches de apoio pra facilitar o desenvolvimento paralelo entre os membros da equipe, facilitar a identificação de recursos e para correção de problemas em produção rapidamente. Ao contrário dos branches principais esses possuem um tempo de vida limitado, uma vez que serão removidos posteriormente.

Os brances de apoio podem ser: Feature branches, Release branches e Hotfix branches.

Cada um desses branches tem um propósito específico e possuem regras de quais serão seus branches origens e quais devem ser os branches destino para merge. Vamos falar de cada um deles:

Feature branches

Pode se ramificar a partir : develop
Pode ser mergeado para: develop
Convenção de nomes: Qualquer coisa exceto master, develop, release-* ou hotfix-*

Features branches são usados para desenvolver novas funcionalidades para um realease futuro. Esses branches existem enquanto a funcionalidade está sendo desenvolvida, mas acabará quando essa for mesclada de volta ao branch develop (para adicionar o recurso definitivamente ao release) ou descartada (em caso de fracasso).

Features branches existem, normalmente, no repositórios dos desenvolvedores e não no origin.

Criando um feature branch

git checkout -b myfeature develop
# Switched to a new branch "myfeature"

Incorporando uma feature no branch develop

Features concluídas podem ser mergeadas no branch develop para ser adicionado ao próximo 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 merge sempre crie um novo objeto commit, mesmo se esse merge pudesse ser realizado com um fast-forward. Isso evita perda de informações sobre a existência histórica de um ramo e agrupa todas os commits juntos. Veja:

Esse procedimento ira criar alguns commits (vazios) a mais, mas o ganho em relação ao custo é bem maior.

Release branches

Pode ramificar a partir de: develop
Pode ser mergeado para: develop e master
Conversão de nomenclatura: release-*

Os branches releases preparam o projeto para uma nova versão em produção. Eles permitem colocar os pingos nos i’s em seu código. Além disso eles permitem correções de bugs e o preparo de meta-dados, como número de versão. Ao fazer todo esse trabalho em um branch release, o branch develop é limpo para receber recursos para o próximo grande release.

O momento chave para se criar um release é quando o develop apresenta o estado desejado para o lançamento de uma nova versão (ou quase). Onde pelo menos todos os recursos.

É exatamente no inicio desse branch que o número da versão deve ser definido, não antes. Até aquele momento o branch develop reflete as mudanças que serão apresentadas na próxima versão, mas naquele momento, ainda não está claro se aquelas alterações se tornarão uma versão 0.3 ou 1.0. Regras de numeração que devem ser definidas pela equipe como regras de projeto.

Criando um branch release

Um release é criado a partir do branch develop. Por exemplo, digamos que a versão 1.1.5 está em produção e temos um grande release para lançar. O código no desenvolvimento já está pronto para a “próxima versão” e decidimos então que essa vai se tornar a versão 1.2 (em vez de 1.1.6 ou 2.0). Por isso que o nome do branch deve refletir o numero da versão que será lançada.

$ git checkout -b release-1.2 develop
# Switched to a new branch "release-1.2"
$ ./bump-version.sh 1.2
# Files modified successfully, version bumped to 1.2.
$ git commit -a -m "Bumped version number to 1.2"
# [release-1.2 74d9424] Bumped version number to 1.2
# 1 files changed, 1 insertions(+), 1 deletions(-)

Depois de criarmos um novo branch e mudar para ele, definimos então a versão do release, neste caso utilizamos o ./bump-version.sh para realizar algumas modificações na em nossa cópia de trabalho (obviamente isso pode ser feito manualmente). Em seguida commitamos nossas modificações com o número de versão.

Este novo branch pode existir por um tempo, até que o release seja implantado definitivamente. Durante esse tempo, algumas correções ou bugs podem ser aplicados a este ramo (ao invés do develop). Adições de grandes modificações são estritamente proibidas, elas devem aguardar o próximo grande release.

Finalizando um branch release

Quando o branch release está pronto para ir pra produção algumas ações precisam ser realizadas. Em primeiro lugar, esse branch é incorporado ao master (uma vez que cada commit no master é um novo release por definição, lembre-se disso). Então este commit deve ser taggeado para futuras referências históricas. E, finalmente, as alterações feitas no branch release precisa ser mescladas de volta para o develop, para que os futuros releases também contenham suas correções bugs.

$ git checkout master
# Switched to branch 'master'
$ git merge --no-ff release-1.2
# Merge made by recursive.
# (Summary of changes)
$ git tag -a 1.2

E para manter estas alterações realizadas no release em nosso código de trabalho precisamos mergea-las de volta ao develop.

$ git checkout develop
# Switched to branch 'develop'
$ git merge --no-ff release-1.2
# Merge made by recursive.
#(Summary of changes)

Hotfix branches

Pode ramificar a partir de: develop
Pode ser mergeado para: develop e master
Conversão de nomenclatura: release-*

Os branches de hotfix são bem parecidos com os branches releases, pois também preparam uma nova versão para ser enviada à produção, mas ela não é planejada. A ideia deles é suprir a necessidade de agir imediatamente após algum problema na versão que está em produção. Quando um erro crítico acontece ele deve ser resolvido imediatamente, para isso, então, deve ser criado um branch de hotfix a partir da tag no master que corresponde à versão que está em produção.

A grande sacada desse branch, é que os membros da equipe podem continuar o desenvolvimento do projeto no develop normalmente enquanto outro desenvolvedor realiza rapidamente uma correção para um problema crítico.

Criando um branch de hotfix

Os branchs de hotfix são criados a partir do master. Por exemplo, digamos que a versão 1.2 é a versão que está em produção e devido a um erro grave, está causando problemas. As alterações realizadas no branch develop ainda estão instáveis, não sendo elegíveis a um release para produção. Devemos então criar um branch hotfix para corrigir o problema.

$ git checkout -b hotfix-1.2.1 master
# Switched to a new branch "hotfix-1.2.1"
$ ./bump-version.sh 1.2.1
# Files modified successfully, version bumped to 1.2.1.
$ git commit -a -m "Bumped version number to 1.2.1"
# [hotfix-1.2.1 41e61bb] Bumped version number to 1.2.1
# 1 files changed, 1 insertions(+), 1 deletions(-)

Não esqueça de alterar o número da versão depois de criar o branch. Então envie as alterações em um ou mais commits.

$ git commit -m "Fixed severe production problem"
# [hotfix-1.2.1 abbe5d6] Fixed severe production problem
# 5 files changed, 32 insertions(+), 17 deletions(-)

Finalizando um branch de hotfix

Quando finalizado, um bugfix precisa ser mergeado de volta aos branches master e develop. Então o procedimento fica similar ao realizado no branch de release. Primeiro, atualize o master e a tag do release.

$ git checkout master
# Switched to branch 'master'
$ git merge --no-ff hotfix-1.2.1
# Merge made by recursive.
# (Summary of changes)
$ git tag -a 1.2.1

Agora, inclua o bugfix no develop também:

$ git checkout develop
# Switched to branch 'develop'
$ git merge --no-ff hotfix-1.2.1
# Merge made by recursive.
# (Summary of changes)

A única exceção à regra aqui é quando existe um branch release corrente, então o hotfix irá ser mergeado com esse branch e consequentemente também será mergeado no develop quando o release for finalizado. Caso o develop precise urgentemente desse bugfix e não pode esperar, você pode mergear o branch release no branch develop sem prejuízos .

E finalmente o hotfix pode ser removido:

$ git branch -d hotfix-1.2.1
# Deleted branch hotfix-1.2.1 (was abbe5d6).

Essa é um resumo e tradução do artigo A successful Git branching model

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.