Pular para o conteúdo
~/buildbase $ 06/11/2026
Desenvolvimento

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

Vista aérea do encontro de dois rios

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
  1. Merge junta históricos, não só arquivos
  2. Fast-forward, merge commit, squash e rebase: o que muda
  3. Sintoma: “CONFLICT (content): Merge conflict in arquivo.js”
  4. Sintoma: o conflito aparece em dezenas de arquivos de uma vez
  5. Sintoma: conflito em package-lock.json ou outro arquivo gerado
  6. Sintoma: o merge terminou, mas o sistema quebrou
  7. Sintoma: “Your local changes would be overwritten by merge”
  8. 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 add e conclua com git commit. Se travar, git merge --abort volta 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.

Vista aérea do encontro de dois rios que seguem como um só
Dois cursos que viram um: a imagem mais fiel de um merge. Foto: Carsten Steger / Wikimedia Commons (CC BY-SA 4.0)

 

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.

TipoComando típicoResultado no histórico
Fast-forwardgit merge minha-branch (quando possível)Linha reta, nenhum commit extra
Merge commitgit merge --no-ff minha-branchCommit de junção com dois pais, preserva a branch visível
Squashgit merge --squash minha-branchTodas as mudanças viram um único commit novo
Rebasegit 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-cliente

Tudo 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:

  1. Abra o arquivo e decida o resultado final. Pode ser um lado, o outro ou uma combinação dos dois.
  2. Apague os três marcadores. Sobrar um ======= perdido quebra o código.
  3. Rode git add arquivo.js para dizer ao Git que aquele conflito está resolvido.
  4. 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.json ou --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 add no 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.

Rodovia com faixas de tráfego que se juntam em uma pista
Faixas que se juntam sem sinal de alerta ainda podem dar problema mais à frente. Foto: Doug Kerr / Wikimedia Commons (CC BY-SA 2.0)

 

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.