Quando trabalhamos com algum tipo de fluxo de trabalho no Git, como o Git flow, por exemplo, acabamos por criar várias ramificações da branch principal e com isso precisaremos fazer a unificação quando houver a finalização das novas branches. O comando git merge auxilia nessa tarefa de juntar as ramificações.
No decorrer dos dias, podemos nos deparar com problemas na hora de se fazer uma mesclagem de branches, sendo um dos principais a ocasião em que dois desenvolvedores modificam o mesmo arquivo e acabam criando um conflito entre as branches.
Dessa forma, veremos as alternativas possíveis para a solução de conflitos, formas alternativas para unificar as branches e muito mais! Segue o conteúdo:
- O que é o Git merge e como funciona?
- Quais as diferenças entre Git merge e Git rebase e quando usá-los?
- Quais são as estratégias de mesclagem para Git merge?
- Como preparar as branches para a mesclagem?
- Git merge: fazendo a mesclagem na prática
- Como encontrar conflitos durante a mesclagem?
- Opções para usar com Git merge
O que é o Git merge e como funciona?
Git merge é o comando que unifica algum histórico bifurcado. Resumindo, o Git merge permite que você pegue as linhas criadas a partir do Git branch e faça uma integração para a ramificação principal. É importante notar que o comando git branch cria uma nova ramificação a partir da branch que o desenvolvedor está situado.
Ainda, vale ressaltar que a mesclagem é sempre feita com o auxílio do comando Git checkout, isso porque a alteração vai ser feita apenas na branch principal e a nova ramificação não sofre alteração. Exemplificando, fazemos a troca de branches com o Git checkout e, logo em seguida, fazemos a mesclagem da nova bifurcação para a ramificação principal.
O funcionamento do Git merge é feito a partir da combinação de várias sequências de commits um um histórico unificado. É amplamente usado para mesclar duas branches. Com isso, o algoritmo da ferramenta pega a ponta de cada branch e encontra o commit base em comum. A partir disso, ele cria uma confirmação dessa mesclagem que combina as alterações de cada sequência do commit.
Vamos dizer que a nossa nova ramificação é baseada na branch main. Agora, a gente quer unificar a nova funcionalidade na branch principal.
Rodando o comando, temos a mesclagem da branch com a nova funcionalidade para a branch atual que vamos assumir sendo a main. O Git automaticamente escolhe o algoritmo que vai ser usado para essa operação.
O commit de mesclagem acaba se tornando único com essa operação. Na hora da criação do commit de mesclagem, o Git tenta automaticamente mesclar o histórico dos commits para facilitar a sua vida. Se o Git encontra algum pedaço de dado alterado em ambos os históricos, não vai ser possível fazer essa operação de forma automática. Esse cenário é chamado de conflito no versionamento de códigos e o Git vai pedir a intervenção do usuário ou usuária para continuar a unificação.
Quais as diferenças entre Git merge e Git rebase e quando usá-los?
A mesclagem do Git merge acontece entre duas branches e o resultado é um commit novo com todas as alterações realizadas da nova branch. Já o Git rebase executa uma unificação das ramificações em um só, com isso conseguimos obter um histórico de commits mais limpo.
Uma das principais desvantagens do rebase é a forma que ele altera a estrutura das branches envolvidas, acabando por reescrever até mesmo o histórico dos commit delas, o que pode causar até a perda do trabalho durante o processo de mesclagem. Também não conseguimos rastrear quando foi feita a unificação, já que o rebase não gera nenhum commit de mesclagem.
De acordo com o fluxo de trabalho do Git flow, temos uma regra chamada “Golden Rule of Rebasing” que basicamente diz para não fazermos rebase em branches públicas, já que, além de ser uma operação perigosa, acabamos perdendo o histórico de commits, causando desencontros das ramificações locais e remotas. No entanto, é possível utilizar os dois comandos em um fluxo de trabalho de forma efetiva
A partir da branch principal do seu repositório, criamos uma ramificação de uma nova funcionalidade. Nela, geramos algumas alterações e adicionamos uma nova branch utilizando o Git Commit. Logo depois, precisamos mesclar as branches e, com isso, é necessário realizar dois passos: o primeiro é juntar as alterações feitas na master em nossa branch e logo em seguida mesclar a funcionalidade na branch principal.
Analisando com calma, podemos perceber que não faz sentido criar um commit de mesclagem nessa ação, ficaria muito mais limpo se pudéssemos simplesmente trazer as alterações da master para a nossa branch. Com isso em mente, utilizando o rebase, essa possibilidade se torna viável. Rodando o comando abaixo temos essa operação feita pelo Git.
git rebase master
A partir disso, temos que fazer agora a unificação da funcionalidade na branch principal, já que cuidamos dos possíveis problemas de conflitos nos arquivos e conseguimos agora mesclar de maneira simples as branches com o Git merge, criando assim um novo commit de mesclagem que nos auxilia como um histórico de mesclagem das branches.
Dessa forma, conseguimos utilizar o melhor dos dois mundos para melhorar o nosso fluxo de trabalho com a ferramenta!
Quais são as estratégias de mesclagem para Git merge?
O Git tem diversos métodos diferentes para encontrar o commit em comum de uma mesclagem. Esses métodos são amplamente chamados de “estratégias de mesclagem“. Por padrão, o Git merge escolhe por conta própria a sua estratégia de mesclagem, a mesma coisa ocorrendo com o comando Git pull.
Para escolher uma estratégia, o parâmetro -s deve ser passado juntamente com o nome da estratégia. Logo abaixo podemos ver as estratégias de mesclagem disponíveis:
Recursive
git merge -s recursive branch1 branch2
Ela opera apenas duas branches por vez, é a estratégia utilizada por padrão na mesclagem e na hora do Git pull. Também pode detectar mesclagens envolvendo renomeações, porém não pode fazer uso das cópias detectadas.
Essa estratégia pode conter as seguintes opções:
- ours
- theirs
- patient
- diff-algorithm=[patience|minimal|histogram|myers]
- renormalize
- no-renormalize
- no-renames
- find-renames[=<n>]
- subtree[=<caminho>]
Resolver
git merge -s resolve branch1 branch2
Pode resolver apenas unificações com 2 branches e utiliza um algoritmo de mesclagem de 3 vias. Ela tenta com cuidado detectar as ambiguidades da mesclagem cruzada, e também é considerada a estratégia mais rápida e segura.
Octopus
git merge -s octopus branch1 branch2 branch3 branchN
É a estratégia padrão para as mesclagens com mais de 2 branches. Semelhante às outras estratégias, se ocorrer algum conflito, o Octopus se recusa a fazer a mesclagem. Ele acaba sendo muito utilizado para agrupar ramificações semelhantes.
Ours
git merge -s ours branch1 branch2 branchN
A Ours lida com com um número diverso de ramificações. O resultado final sempre é a ponta da branch principal, ignorando a alteração de todas as outras branches. Deve ser utilizada para substituir o histórico antigo de desenvolvimento dos ramos laterais.
Subtree
git merge -s subtree branchA branchB
É uma amplificação da estratégia recursiva. Na mesclagem da branch A com a B, se B for uma subárvore filha de A, B vai ser primeiro atualizada para refletir a estrutura da árvore de A. Essa renovação também ocorre na árvore ancestral comum entre as duas branches.
Como preparar as branches para a mesclagem?
Antes de fazer uma mesclagem, devemos garantir que a unificação ocorra corretamente através de algumas etapas.
Confirme se o branch receptor está correto
Para fazer a confirmação, execute o comando git status. Ele vai retornar a branch que você está alocado. Caso precise, rode o git checkout para trocar de ramificação. Para o nosso caso, podemos rodar git checkout main, por exemplo.
Verificar se os branches estão atualizados com os commits mais recentes
Antes de tudo, precisamos verificar se as branches que vamos fazer a mesclagem estão com as alterações remotas mais recentes. Para isso execute o comando git fetch, que irá recuperar as informações remotas de todas as ramificações. Assim que a busca for finalizada execute o comando git pull caso seja necessário.
Git merge: fazendo a mesclagem na prática
Após a preparação das branches, podemos agora iniciar a ação de mesclagem a partir do comando Git merge, lembrando de utilizar o nome da branch que vai ser mesclada a partir da ramificação receptora.
Mesclagem de avanço rápido
Uma mesclagem de avanço rápido ocorre quando existe um caminho linear da ponta da ramificação atual até a ramificação que queremos unir. Com isso, ao invés de fazermos a mesclagem das ramificações, o Git apenas move a ponta da branch atual até a ponta da ramificação que escolhemos.
Isso se chama “fast forward”, em português “avanço rápido“. Dessa maneira, os históricos são unificados com eficiência, já que os commits da ramificação desejada estão presentes na branch atual.
Mesclagem de 3 vias
A mesclagem de 3 vias ocorre quando a branch principal progride enquanto a nova ramificação está em desenvolvimento. Dessa maneira, o Git não consegue realizar o avanço rápido das branches e acaba tendo que realizar a mesclagem convencional, criando por fim o commit da mesclagem. Esse cenário é muito comum em times de desenvolvimento, dado que pode vir a ocorrer muitas alterações na ramificação principal.
Como encontrar conflitos durante a mesclagem?
Quando ocorre um conflito entre duas ou mais ramificações, o Git pede a intervenção do desenvolvedor ou desenvolvedora para que faça a correção das versões que serão usadas.
Como resolver os conflitos?
A apresentação dos conflitos dependerá de cada máquina. Se você não alterar a configuração do programa de merge no Git config, provavelmente o arquivo aparecerá no terminal juntamente com o programa Vim. Dessa forma, para resolver os conflitos você deve procurar nos arquivos que estão listados como conflituosos as marcações digitais: <<<<<<<, =======, e >>>>>>>. Um exemplo disso é:
Aqui vai algum conteúdo que não foi afetado pelo conflito
<<<<<<< main Esse é algum texto conflituoso da branch main
=======Esse é algum texto conflituoso da branch com a funcionalidade
>>>>>>> branch da funcionalidade;
Por fim, você pode resolver o conflito da melhor maneira que encontrar. Vale ressaltar que conflitos apenas ocorrem em mesclagens de 3 vias, não é possível ocorrerem alterações conflituosas em mesclagens de avanço rápido.
Opções para usar com Git merge
Veremos agora algumas opções úteis na hora de mesclar as branches, para uma lista completa, verifique a documentação oficial do Git.
Criando commits de avanço rápido: –no-ff
git merge main --no-ff
Esse comando serve para criar um novo commit mesmo quando o avanço rápido é possível.
Parando conflitos: –abort
git merge main --abort
Caso ocorra um conflito entre as ramificações, essa opção vai abortar a mesclagem e restaurar o estado do projeto anterior ao merge.
Editando o commit de mesclagem: –no-commit
git merge main --no-commit
Nessa opção temos a possibilidade de editar o commit de mesclagem antes de ele ser adicionado ao histórico. Lembre-se que o avanço rápido não cria commits de mesclagem. Para esse caso, utilize o –no-ff juntamente o –no-commit.
Entendemos como é feita a mesclagem de ramificações do Git Merge, as suas estratégias, peculiaridades e opções. Dessa maneira percebemos que esse comando é de extrema utilidade no auxílio de fluxos de trabalho individuais e conseguiremos, com poucos comandos, unificar quantas branches forem necessárias e de forma simples avançar uma branch de forma rápida sem a necessidade de criar uma commit de mesclagem.
Vimos também como é o funcionamento dos conflitos de arquivos HTML, CSS, Javascript ou qualquer outro tipo de dado e como é o comportamento do Git merge nesses casos. Para finalizar, não devemos esquecer das opções que, como qualquer outro comando do Git, temos a nossa disposição, conseguimos cancelar o avanço rápido, editar commits antes das mesclagens e até mesmo abortar a união quando ocorre algum tipo de conflito.
Gostou do artigo? Continue lendo sobre os 10 comandos Git que toda pessoa que programa precisa saber!