O Git é uma ferramenta incrível para mesclar diferentes ramos de código. Na maioria das vezes as mudanças feitas por você e por seus companheiros de projeto são mescladas automaticamente, mas de vez em quando acontecem conflitos. Neste artigo você vai aprender como resolver esses conflitos e também como evitar que aconteçam.
Em termos simples, um conflito de merge no Git ocorre quando dois desenvolvedores alteram o mesmo trecho de código e a única maneira de resolver este conflito é através de uma intervenção manual, alterando o código em questão e submetendo um novo commit.
Conflitos podem acontecer tanto ao mesclar branches (ramos), quanto ao mesclar commits dentro da mesma branch. O conceito parece simples. Entenda abaixo como os conflitos acontecem e como resolvê-los na prática.
Como os conflitos acontecem
Conflitos no Git são bastante comuns e acontecem sempre quando o mesmo arquivo foi modificado por duas versões diferentes e essas versões não podem ser automaticamente mescladas. Veja a seguir como um conflito pode acontecer na prática:
Digamos que você e seu amigo estão trabalhando na mesma branch em um repositório remoto do GitHub. Vocês estão trabalhando no código de um site.
Você abre o arquivo "index.html" e altera o código que corresponde ao título do site para "Hello World".
Você salva o que acabou de fazer através de um novo commit local.
git add index.html
git commit -m "Alterando título do site"
Em seguida, você precisa usar git pull
para receber o repositório remoto, mesclar o código na sua máquina para então poder atualizar o repositório com as suas próprias alterações.
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Ops! Uma mensagem de CONFLITO. Sabemos que o conflito ocorreu em index.html, pois a mensagem de conflito indica isso.
Ao abrir o arquivo index.html, ele agora contém o seguinte:
<html>
<head>
<<<<<<< HEAD
<title>Hello World</title>
=======
<title>Olá Mundo</title>
>>>>>>> master
</head>
<body>
Lorem ipsum dolor sit amet consectetur ...
</body>
</html>
Bom, acontece que seu amigo deu um commit seguido de um push antes de você, alterando a mesma linha de código de forma diferente. Neste caso ele escreveu "Olá Mundo" no título, e você, "Hello World".
O Git é fantástico em resolver fusões de código automaticamente, porém em casos onde duas alterações diferentes são feitas no mesmo trecho de código, o sistema de versionamento não tem como saber qual das versões é a definitiva. Precisamos então, intervir manualmente no código, e para isso o Git cria essa flag, com "<<<<<<<", "=======" e ">>>>>>>", mostrando que nas seguintes linhas houve conflito.
Entendendo a marcação de conflito do Git
Para entender essa marcação de conflito, você precisa entender primeiro que o conflito ocorre entre duas versões daquilo que o Git julga ser o mesmo trecho de código.
- A primeira sendo a sua versão (HEAD), e;
- A segunda sendo a versão do ramo/branch que estamos tentando mesclar (master, neste caso).
Portanto a sintaxe da marcação vai ser sempre da seguinte forma:
<<<<<<< HEAD
Aqui está a sua alteração de código.
=======
Aqui vai a alteração do seu amigo que o Git tentou mesclar
>>>>>>> master
Como resolver conflitos no Git
Primeiramente, precisamos localizar todos os arquivos onde ocorreram conflitos. Eu recomendo inspecionar as mensagens de conflito como no exemplo abaixo:
CONFLICT (content): Merge conflict in index.html
Ao final da linha você tem o arquivo onde ocorreu o conflito, mas se por acaso você já fechou o terminal e não tem indicação de quais arquivos tem conflito, você pode usar git status
e ver quais arquivos foram modificados pelo merge ou fazer uma busca geral no projeto (dependendo do seu editor de código pode ser Ctrl + Shift + F) por "<<<<<<<", ou "=======".
Como explicado acima, o Git gera essa marcação com duas versões do mesmo código: A versão que você tem, e a versão que está tentando mesclar.
Para resolver o conflito, portanto, é preciso alterar o arquivo para que contenha apenas uma versão. Apagando o código incorreto e removendo as marcações de conflito. Veja o exemplo abaixo:
<html>
<head>
<<<<<<< HEAD
<title>Hello World</title>
=======
<title>Olá Mundo</title>
>>>>>>> master
</head>
<body>
Lorem ipsum dolor sit amet consectetur ...
</body>
</html>
Nosso objetivo é deixar assim:
<html>
<head>
<title>Hello World</title>
</head>
<body>
Lorem ipsum dolor sit amet consectetur ...
</body>
</html>
Ou seja, a questão aqui foi apenas escolher qual versão do código apagar. Mas vai ter casos em que uma versão é complementar à outra, então você pode copiar parte do código do seu amigo e parte do seu código, gerando uma terceira versão definitiva para aquele trecho de conflito.
Não há regras sobre o que manter ou apagar na resolução de um conflito. A regra é que o arquivo seja alterado, mantendo-o em uma versão funcional e correta.
Depois que resolveu o conflito, você precisa adicionar a resolução à um novo commit para que seu código possa ser salvo no repositório remoto.
git add index.html
git commit -m "Resolvendo conflitos"
git push
Como evitar conflitos no Git
De vez em quando é inevitável que duas pessoas alterem o mesmo trecho de código, portanto eu não recomendo evitar conflitos a todo custo, mas se familiarizar com a forma de corrigir os conflitos e adotar isso como parte da rotina de desenvolvimento.
Mas existem casos em que os conflitos podem gerar bagunça, especialmente em projetos grandes, onde centenas de arquivos podem ser alterados automaticamente em certos casos.
Para isso, a minha dica é tentar fazer com que todos na equipe sempre estejam atualizados com o repositório remoto através de git pull frequente, pois sempre que alguém tem um código muito desatualizado, essa pessoa pode fazer commits com alterações novas em códigos que já estão 20 versões atrasadas.
Isso geralmente gera retrabalho e dor de cabeça para quem tem de resolver os conflitos e interpretar qual seria a versão mais correta do código. Veja abaixo algumas outras dicas e ferramentas para resolução de conflitos que podem te ajudar!
Dicas e comandos para facilitar na resolução de conflitos
Se o conflito surgiu durante um merge, mas poderia ser evitado. Você pode querer desfazer o merge, para isso, você pode usar o comando abaixo, que desfaz o merge e volta a branch para a versão anterior ao merge e anterior ao(s) conflito(s).
git merge --abort
Caso queira desfazer alterações em um arquivo específico que esteja dando conflito, e simplesmente descartar suas alterações locais para que o conflito não ocorra, você pode usar o comando git reset.
git reset
Para entender o status dos arquivos alterados, bem como o que foi alterado em cada arquivo, dois comandos são úteis. O comando status mostra o status de todos os arquivos alterados na sua branch atual.
git status
O comando git diff mostra todas as alterações locais, linha por linha, nos arquivos modificados, inclusive arquivos que estão em conflito e ainda não foram adicionados ao último commit.
git diff
O comando log seguido do argumento merge, mostra uma lista com os commits que tiveram conflitos ao mesclar duas branches.
git log --merge