O que é merge no Git e como resolver conflitos sem medo

Conflito de merge não é sinal de que alguém fez besteira. É sinal de que o Git foi honesto. Quando duas pessoas mudam a mesma linha de jeitos diferentes, ele se recusa a adivinhar qual versão vale e devolve a decisão para você. A alternativa, uma ferramenta que escolhesse sozinha, seria muito mais perigosa.
$ ls ./neste-artigo
- Merge junta históricos, não só arquivos
- Fast-forward, merge commit, squash e rebase: o que muda
- Sintoma: “CONFLICT (content): Merge conflict in arquivo.js”
- Sintoma: o conflito aparece em dezenas de arquivos de uma vez
- Sintoma: conflito em package-lock.json ou outro arquivo gerado
- Sintoma: o merge terminou, mas o sistema quebrou
- Sintoma: “Your local changes would be overwritten by merge”
- Merge sem medo começa com uma cópia de segurança de dois segundos
O medo vem de outro lugar: a tela cheia de <<<<<<< e >>>>>>>, a sensação de que um comando errado apaga o trabalho da semana. Entender o que o merge faz por dentro tira quase todo esse medo, porque mostra que dá para voltar atrás em praticamente qualquer ponto.
Resumo de bolso: merge é a operação que junta o histórico de duas branches em uma. Na maioria das vezes o Git faz isso sozinho. Quando a mesma parte de um arquivo mudou nos dois lados, ele para, marca o trecho e espera que você escolha o resultado, salve, rode
git adde conclua comgit commit. Se travar,git merge --abortvolta tudo ao estado anterior.
Merge junta históricos, não só arquivos
Imagine a branch main e uma branch cadastro-cliente criada a partir dela na segunda-feira. Até quinta, você fez três commits na sua branch e outra pessoa fez dois na main. Quando você roda git merge cadastro-cliente estando na main, o Git procura o ponto onde as duas linhas se separaram (o ancestral comum) e compara o que cada lado mudou desde ali.
Esse é o chamado three-way merge: três versões entram na conta, o ancestral, a sua e a outra. Se um lado mexeu no arquivo A e o outro no arquivo B, não há disputa. Se os dois mexeram no mesmo arquivo, mas em trechos distantes, também não. O resultado é um novo commit de merge, com dois pais, que registra o encontro das linhas.

Fast-forward, merge commit, squash e rebase: o que muda
Nem todo merge cria um commit novo. Se a main não recebeu nada desde que você criou sua branch, o Git só avança o ponteiro da main até o seu último commit. Isso é o fast-forward: histórico reto, sem commit de junção.
| Tipo | Comando típico | Resultado no histórico |
|---|---|---|
| Fast-forward | git merge minha-branch (quando possível) | Linha reta, nenhum commit extra |
| Merge commit | git merge --no-ff minha-branch | Commit de junção com dois pais, preserva a branch visível |
| Squash | git merge --squash minha-branch | Todas as mudanças viram um único commit novo |
| Rebase | git rebase main (na sua branch) | Seus commits são reescritos por cima da main, em linha |
A documentação oficial do git merge detalha cada opção. Para quem está começando, a regra prática é: use o que o time já usa. Misturar estilos num mesmo repositório é o que deixa o histórico ilegível.
Um aviso sobre rebase: como ele reescreve commits, nunca faça rebase de uma branch que outras pessoas já baixaram. Você vai criar uma versão paralela da história e o próximo git pull delas vira uma confusão.
Sintoma: “CONFLICT (content): Merge conflict in arquivo.js”
Essa é a mensagem clássica. A causa: o mesmo trecho do arquivo mudou nos dois lados desde o ancestral comum. O Git mesclou tudo o que conseguiu e deixou o arquivo com marcadores assim:
<<<<<<< HEAD
const taxa = 0.05;
=======
const taxa = calcularTaxa(estado);
>>>>>>> cadastro-clienteTudo entre <<<<<<< HEAD e ======= é a versão da branch onde você está. O que vem depois, até >>>>>>>, é a versão que está chegando. A solução tem quatro passos:
- Abra o arquivo e decida o resultado final. Pode ser um lado, o outro ou uma combinação dos dois.
- Apague os três marcadores. Sobrar um
=======perdido quebra o código. - Rode
git add arquivo.jspara dizer ao Git que aquele conflito está resolvido. - Quando todos os arquivos estiverem resolvidos, finalize com
git commit.
O detalhe que quase ninguém conta: resolver o conflito não é escolher uma das versões. No exemplo acima, a pergunta certa é “a outra pessoa trocou a taxa fixa por um cálculo; o meu código ainda funciona com isso?”. Muitas vezes a resposta correta mistura os dois lados, e só rodando os testes depois você descobre.
Sintoma: o conflito aparece em dezenas de arquivos de uma vez
Causa mais comum: alguém reformatou o projeto inteiro (trocou aspas, indentação, ordem de imports) enquanto você trabalhava. Cada linha mudou, então cada linha conflita.
Solução: aborte com git merge --abort, atualize sua branch com a principal antes de continuar e, se o projeto tem formatador automático (Prettier, Black, gofmt), rode-o na sua branch para que as duas versões fiquem com a mesma formatação. O número de conflitos costuma cair de dezenas para um ou dois.
Prevenção: integre a principal na sua branch com frequência. Uma branch que fica três semanas isolada acumula diferenças demais.
Sintoma: conflito em package-lock.json ou outro arquivo gerado
Arquivos gerados por ferramenta (lockfiles do npm, yarn ou poetry) mudam em blocos enormes e são quase impossíveis de mesclar à mão. Não tente.
- Aceite a versão da branch principal:
git checkout --theirs package-lock.jsonou--ours, conforme o lado. - Rode o instalador (
npm install) para que o arquivo seja regenerado com as dependências dos dois lados. - Faça
git addno arquivo regenerado e siga o merge.
Atenção ao sentido de ours e theirs: no merge, “ours” é a branch onde você está. No rebase, os papéis se invertem, o que já confundiu muito dev experiente.
Sintoma: o merge terminou, mas o sistema quebrou
Esse é o mais traiçoeiro, porque o Git não reclamou de nada. A causa é o chamado conflito semântico: você renomeou uma função na sua branch e a outra pessoa, na dela, criou uma chamada nova usando o nome antigo. Textualmente não há sobreposição, logicamente o código não roda.

Solução: rodar a suíte de testes depois de todo merge, mesmo os que não deram conflito. É por isso que times usam integração contínua no pull request. Se você ainda não tem testes, pelo menos suba a aplicação e clique nas telas que a outra branch tocou.
Para desfazer um merge já commitado e ainda não enviado, git reset --hard ORIG_HEAD volta ao estado anterior. Se já foi enviado, prefira git revert -m 1 <hash-do-merge>, que cria um commit desfazendo a junção sem reescrever o histórico de ninguém.
Sintoma: “Your local changes would be overwritten by merge”
Aqui não há conflito ainda. O Git está avisando que você tem alterações não commitadas em arquivos que o merge precisa mexer, e ele não vai sobrescrever o seu trabalho sem permissão.
Duas saídas: faça commit do que está pronto ou guarde temporariamente com git stash, rode o merge e depois recupere com git stash pop. A segunda opção pode gerar conflito na volta, que se resolve exatamente como os anteriores.
Se você está montando sua base de Git do zero, a trilha de estudo gratuita que vai do Git ao primeiro emprego organiza a ordem dos assuntos.
Merge sem medo começa com uma cópia de segurança de dois segundos
Antes de qualquer merge que pareça arriscado, crie uma branch de backup: git branch backup-antes-do-merge. Ela não custa nada, guarda o ponto exato onde você estava e pode ser apagada depois. Se tudo der errado, git reset --hard backup-antes-do-merge te leva de volta. A partir de agora, o erro a evitar é o oposto do medo: concluir um merge com conflito sem rodar os testes. Para revisar termos como rebase, HEAD e commit, consulte o glossário de programação, e para conflitos resolvidos direto no navegador, o guia do GitHub para resolver conflitos pela linha de comando mostra variações do mesmo processo.




