Conflitos de merge: resolvendo com calma
1. Entendendo a origem dos conflitos
Conflitos de merge são uma realidade inevitável no trabalho colaborativo com Git. Eles ocorrem quando duas ou mais branches modificam as mesmas linhas de um arquivo de maneiras diferentes, e o Git não consegue decidir automaticamente qual versão deve prevalecer.
É importante distinguir dois tipos de conflito:
- Conflito textual: Ocorre quando as mesmas linhas de um arquivo foram alteradas em branches diferentes. O Git sinaliza explicitamente esses conflitos com marcadores especiais.
- Conflito lógico: Acontece quando as alterações são compatíveis textualmente, mas semanticamente incompatíveis (exemplo: uma função é renomeada em uma branch e seu único chamador é modificado em outra). O Git não detecta esses conflitos automaticamente.
Quando você tenta fazer um merge e há conflitos, o Git exibe uma mensagem como esta:
$ git merge feature-branch
Auto-merging src/app.js
CONFLICT (content): Merge conflict in src/app.js
Automatic merge failed; fix conflicts and then commit the result.
2. Preparando o terreno antes do merge
Antes de iniciar um merge, verifique o estado do repositório:
$ git status
$ git log --oneline --graph --all
Boas práticas para minimizar conflitos:
- Mantenha sua branch atualizada: Faça
git pullregularmente para incorporar as alterações da branch base. - Commits pequenos e focados: Alterações granulares são mais fáceis de revisar e resolver em caso de conflito.
- Configure uma ferramenta de merge visual:
$ git config merge.tool meld
$ git config mergetool.meld.path /usr/bin/meld
3. Identificando e analisando o conflito
Quando um conflito ocorre, o Git insere marcadores especiais nos arquivos conflitantes:
<<<<<<< HEAD
console.log("Versão da branch atual");
=======
console.log("Versão da branch que está sendo mergeada");
>>>>>>> feature-branch
Para visualizar as diferenças de forma mais clara:
$ git diff
O comando git mergetool abre a ferramenta configurada para cada arquivo com conflito:
$ git mergetool
4. Estratégias de resolução manual
Escolhendo uma versão específica
Para aceitar completamente a versão da branch atual (HEAD):
$ git checkout --ours src/app.js
Para aceitar a versão da branch que está sendo mergeada:
$ git checkout --theirs src/app.js
Editando manualmente
Abra o arquivo em um editor de texto e resolva o conflito manualmente:
// Antes do conflito
<<<<<<< HEAD
const taxa = 0.15;
=======
const taxa = 0.18;
>>>>>>> feature-branch
// Depois da resolução (escolhendo a taxa mais recente)
const taxa = 0.18;
Remova todos os marcadores (<<<<<<<, =======, >>>>>>>) e mantenha apenas o código desejado.
5. Resolvendo conflitos complexos com ferramentas
Ferramentas visuais como Meld, KDiff3 e Beyond Compare facilitam a resolução de conflitos complexos:
$ git mergetool --tool=meld
A interface típica apresenta três painéis:
- Local (esquerda): sua versão atual
- Base (centro): o ancestral comum
- Remoto (direita): a versão a ser mergeada
- Resultado (inferior): o arquivo final a ser salvo
Dicas para manter a calma durante conflitos extensos:
- Resolva um conflito por vez: O Git processa os arquivos em ordem.
- Use
git diff --name-only --diff-filter=Upara listar apenas arquivos não resolvidos. - Faça pausas: Conflitos grandes podem ser mentalmente desgastantes.
6. Finalizando o merge com segurança
Após resolver todos os conflitos manualmente ou com ferramentas:
$ git add src/app.js
$ git commit
O Git abrirá o editor com uma mensagem de commit de merge pré-preenchida. Alternativamente:
$ git merge --continue
Para verificar a integridade do merge:
$ git log --graph --oneline --all
* 1a2b3c4 (HEAD -> main) Merge branch 'feature-branch'
|\
| * f5e6d7c Implementa nova funcionalidade
* | a1b2c3d Corrige bug no cálculo de taxa
|/
* 9a8b7c6 Versão base
7. Prevenindo conflitos futuros
Comunicação em equipe
Estabeleça canais de comunicação para informar quando alguém estiver trabalhando em áreas críticas do código. Use ferramentas como Slack, Discord ou comentários em tarefas do projeto.
Uso estratégico de rebase
O rebase pode manter um histórico mais linear e evitar conflitos acumulados:
$ git checkout feature-branch
$ git rebase main
Durante o rebase, os conflitos são resolvidos commit a commit, o que geralmente é mais gerenciável do que resolver todos de uma vez no merge.
Estratégias de branching eficientes
- Feature branches curtos: Branches que duram poucos dias geram menos conflitos.
- Integrações frequentes: Faça merge ou rebase da branch principal diariamente.
- Branches por funcionalidade: Cada branch deve ter um escopo bem definido.
Exemplo de workflow com integração frequente:
# Início do dia
$ git checkout main
$ git pull
$ git checkout feature-x
$ git rebase main
# Durante o desenvolvimento
$ git add .
$ git commit -m "Implementa parte da funcionalidade"
# Final do dia
$ git checkout main
$ git pull
$ git checkout feature-x
$ git rebase main # Resolve conflitos pequenos diariamente
Referências
- Documentação oficial do Git sobre merge — Guia completo sobre o comando
git merge, incluindo opções e flags para controle de conflitos. - Resolvendo conflitos de merge no Git - Atlassian — Tutorial prático da Atlassian com exemplos detalhados de resolução de conflitos.
- Ferramentas de merge externas - Git SCM — Configuração e uso de ferramentas como Meld, KDiff3 e Beyond Compare no Git.
- Estratégias avançadas de merge - Git Book — Capítulo do livro Pro Git sobre estratégias avançadas de merge e resolução de conflitos complexos.
- Prevenindo conflitos com boas práticas de branching - GitHub — Guia do GitHub sobre como evitar conflitos com fluxos de trabalho eficientes e comunicação em equipe.