• Arts Lab in Interfaces, Computers, and Everything Else

    (Education, Exceptions, Experiences, Enterntainment, Environment, Entropy, Errors, and Etecetera...)


Fluxo de desenvolvimento com o modelo GitFlow


GitFlow

O GitFlow é um modelo de ramificação para desenvolvimento de software, utilizando a ferramenta de controle de versão git. Esse modelo, criado pelo engenheiro de software Vincent Driessen, tornou-se popular. Segundo Driessen, adequado para softwares com versionamento explicito ou para manter múltiplas versões softwares, garantindo uma estabilidade do ramo principal durante o processo de desenvolvimento.

Figura 1: Fluxo de desenvolvimento do modelo de ramificação GitFlow.
Figura 1: Fluxo de desenvolvimento do modelo de ramificação GitFlow.


O modelo possui uma organização de branches e fluxo de trabalho que torna fácil a localização pontos de alterações do código, visualizando facilmente cada implementação.

Repositórios remotos

O repositório do projeto é centralizado. Cada desenvolvedor contém um fork do repositório central para fazer o push das alterações/commits em vez de manipular diretamente o repositório central. Dessa forma, para que as alterações sejam adicionadas no repositório central, o desenvolvedor deve criar um Pull Request, permitindo que os administradores do projeto revisem as alterações antes de aceitá-las. Também pode ser feito o pull entre os forks, criando sub-times para trabalharem em uma mesma implementação – sub-times de dois ou mais membros. A imagem a segui apresenta essa configuração de repositórios remotos:

Figura 2: Configuração de repositórios: Centralizado mas descentralizado.
Figura 2: Configuração de repositórios: Centralizado mas descentralizado.


Na imagem anterior, origin refere-se ao repositório central e os demais repositório são os forks do central – Alice, Bob, David e Chair. A imagem ilustra os sub-times Alice/Bob, Alice/David e Clair/David. Vale ressaltar que não é feito Pull Request para o repositório central das alterações de trabalhos em progresso, o desenvolvedor ou um sub-time deve finalizar o trabalho no seu respectivo fork antes de fazer o Pull Request.

Branches

No modelo GitFlow, o repositório central do projeto contém dois branches: o master e o develop, ambos com vida infinita. O master é o branch principal e o seu código-fonte (HEAD) é sempre uma versão estável – production release. O branch develop contém sempre implementações completas de features para a próxima release.

Os desenvolvedores não trabalham diretamente nos branches master e develop. Branches de apoio são criados para a implementação de correções de bugs, implementação de features, alterações de metadados da release e outros processos de release. Diferente do master e develop, os branches de apoio tem finita e possuem implementações em progresso.

Tipos de branches de apoio:

  • Feature: para implementação de novas features;
  • Release: para processos de release como alterações de metadados e empacotamento do software;
  • Hotfix: para correções de bugs que não podem esperar a próxima release.

Cada um dos tipos, além de terem um objetivo específico, estão sujeitos a regras estritas sobre quais ramificações podem ser sua ramificação de origem e quais ramificações devem ser seus destinos de mesclagem, sendo categorizados pela forma de uso. A seguir, veremos os detalhes de cada um dos tipos de branches de apoio, em paralelo com exemplos ilustrando o fluxo de trabalho do modelo GitFlow e os comandos utilizados em cada etapa!

Branches feature

Figura 3: Branch feature.
Figura 3: Branch feature.

Regras:

  • Ramificação a partir do branch develop;
  • Após finalizar a implementação, são mesclados para o branch develop;
  • Não tem convensão de nome, o nome apenas remete a feature em desenvolvimento;
  • Existem apenas nos forks do repositório central.

Criando um branch feature

Para implementação de uma nova feature, é criado um branch de apoio do tipo feature a partir do branch develop. Exemplo:

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

O comando git checkout com o parametro -b <nome do branch>, cria o branch e muda para o branch criado. O paramentro develop, no final do comando, informa que o branch será criado a partir do branch develop.

Mesclando (Merge) o branch feature no branch develop

Após finalizar a implementação da feature no branch myfeature, o branch é mescado no branch develop. Feito o merge, deletamos o branch myfeature e subimos as alterações (push) para o repositório remoto do desenvolvedor. Para isso, usamos os seguintes comandos:

$ 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

Neste e nos exemplo a seguir, o parâmetro origin é o link do repositório remoto do desenvolvedor.

A flag --no-ff faz com que o merge sempre crie um commit para registrar a existência histórica de um branch, agrupando os commits do branch. Compare, na Figura 4, o comando merge com e sem a flag:

Figura 4: Diferença entre o merge com e sem a flag –no-ff.
Figura 4: Diferença entre o merge com e sem a flag –no-ff.


Se não usarmos a flag, teríamos que ler as mensagens de log dos commits para identificar quais são os commits que juntos consolidam uma implementação. Com a flag, essa identificação torna-se bem mais fácil. A Figura 4 destaca os commits que consolidam a implementação de uma feature, nas duas situações.

Branches release

Regras:

  • Ramificação a partir do branch develop;
  • Após finalizar a implementação, são mesclados para os branches develop e master;
  • Convenção de nome: release-<número de versão>.

No branch release, finalizamos a release do software. É feito a alteração dos metadados, atualizando o número de versão do software e realizando outros procedimentos de release como o empacotamento do software. Além disso, é permitido pequenas correções de bugs. O branch é criando quando as implementação no branch develop está em um estado bem próximo da nova release do software.

Terminando o processo de release, o branch release é mesclado para o branch develop e o para o branch master. Nesse momento o branch develop fica livre para a implementações de novas features de uma futura release.

Criando o branch release

Neste exemplo, o número 1.2 é a versão do release. Observe que o nome do branch segue a convenção release-<número de versão>:

$ 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 criar o branch e mudar para ele, realizamos os procedimentos de release. O arquivo bump-version.sh é um script fictício que altera os arquivos de metadados para refletir a nova versão. Após executar o script, fazemos o commit para registrar as alterações.

Finalizando o branch release

Quando o estado do ramo de release está pronto para se tornar um release real, algumas ações precisam ser executadas. Primeiro, o ramo de release é mesclado no master (já que todo commit no master é um novo release por definição, lembre-se). Em seguida, atribuímos uma tag nesse commit no master, termos uma referência a esta versão histórica. Finalmente, as alterações feitas na ramificação da versão precisam ser mescladas novamente ao branch develop, para que versões futuras também contenham essas alterações.

As duas primeiras etapas no Git:

$ 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

Mesclando o branch release no branch develop:

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

Essa etapa pode levar a um conflito de mesclagem. Se sim, corrija e confirme.

Agora, o processo de release foi finalizado e o branch de release pode ser removido, pois não precisamos mais dele:

$ git branch -d release-1.2
Deleted branch release-1.2 (was ff452fe).

Branches hotfix

Figura 5: Branch hotfix.
Figura 5: Branch hotfix.

Regras:

  • Ramificação a partir do branch master;
  • Após finalizar a implementação, são mesclados para o branch master;
  • Convenção de nome: hotfix-<número de versão>.

Os branches hotfix são muito parecidos com os branches de release, pois também entregam uma nova versão do software, embora não planejada. Eles surgem da necessidade de agir imediatamente em um estado indesejado de uma versão de produção lançada – versão de produção.

Quando há um erro crítico em uma versão de produção, não podemos esperar a próxima release para corrigir-ló. S sendo assim, um branch hotfix é criado para a correção. A essência é que o trabalho dos membros da equipe (no branch develop) pode continuar enquanto é feito a correção.

Criando o branch hotfix

Esse branch é criado a partir do master e o seu nome segue a convenção hotfix-<numero de versão>. No exemplo a seguir, o bug encontra-se na versão 1.2 (1.2.0) e incrementamos o terceiro número da versão. Essa incrementação segue as regras do Semantic Versioning 2.0.0:

$ 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(-)

Após criar o branch e incrementar o número de versão nos arquivos de metadados, podemos corrigir o bug 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 o branch hotfix

Ao terminar a implementação de correção do bug, o branch hotfix é mesclado aos branches master e develop. Primeiro, atualize o master e marque a versão com a tag:

$ 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

Em seguida, inclua a correção do bug 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)

Por fim, remova o branch hotfix:

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

Referências

  1. https://nvie.com/posts/a-successful-git-branching-model/

Back to blog