Entenda as diferenças entre os tipos de merge no Git: Fast-forward X Three-way
Tags: git, beginners, programming, versioncontrol
Published: 2021-12-29 01:44:13

Pré-requisitos
- [Conceitos básicos sobre Git](https://dev.to/rsantanarj/entenda-conceitos-basicos-sobre-git-d2f)

Como todo sistema de controle de versão (VCS) o Git dispõe de recursos de criação e união de _branches_. Para mesclar uma _branch_ na outra, você pode considerar _merge_ ou _rebase_. Nesse artigo será apresentado o que é um _merge_, as estratégias envolvidas _fast-forward_ e _three-way_ e o que são conflitos e quando podem ocorrer. 

## O que é um _merge_?
_Merge_ é uma opção muito comum e útil em repositórios que possuem mais de uma _branch_ além da principal. Se é necessário fundir uma _branch_ em outra, o _merge_ pode ser uma opção.

Vale lembrar que uma _branch_ no Git, é um ponteiro para um _commit_, que será a última versão de tal ramificação. Por exemplo, considere a sequência, a qual inicialmente existe um _commit_ inicial "first commit" na _branch_ principal.

![Merge etapa 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bdunpoxet6x77r7ap5tc.png) 

Em seguida, um novo _commit_ "setup" é realizado.

![Merge etapa 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3tvsogrt8uvzvmunl343.png) 

Após isso, um contribuidor João cria uma nova _branch_ "login" a partir do _commit_ setup. Isso significa que neste momento ambas as _branches_ apontam para a mesma versão do repositório e que possuem o mesmo histórico, porém são dois ramos totalmente independentes. Qualquer nova versão em uma _branch_ não afetará a outra e vice-versa. Observe o resultado de um novo _commit_  "creating login feature" na _branch_ "login".

![Merge etapa 3](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ny1402kntc5gffu4vdff.png) 

Note que o rótulo da _branch_ login passou a referenciar este último _commit_, mas o rótulo da _branch_ master ainda está apontando para o _commit_ "setup". Ou seja, a _branch_ login está a um _commit_ à frente em relação a _branch_ master.

E se João desejar integrar os _commits_ feitos na _branch_ login na _branch_ master, como fazer isso? Uma das opções, é através de _merge_.

## _Merge fast-forward_
João decide integrar a _branch_ login na _branch_ master. Para isso ele retorna para a _branch_ master com o comando abaixo:

`git checkout master`

![Fast-forward etapa 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/16cfmymsutf6o9qvtpvv.png) 

Em seguida executa o comando:

`git merge login`

![Fast-forward etapa 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fdhnf15gh98xtboyiyl7.png) 

Note que nessa situação, a única tarefa necessária que o Git precisou realizar foi mover o rótulo da _branch_ master para o _commit_ "creating login feature". Por isso, o nome dessa estratégia é _fast-forward_ (em português avanço rápido), pois é simplesmente uma alteração de ponteiro.

## _Merge three-way_
Agora considere que antes de João integrar a _branch_ login na _branch_ master, uma outra contribuidora Maria promoveu um novo _commit_ "creating user feature" na _branch_ master.

![Three-way etapa 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhtuz95soahpbl2g0d9p.png) 

Nesse caso, uma tentativa de _merge_ não terá o mesmo resultado do exemplo anterior. Isso porque um _commit_ foi realizado na _branch_ master após a criação da _branch_ login. Então não basta mover o ponteiro, há duas versões que precisam ser combinadas, que são o "creating user feature", _commit_ que só existe na _branch_ master e "creating login feature", _commit_ que só existe na _branch_ login. Veja a seguir o resultado de um _merge_ nessa situação.

![Three-way etapa 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nhzbbjvkv7om775yzbhw.png) 

Observe que foi gerado um novo _commit_ "Merge". Tal _commit_ foi gerado de forma automática pelo Git, que uniu as alterações envolvidas nos dois ramos em uma nova versão do repositório. Veja também que este novo _commit_ possui dois pais e o histórico do repositório não é mais linear. A estrutura que representa o histórico linear ou bifurcado no Git pode ser definida como DAG (_Directed Acyclic Graph_ ou Grafo Acíclico Dirigido).

## Conflitos
No exemplo anterior, ao realizar um _merge_ com a estratégia _three-way_, o Git conseguiu gerar um _commit_ de _merge_ automaticamente, pois as alterações que precisavam ser combinadas não conflitavam. Entretanto, as duas _branches_ poderiam ter alterado as mesmas linhas de um mesmo arquivo e nesse caso, o Git não pode determinar automaticamente o que está correto e é necessário decisões humanas. 

Os conflitos afetam apenas o contribuidor que conduz a mesclagem, o resto da equipe não tem conhecimento do conflito. O Git marcará o arquivo como conflitante e interromperá o processo de fusão. É responsabilidade desse contribuidor resolver o conflito.

Por exemplo, imagine que Joao além de ter criado um novo recurso login, tenha alterado a linha 4 de um arquivo global do sistema. Maria também, além de ter criado o novo recurso de usuários, alterou a linha 4 desse mesmo arquivo. Qual alteração deve prevalecer no _commit_ de _merge_, a alteração de Maria? Ou a alteração de João? O Git não pode determinar isso de forma automática, João que realizou a mesclagem que irá avaliar. Talvez a alteração de João seja melhor, como ele também pode considerar que a alteração de Maria faça mais sentido ou ainda, a alteração de ambos se complete.

Vale destacar que conflitos só surgem em mesclagens com a estratégia _three-way_. _Merge_ _fast-forward_ nunca irá gerar um conflito, pois não há histórico divergente a ser combinado. É somente mudança de ponteiro.

## _No fast-forward_
Considere novamente a seguinte situação:

![No fast-forward etapa 1](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0zsbo52o6xhs3a7aibyo.png) 

Neste caso, quando foi executado um _merge_ da _branch_ login em direção a _branch_ master, o Git assumiu a estratégia _fast-forward_ e moveu o rótulo da _branch_ master para o _commit_ "creating login feature". Porém, é possível forçar a utilização de uma estratégia _three-way_, fazendo que o Git crie um _commit_ de _merge_. Para isso basta incluir a opção --no-ff (no _fast-forward_) no comando _merge_:

`git merge --no-ff login`

![No fast-forward etapa 2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/57ezibisgk81dzgbfo3r.png) 

Note que esta era uma situação típica de _fast-forward_, porém com a opção --no-ff o Git foi forçado a gerar um _commit_ de _merge_. Isso pode ser útil, ocasionalmente, caso você precise marcar pontos de mesclagem no histórico. Você só deve ter em mente que um histórico com muitos _commits_ de _merge_ pode torná-lo confuso e de difícil compreensão. Já em situações que o _merge_ seria pela estratégia _three-way_, você não pode forçar um _fast-forward_.

## _Merge_ sem _checkout_, é possível?
Nos exemplos vistos, antes de uma ação de _merge_, foi realizado um _checkout_ na _branch_ master. Em todos os casos o objetivo era um só, incluir os _commits_ exclusivos da _branch_ login na _branch_ master, que era a _branch_ corrente com o _checkout_.

Neste contexto, uma pergunta que pode surgir é sobre a possibilidade de fazer uma mesclagem entre duas _branches_, que não são a _branch_ corrente. A resposta para esta dúvida pode ser respondida pelo texto a seguir, retirado diretamente da [fonte](https://stackoverflow.com/questions/3216360/merge-update-and-pull-git-branches-without-using-checkouts):

> _"...Você não pode mesclar uma branch B na branch A sem fazer o checkout de A primeiro, caso isso resulte em uma mesclagem sem fast-forward (three-way). Isso ocorre porque uma cópia de trabalho é necessária para resolver quaisquer conflitos em potencial. No entanto, no caso de mesclagens fast-forward, isso é possível , porque tais mesclagens nunca podem resultar em conflitos, por definição..."_

## Conclusão
Como vimos, um _merge_ pode ser executado de duas formas: por estratégia _fast-forward_ ou _three-way_. A primeira, quando possível, será a melhor opção pois mantém um histórico linear e está livre de resolução de conflitos, pois envolve somente alteração de referência. Já a segunda, além de gerar um histórico bifurcado, com _commits_ adicionais de mesclagem, ainda está suscetível a resolução de conflitos. Esses _commits_ adicionais podem prejudicar a leitura do histórico. Ao menos que você utilize um --no-ff, o Git irá definir a estratégia de _merge_ de acordo o histórico das duas _branches_ envolvidas na mesclagem.

Quer aprender mais sobre esta ferramenta essencial para desenvolvedores de software? Aprenda no mais novo curso de [Git - Básico ao avançado] (https://www.udemy.com/course/git-basico-ao-avancado-2021/?referralCode=D1231C6B99B30C6EDF14)