Git push rejected non-fast-forward: como integrar o remoto sem perder commits
Publicado em 2026-08-29T20:14:00Z · atualizado em 2026-08-29T19:14:27+00:00
O erro **rejected non-fast-forward** aparece quando o Git se recusa a mover uma branch remota para o seu commit porque esse movimento descartaria commits que já existem no servidor. Em geral, outra pessoa enviou mudanças depois do seu últim
Git push rejected non-fast-forward: como integrar o remoto sem perder commits
O erro rejected non-fast-forward aparece quando o Git se recusa a mover uma branch remota para o seu commit porque esse movimento descartaria commits que já existem no servidor. Em geral, outra pessoa enviou mudanças depois do seu último fetch, ou a branch remota foi atualizada por automação. A rejeição é uma proteção, não corrupção do repositório.
A saída segura é preservar o estado local, atualizar a visão do remoto, comparar os históricos e escolher conscientemente entre rebase e merge. git push --force não deve ser o primeiro reflexo: ele pode remover trabalho compartilhado. Este guia usa um laboratório local com dois clones, mostra como recuperar conflitos e explica quando --force-with-lease é apropriado.
Legenda: o push comum só avança a referência remota quando o commit atual contém o histórico já publicado.
Leia a rejeição completa
Uma mensagem típica é:
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs
hint: Updates were rejected because the tip of your current branch is behind
Confirme qual origem, branch local e branch remota estão envolvidas:
git status -sb
git remote -v
git branch -vv
git rev-parse --show-toplevel
Não execute comandos de integração antes de verificar se está no repositório certo. Projetos com vários remotos podem usar origin para um fork e upstream para o repositório principal. A mensagem também pode ser causada por proteção de branch ou hook; nesses casos, o servidor costuma apresentar uma explicação adicional.
O que fast-forward significa
Imagine que o remoto aponta para B e seu commit C é descendente direto:
A---B---C
remoto local
Mover o remoto de B para C preserva todos os commits e é fast-forward. Agora considere histórias divergentes:
C local
/
A---B
\
D remoto
Atualizar o remoto diretamente para C esconderia D. Por padrão, o Git rejeita. Você precisa construir um histórico que contenha ambos, por merge ou rebase, ou deliberadamente substituir a história remota quando a política permite.
Crie um laboratório com dois clones
O experimento abaixo usa apenas pastas descartáveis e um repositório bare:
mkdir laboratorio-git && cd laboratorio-git
git init --bare remoto.git
git clone remoto.git ana
git clone remoto.git bruno
No primeiro clone:
cd ana
git switch -c main
printf "linha inicial\n" > notas.txt
git add notas.txt
git commit -m "Adiciona notas"
git push -u origin main
No segundo clone, atualize a referência e crie outra mudança:
cd ../bruno
git fetch origin
git switch -c main --track origin/main
printf "mudança remota\n" >> notas.txt
git commit -am "Atualiza pelo clone Bruno"
git push
Volte ao primeiro clone, crie um commit sem buscar e tente enviar:
cd ../ana
printf "mudança local\n" >> local.txt
git add local.txt
git commit -m "Adiciona arquivo local"
git push
A rejeição agora é reproduzível e nenhuma conta externa foi usada.
Faça um ponto de segurança antes de integrar
Se há arquivos não commitados, o rebase ou merge pode ser interrompido. Veja o estado:
git status
git diff
git diff --staged
O melhor ponto de segurança é um commit coerente na sua branch. Se o trabalho ainda não pode virar commit, use uma branch temporária ou stash identificado:
git switch -c seguranca/antes-da-integracao
git add -A
git commit -m "WIP: preserva estado antes da integração"
Depois volte à branch original, se necessário. Branches e commits são mais fáceis de inspecionar que um stash anônimo. Não inclua segredos só para criar o ponto de segurança; remova-os do índice e trate a exposição se já foram commitados.
Atualize o remoto com git fetch
git fetch origin baixa objetos e atualiza referências de rastreamento como origin/main, sem alterar seus arquivos de trabalho:
git fetch origin
git status -sb
git log --oneline --graph --decorate --all -n 20
Compare o que existe apenas em cada lado:
git log --oneline HEAD..origin/main
git log --oneline origin/main..HEAD
git diff --stat HEAD...origin/main
O primeiro comando lista commits remotos ausentes localmente; o segundo lista commits locais não publicados. O operador de três pontos no diff compara a partir do ancestral comum, útil para entender a divergência. A ferramenta de comparação de texto pode ajudar com trechos pequenos, mas não substitui o grafo do Git.
Opção 1: rebase para uma história linear
O rebase reaplica seus commits locais sobre o topo remoto:
git rebase origin/main
O resultado conceitual é:
A---B---D---C'
C' contém a mesma intenção de C, mas é um novo commit com outro identificador. Se os commits locais ainda não foram compartilhados, isso costuma produzir um histórico simples. Depois:
git push origin main
Não faça rebase de commits que outras pessoas já usam sem combinar a mudança. Reescrever história publicada obriga os demais clones a reconciliar referências diferentes.
Resolva conflitos durante o rebase
Quando as duas linhas alteram a mesma região, o Git pausa:
git status
git diff --name-only --diff-filter=U
Abra cada arquivo, entenda as duas intenções e remova os marcadores <<<<<<<, ======= e >>>>>>>. Depois teste e continue:
git add caminho/do/arquivo
git rebase --continue
Se perceber que escolheu a estratégia errada:
git rebase --abort
Não use git checkout --ours ou --theirs automaticamente sem entender que, durante rebase, os nomes podem parecer invertidos em relação à intuição. Revise o conteúdo final, execute testes e veja git diff --check para detectar marcadores ou espaços problemáticos.
Opção 2: merge para preservar as linhas de trabalho
O merge cria um commit que conecta os dois históricos:
git merge origin/main
Se houver conflito, edite os arquivos, adicione as resoluções e finalize:
git add -A
git commit
git push origin main
O histórico registra explicitamente que dois ramos existiram. Isso pode ser desejável em branches compartilhadas ou quando a equipe evita reescrever commits locais. Se quiser cancelar antes do commit:
git merge --abort
Merge não é inferior a rebase; representa outra política de histórico. O importante é que o novo topo seja descendente do topo remoto e preserve ambas as mudanças.
Escolha entre pull --rebase e pull --no-rebase
git pull combina fetch com uma integração. Para diagnóstico, separar as etapas deixa o processo mais claro. Depois que a equipe define uma política, você pode usar:
git pull --rebase origin main
ou:
git pull --no-rebase origin main
git pull --ff-only aceita somente atualização sem divergência e falha quando um merge ou rebase seria necessário. É uma opção conservadora para evitar integração implícita. Configure a preferência no repositório, não globalmente por acidente:
git config pull.rebase true
Documente a escolha no guia de contribuição. Misturar políticas sem acordo gera ruído e conflitos desnecessários.
Quando force-with-lease é aceitável
Após um rebase de uma branch que você publicou intencionalmente, o push comum pode ser rejeitado porque os identificadores mudaram. Se a política permite reescrever essa branch e você verificou que ninguém publicou algo novo, prefira:
git push --force-with-lease origin minha-branch
O lease compara a referência remota com a expectativa local e recusa a substituição se ela mudou desde seu último conhecimento. Ainda assim, faça git fetch e leia o grafo imediatamente antes. Não use em main ou branch compartilhada sem autorização. --force-with-lease reduz risco; não torna a reescrita inofensiva.
O --force puro ignora essa proteção. Reservá-lo a manutenção controlada evita apagar commits de terceiros.
Legenda: integrar é o padrão; reescrever só é opção em branch autorizada e após verificar o estado remoto.
Branch protegida e pull request
Alguns servidores recusam push direto mesmo quando seria fast-forward. A mensagem pode mencionar protected branch, required checks ou permission denied. Nesse fluxo, envie sua branch:
git switch -c correcao/minha-alteracao
git push -u origin correcao/minha-alteracao
Abra um pull request e aguarde revisão e verificações. Não tente contornar a proteção mudando o remoto ou usando credencial de outra pessoa. A restrição existe para manter testes, auditoria e aprovação.
Se o nome da branch padrão mudou de master para main, confira git remote show origin e atualize a referência. Um push para a branch errada pode parecer problema de sincronização quando é apenas destino incorreto.
E se o remoto foi reescrito por outra pessoa
Depois de fetch, você pode perceber que origin/main não é descendente da referência anterior. Antes de fazer qualquer coisa, capture o grafo e comunique a equipe. Use o reflog para localizar sua visão anterior:
git reflog show origin/main -n 10
git log --graph --oneline --decorate --all -n 30
Não force a branch de volta por conta própria. A reescrita pode ter removido um segredo, revertido uma publicação indevida ou aplicado uma correção coordenada. Crie uma branch apontando para commits que precisa preservar:
git branch recuperacao/meus-commits <hash>
Depois, replique os commits necessários com rebase ou cherry-pick conforme decisão da equipe.
Recupere um commit que parece ter sumido
Commits locais normalmente continuam no banco de objetos por algum tempo, mesmo após reset ou rebase. O reflog registra movimentos das referências locais:
git reflog --date=local
git show <hash>
git branch recuperacao/commit <hash>
Crie a branch antes de continuar experimentos. A ferramenta de hash ajuda a explicar identificadores, mas não acessa objetos do seu repositório. O comando git show é a fonte correta para conteúdo e metadados do commit.
Evite executar limpeza agressiva enquanto procura o trabalho. Não apague a pasta .git; ela contém o histórico e o reflog usados na recuperação.
Automatize uma verificação antes do push
Em CI ou script local, atualize a referência e teste ancestralidade:
git fetch origin main
if git merge-base --is-ancestor origin/main HEAD; then
echo "push pode avançar origin/main"
else
echo "histórico divergiu; integre antes de enviar"
exit 1
fi
O teste não resolve conflitos nem decide a política, mas transforma uma surpresa tardia em diagnóstico claro. Hooks locais não devem ser a única proteção porque podem ser ignorados; combine com regras no servidor.
Para equipes, também vale exibir branch, ahead e behind no prompt ou IDE. A informação reduz pushes feitos a partir de um estado antigo.
Erros comuns que pioram a situação
Evite estas respostas automáticas:
- executar
git push --forcesem buscar o remoto; - usar
git reset --hard origin/maine apagar mudanças locais sem backup; - fazer rebase de uma branch compartilhada sem avisar;
- resolver todos os conflitos escolhendo sempre um lado;
- apagar
.gite clonar novamente antes de preservar commits locais; - misturar
origin/main,upstream/maine a branch local.
Se um tutorial sugere comando destrutivo, primeiro entenda quais referências serão movidas. Use git status, git log --graph e uma branch de segurança. Para praticar comandos, a biblioteca Git do IATechNerds pode complementar este laboratório.
Checklist antes do novo push
Confirme que:
- O trabalho local está commitado ou preservado em branch.
git fetchfoi executado no remoto correto.- O grafo mostra claramente os commits exclusivos de cada lado.
- Rebase ou merge segue a política da equipe.
- Conflitos foram resolvidos por intenção, não por escolha automática.
- Testes e
git diff --checkpassaram. --force-with-leasesó será usado em branch autorizada.- O novo topo contém tudo que deve permanecer publicado.
Após o push, confira a saída e a interface do servidor. O objetivo não é apenas remover a mensagem de erro, mas preservar a história correta.
Em uma revisão final, abra o conjunto de commits que será incorporado e confirme autoria, mensagens e arquivos modificados. Um histórico tecnicamente integrável ainda pode conter artefatos locais, credenciais, arquivos gerados ou alterações fora do escopo. Compare a branch contra a base com git diff origin/main...HEAD e use a suíte do projeto. Se o push continuar rejeitado após a integração, leia a nova mensagem: a referência pode ter avançado novamente enquanto você trabalhava. Nesse caso, faça outro fetch e repita a comparação, sem recorrer imediatamente a força. Em equipes movimentadas, branches curtas e pull requests pequenos diminuem a janela de divergência. A rejeição deixa de ser um obstáculo misterioso e passa a ser um sinal de sincronização que protege o repositório.
Documentação primária
Autoria, revisão e método de produção
Autoria editorial: Equipe IATechNerds. Revisão técnica e editorial: pendente, obrigatória antes da publicação. O texto foi produzido com apoio de IA generativa, consulta à documentação oficial do Git e laboratório fictício reproduzível com repositório bare e dois clones. Nenhum comando foi executado em repositório do leitor. A política de branches, exemplos e links internos precisam de validação humana antes da publicação.
Camada extra: como tomar uma decisão melhor neste cenário
Em uso real, Git push rejected non-fast-forward: como integrar o remoto sem perder commits costuma envolver mais de uma camada. O ganho de qualidade aparece quando cada camada tem uma pergunta de verificação antes de qualquer mudança definitiva.
O erro **rejected non-fast-forward** aparece quando o Git se recusa a mover uma branch remota para o seu commit porque esse movimento descartaria commits que já existem no servidor. Em geral, outra pessoa enviou mudanças depois do seu últim A ideia central pode ser testada com quatro perguntas: o que foi observado, qual hipótese explica o sintoma, qual mudança é reversível e qual evidência prova que funcionou. Esse encadeamento evita que uma coincidência seja confundida com causa e torna o procedimento repetível por outra pessoa.
Qualidade de IA precisa de um teste, não de uma impressão
Para Git, non-fast-forward, rebase, merge, escolha três tarefas representativas: uma simples, uma ambígua e uma em que a resposta possa ser conferida em fonte primária. Compare utilidade, rastreabilidade, privacidade, velocidade e quantidade de correção humana necessária. Uma resposta elegante não compensa uma citação inexistente ou uma conclusão que não pode ser reproduzida.
Separe ainda o que pode sair do dispositivo do que deve permanecer local. Dados públicos toleram fluxos diferentes de documentos internos, contratos, informações pessoais ou bases de clientes. A escolha entre processamento local e nuvem deve ser feita pelo risco do conteúdo, não apenas pela conveniência da interface.
Grade de avaliação prática
| Critério | Pergunta | Evidência |
|---|---|---|
| Correção | A resposta bate com a fonte? | Checagem independente |
| Rastreabilidade | É possível localizar de onde veio? | Citação ou trecho verificável |
| Privacidade | Que dado sai do dispositivo? | Política e configuração |
| Esforço | Quanto trabalho humano resta? | Tempo de revisão |
O ponto em que a IA deixa de ajudar
Se a tarefa exige precisão absoluta e a saída não pode ser validada, a automação deve ser limitada a apoio: localizar trechos, organizar opções ou preparar uma primeira versão. A decisão final precisa permanecer com alguém capaz de verificar a fonte e assumir a responsabilidade pelo resultado.
Checklist de fechamento
- Registre o estado inicial antes de alterar qualquer coisa.
- Faça uma mudança por rodada e anote o efeito.
- Teste um caso normal, um caso-limite e o cenário que originalmente falhou.
- Confirme que a solução continua válida depois de reiniciar, reabrir ou repetir o fluxo.
- Guarde uma forma de voltar ao estado anterior quando a alteração for destrutiva.
Esse método acrescenta profundidade sem transformar o artigo em teoria abstrata: o leitor entende por que cada passo existe e como reconhecer quando a situação exige uma decisão diferente.
