Início > Processo de Desenvolvimento > Um Pouco de Merge

Um Pouco de Merge

Certa vez ocorreu a maior polêmica quando precisávamos fazer merge de dois branches. Cometi vários enganos, pois não lembrava qual era o procedimento. Agora que pesquisei um pouco e reavivei a memória, contarei de forma sucinta o que é e como fazer merge. Não tratarei sobre ferramentas de merge embora tenha preferência pelas que vêm embutidas no Subclipse e no Tortoise, pois entendi que antes de utilizar uma ferramenta é necessário entender conceitos.

Alguns Conceitos

Merge é uma operação fundamental do controle de revisão. Consiste na fusão de variadas alterações feitas em um mesmo arquivo em ramos diferentes de desenvolvimento.

Muitos softwares de controle de revisão trazem ferramentas que auxiliam o merge. Em situações ideais, ou seja, quando não ocorrem conflitos, o merge pode ser feito automaticamente através dessas ferramentas. Em situações de conflito, ou seja, quando há alterações na mesma linha de um arquivo que será integrado, o merge deve ser feito manualmente. Nesse caso, o responsável pelo merge deve decidir exatamente o que o arquivo resultante deveria ter.

Trunk é a linha principal de desenvolvimento. A partir dela nascem os branches que a ele serão reintegrados ao término do processo de merge.

De acordo com o livro do Subversion:

Um branch é uma linha de desenvolvimento que existe independentemente de outra linha ainda que compartilhe um histórico comum.

O branch sempre inicia sua vida como uma cópia do trunk ou de outro branch. A partir daí dará continuidade a seu próprio histórico independente da linha de desenvolvimento da qual ele se originou.

Branches sempre trazem associada uma idéia de tempo, pois nascem em algum momento no passado e são destruídos ou descontinuados em algum momento no futuro depois que seu objetivo foi atingido.

Uma Tag é uma fotografia de um projeto em um dado momento, ou seja, trata-se de um branch congelado. Em uma tag são inseridos metadados de uma versão, como número de release, revisão, etc.

Quando Criar Um Branch

Há vários motivos para criar um branch. Um deles é quando uma funcionalidade a ser desenvolvida causa impacto em várias partes de um sistema e essa funcionalidade não está prevista para a próxima release. Deve-se criar um branch que será integrado ao trunk apenas no momento oportuno.

Outro motivo é a criação de uma tag. Uma tag indica o momento em que foi gerada uma versão.

Até mesmo um merge às vezes necessita da criação de um branch auxiliar. Imaginem um caso em que muito código foi produzido em um branch e muito código potencialmente conflitante foi e continua sendo desenvolvido no trunk.

Uma das alternativas é pedir para que a equipe de desenvolvimento pare de fazer commit de suas alterações, mas o desenvolvimento não pode parar. A outra é criar um branch auxiliar a partir do trunk para fazer o merge em duas etapas: primeiro compatibilizando o branch com o branch auxiliar e em seguida o branch auxiliar com o trunk, pois ele continuou evoluindo enquanto o merge era feito.

Juntando o Joio ao Trigo

A figura abaixo mostra a situação de dois ramos de desenvolvimento em um dado momento:

Fig. 1 - Linhas de Desenvolvimento

Fig. 1 - Linhas de Desenvolvimento

Quando faço um merge de dois ramos, primeiro descubro a revisão do trunk em que o branch foi criado e em seguida a sua revisão Head. No exemplo abaixo, o branch foi criado na revisão 2379 do trunk e sua revisão Head é a 2593. As alterações constantes nesse intervalo (2379-2593) serão integradas às alterações ocorridas até a revisão Head do trunk, que é a 2450.

Fig. 2 - Revisões das Linhas de Desenvolvimento

Fig. 2 - Revisões das Linhas de Desenvolvimento

Após a compatibilização, deve ser adicionado metadado ao commit do trunk. Costumo inserir algo como:

Merge de BRANCH (de 2379 até 2593) para TRUNK (Head 2450)

O importante é formalizarmos uma maneira de marcar o ponto onde o merge ocorreu. Com checkpoints, podemos encontrar mais facilmente o ponto em que um bug foi inserido observando os dados da compatibilização de branches.

Há casos, muitas vezes alheios a nossa vontade, em que um branch é derivado de outro branch e não diretamente do trunk:

Fig. 3 - Linhas de Desenvolvimento Derivadas

Fig. 3 - Linhas de Desenvolvimento Derivadas

Esse tipo de situação não é muito comum, mas como dizia o professor de um antigo colega:

É 100% de certeza de que não acontece, mas pode acontecer.

O processo é mais longo, mas ainda assim não é traumático. Basta abstrair a idéia de branch e trunk para linha de origem e linha de destino e fazer o merge em duas etapas. Primeiro, deve-se descobrir as revisões das linhas de desenvolvimento envolvidas:

Fig. 4 - Revisões das Linhas de Desenvolvimento Derivadas

Fig. 4 - Revisões das Linhas de Desenvolvimento Derivadas

Em seguida, deve-se fazer o merge do branch 2 (origem) com o branch 1 (destino) seguindo os passos informados. Por fim, faz-se o merge do branch 1 (origem) com o trunk (destino).

E Se Houver Conflitos?

Em uma situação ideal, no branch que está sendo integrado não existem quaisquer alterações feitas em arquivos que também foram alterados no trunk. Sendo esse o caso, o merge pode ser feito automaticamente por alguma ferramenta especializada.

Mesmo quando alterações são feitas nos mesmos arquivos nos dois ramos de desenvolvimento é possível fazer merge automático desde que as alterações não tenham sido feitas na mesma linha do arquivo. Nesse caso, ocorre um conflito de versões.

Conflitos são mais comuns do que se pode imaginar. Eu acharia muito estranho se durante todo o desenvolvimento de um projeto – alguns meses ou até alguns anos – nunca houvesse uma situação em que duas pessoas necessitassem alterar um mesmo trecho de código, pois trata-se de um sistema e não de um conjunto de partes isoladas. É natural que haja código compartilhado ou potencialmente reaproveitável em uma aplicação.

Um editor de conflitos permite que se visualize em uma mesma janela o arquivo alterado no branch, o mesmo arquivo na cópia local (trunk) e o resultado do merge das duas versões do arquivo.

Se houver conflito, deve-se entrar em contato com a pessoa que fez as alterações no trunk e a que fez as alterações no branch para que possamos decidir qual é a alteração que ficará na versão integrada. Terminada a edição do conflito, deve-se realizar o commit das alterações atribuindo o metadado.

Cadê os Testes Unitários?

Pois é, cadê? Por motivos variados, os testes unitários ficam desatualizados ou simplesmente não existem. Não podemos confiar na inspeção visual de código integrado automaticamente e muito menos manualmente.

Quando não temos uma forma segura e automatizada de garantir que o código integrado funciona corretamente, não saberemos se foi introduzido um bug. Sendo assim, garanta que os testes possuem boa cobertura e sempre estejam atualizados.

Anúncios
  1. Nenhum comentário ainda.
  1. 17/02/2010 às 12:32 PM
  2. 31/05/2013 às 10:19 PM

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: