Cloudflare Workers: o que é, como funciona e quando usar na edge
Cloudflare Workers executa código serverless na edge sem servidor para gerenciar. Entenda como funciona, quais casos de uso fazem sentido e o que avaliar antes de adotar.
Você deu um git push, o terminal cuspiu um bloco de texto vermelho com rejected, non-fast-forward e failed to push some refs, e agora você está olhando para a tela pensando se quebrou o repositório.
Calma. Na maioria das vezes isso significa uma coisa só: sua branch local e a branch remota divergiram. Cada uma tem commits que a outra não tem. O Git não sabe qual lado deve vencer, então ele para e pede que você decida.
O problema é que a mensagem de erro não explica isso de forma amigável, e não existe um único comando certo. O que você deve rodar depende de uma pergunta simples: qual lado tem o trabalho que vale a pena manter?
Neste guia você vai aprender a:
status, push e pull);Se você ainda está pegando o jeito do básico, vale passar antes pelo verbete o que é Git e voltar aqui.
Pense nas suas branches como linhas de commits. Entre a sua branch local main e a remota origin/main, existem quatro situações possíveis.
1. Em dia. As duas apontam para o mesmo commit. Não há nada a fazer.
a - b - c - d
^ LOCAL
^ REMOTO
2. Local atrasada. Alguém (ou você, em outra máquina) enviou commits que você ainda não baixou. A solução costuma ser um simples git pull.
a - b - c - d - e
^ LOCAL
^ REMOTO
3. Remoto atrasado. Você tem commits novos que ainda não subiram. A solução costuma ser um simples git push.
a - b - c - d - e
^ REMOTO
^ LOCAL
4. Divergentes. Os dois lados avançaram a partir de um ponto em comum, cada um para um canto. É aqui que o Git trava.
a - b - c - d - e (LOCAL)
\
f (REMOTO)
No desenho, d é o último commit que os dois compartilham. Depois dele, o local tem o e e o remoto tem o f. Nenhum dos dois é “ancestral” do outro, então o Git não consegue simplesmente avançar um ponteiro (o famoso fast-forward).
| Situação | O que fazer |
|---|---|
| Em dia | Nada |
| Local atrasada | git pull |
| Remoto atrasado | git push |
| Divergentes | Decidir qual lado manter (o resto deste guia) |
Os cenários mais comuns de quem trabalha sozinho ou em equipe pequena:
main e, enquanto isso, um colega também enviou algo para a main.git commit --amend ou um rebase em commits que já tinham sido enviados.pull antes de começar.Em projetos de site, o último caso é frequente. Um deploy automático que altera um arquivo de versão ou um package-lock.json já basta para fazer o remoto andar sozinho.
Existem três caminhos. O mais confiável é o primeiro.
git fetch + git statusO fetch atualiza o que o seu Git sabe sobre o remoto, sem mexer nos seus arquivos. Depois o status compara os dois lados.
git fetch
git status
Se estiverem divergentes, você verá algo assim:
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 2 different commits each, respectively.
(use "git pull" to merge the remote branch into yours)
Traduzindo: sua branch tem 1 commit que o remoto não tem, e o remoto tem 2 commits que você não tem.
git push$ git push
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:usuario/projeto'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
Cuidado: esse erro nem sempre significa divergência. Ele também aparece quando sua branch só está atrasada. Na dúvida, rode git fetch e git status para confirmar.
git pull$ git pull
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
fatal: Need to specify how to reconcile divergent branches.
Esse é o mais claro dos três: ele diz literalmente que você tem branches divergentes. Só que ele depende da sua configuração. Se você já definiu pull.rebase, o Git não pergunta nada e faz a escolha por você:
| Configuração | O que o git pull faz sozinho |
|---|---|
pull.rebase false | Inicia um merge com o remoto |
pull.rebase true | Faz rebase da sua branch sobre o remoto |
pull.ff only | Aborta com Not possible to fast-forward |
Antes de rodar qualquer comando que reescreve histórico, olhe o que cada lado tem. Dois comandos resolvem.
Para ver um grafo compacto dos dois lados:
git log --oneline --graph --decorate main origin/main
Para listar só os commits que existem de um lado e não do outro:
# commits que só você tem (ainda não foram enviados)
git log --oneline origin/main..main
# commits que só o remoto tem (você ainda não baixou)
git log --oneline main..origin/main
Dois minutos olhando isso evitam a maioria dos desastres. Se a lista do remoto tiver commits de outra pessoa, você já sabe que --force está fora de questão.
Em vez de decorar comandos, responda esta pergunta: qual lado tem trabalho útil?
| Cenário | Comando |
|---|---|
| Quero manter os dois lados | git pull --rebase (ou --no-rebase) |
| O trabalho remoto é inútil | git push --force-with-lease |
| O meu trabalho local é inútil | git reset --hard origin/main |
Vamos ver cada um com calma.
git pull --rebaseO rebase pega os seus commits locais, “levanta” eles do ponto onde divergiram e os reaplica por cima do que veio do remoto. O histórico fica linear, como se você tivesse começado seu trabalho depois do dos outros.
git pull --rebase
Antes e depois:
Antes: Depois:
a - b - c - d - e a - b - c - d - f - e'
\ ^ REMOTO ^ LOCAL
f
Repare que o e virou e'. Ele tem o mesmo conteúdo, mas é um commit novo, com outro identificador. Por isso o rebase é seguro apenas para commits que ainda não foram compartilhados.
Se houver conflito, o Git para no meio e pede que você resolva. O fluxo é:
# 1. abra os arquivos em conflito, resolva e salve
# 2. marque como resolvido
git add arquivo-resolvido.php
# 3. continue o rebase
git rebase --continue
# se quiser desistir e voltar ao estado anterior
git rebase --abort
Ótimo para quando você trabalha direto na main de um projeto pessoal ou em uma branch só sua.
git pull --no-rebase (merge)O merge cria um commit extra que une as duas linhas, preservando o histórico exatamente como aconteceu.
git pull --no-rebase
O Git abre o editor para você confirmar a mensagem do commit de merge. Salve e feche. O resultado:
a - b - c - d - e - M
\ /
f ------
O histórico fica mais “ramificado”, mas nenhum commit é reescrito. Muitas equipes preferem isso justamente por ser mais seguro e fiel ao que aconteceu.
| Rebase | Merge |
|---|---|
| Histórico linear e limpo | Histórico fiel, com commit de união |
| Reescreve seus commits locais | Não reescreve nada |
Qual escolher é decisão de fluxo de trabalho do time. Há bons argumentos dos dois lados.
Às vezes o trabalho que está no remoto é lixo e você quer que ele fique igual ao seu local. O caso clássico, em repositório pessoal:
git push.git commit --amend.Aqui você precisa forçar o envio.
git push --force-with-lease (prefira este)git push --force-with-lease
Ele só sobrescreve o remoto se ninguém tiver mexido nele desde a última vez que você o buscou. Se alguém enviou algo no meio tempo, o push é recusado e você não apaga o trabalho alheio sem querer.
Um detalhe que pouca gente sabe: se seu editor faz git fetch automático em segundo plano (o VS Code faz isso, por exemplo), o Git passa a achar que você “já viu” os commits novos e o --force-with-lease perde a proteção. Para fechar essa brecha existe a opção extra:
git push --force-with-lease --force-if-includes
Ela confere no reflog se você realmente integrou o que veio do remoto antes de forçar.
git push --force (use com muito cuidado)git push --force
Sobrescreve o remoto sem perguntar nada. Em um repositório só seu, tudo bem. Em repositório compartilhado, pode apagar o trabalho de outras pessoas e causar um belo dia ruim para o time.
Em projetos com mais de uma pessoa, ative a proteção de branch no GitHub ou GitLab para a main. Assim o push forçado fica impossível, mesmo por engano.
O inverso: o que está no remoto é o que vale, e o que você fez localmente deve ser jogado fora. Como não existe git pull --force, usamos o reset.
Um caso muito comum: você percebeu que commitou na main algo que deveria estar em outra branch.
git checkout main
# 1. guarde o seu trabalho em uma branch nova, para não perder nada
git checkout -b minha-nova-branch
# 2. volte para a main
git checkout main
# 3. atualize o que o Git sabe sobre o remoto
git fetch
# 4. confira se não há nada sem commitar (o reset vai apagar isso)
git status
# 5. force a main local a ficar igual à remota
git reset --hard origin/main
Note o passo 1: em vez de jogar o trabalho fora, você o guarda numa branch. Assim você pode decidir com calma depois o que fazer com ele. Essa estratégia também ajuda quando você não sabe ainda como resolver o conflito, porque é bem mais fácil mesclar duas branches locais do que lidar com uma remota.
Atenção ao --hard. Ele apaga alterações não commitadas de forma definitiva. Sempre rode git status antes.
Se quiser alternativas ao reset --hard, a partir de outra branch você pode usar:
git branch -f main origin/main
# ou
git fetch origin main:main --force
| Situação | Comando |
|---|---|
| Desfazer um rebase em andamento | git rebase --abort |
| Desfazer um merge em andamento | git merge --abort |
E se você fez um reset ou rebase errado e quer voltar? O git reflog guarda o histórico de onde sua branch esteve, mesmo para commits “perdidos”:
git reflog
git reset --hard HEAD@{2} # volta para o estado de dois movimentos atrás
Por isso, na maior parte das vezes, “perdi meu commit” não é verdade. Ele só está fora do alcance da branch.
Se você já sabe qual estratégia prefere, pode definir o padrão e nunca mais ver aquele aviso:
# sempre fazer rebase ao dar pull
git config --global pull.rebase true
# ou sempre fazer merge
git config --global pull.rebase false
# ou nunca criar merge automático (só aceita fast-forward)
git config --global pull.ff only
Minha opinião honesta: o pull.ff only é o mais seguro para quem está começando. Ele recusa qualquer divergência e te obriga a olhar o problema, em vez de decidir por você. Para quem já tem domínio e trabalha em branches pessoais, pull.rebase true economiza tempo.
Resolver é bom, mas não ter o problema é melhor:
git pull antes de começar a trabalhar e antes de dar push.main. Divergência em branch que só você usa é fácil de resolver.main exigindo pull request e bloqueando force push.Se você mantém um site com deploy via Git (Astro, Next.js, WordPress com tema versionado), divergir na branch errada pode ter consequência real: um --force na main que dispara um deploy com código quebrado, ou um reset que apaga uma correção que já estava em produção.
Algumas boas práticas que uso nos projetos:
Significa que a branch local e a remota têm commits diferentes a partir de um ponto em comum. Cada uma avançou por um caminho, e o Git não consegue juntar as duas sozinho sem você escolher entre merge, rebase ou sobrescrever um dos lados.
É o erro do git push quando o remoto tem commits que você não tem. O Git recusa o envio para não apagar esse trabalho. Pode ser divergência de verdade ou apenas sua branch atrasada, por isso vale rodar git fetch e git status.
Depende do seu fluxo. Rebase deixa o histórico linear, mas reescreve seus commits locais. Merge preserva tudo como aconteceu, ao custo de um commit extra. Em branch compartilhada, o merge é mais seguro. Em branch pessoal, o rebase costuma ser mais limpo.
git push --force?Só em repositório onde você é a única pessoa que envia código. Em repositórios compartilhados, prefira --force-with-lease e ative a proteção de branch na main.
Quase sempre sim. Rode git reflog, ache o estado anterior e volte com git reset --hard HEAD@{n} ou crie uma branch a partir dele. Já alterações que nunca foram commitadas e foram apagadas pelo reset --hard não voltam.
git fetch e git pull?O fetch só baixa as informações do remoto, sem alterar seus arquivos. O pull é um fetch seguido de uma integração (merge ou rebase). Quando há dúvida, comece pelo fetch: ele é inofensivo.
Com git config --global pull.rebase true. Se preferir merge, use false. Para aceitar apenas fast-forward e ser avisado de qualquer divergência, use git config --global pull.ff only.
git fetch e git status para confirmar a divergência.git log --oneline --graph --decorate main origin/main para ver quem tem o quê.git pull --rebase ou git pull --no-rebase.git push --force-with-lease.git reset --hard origin/main.git reflog te salva.Branches divergentes assustam porque a mensagem de erro é técnica, mas o raciocínio é simples: dois lados avançaram e você precisa dizer ao Git qual vale. Com o status você confirma, com o log você enxerga, e com a pergunta “qual lado tem o trabalho útil?” você escolhe o comando certo.
Se o seu projeto vive de deploy por Git e você já perdeu horas (ou um fim de semana) com conflito, branch quebrada ou produção fora do ar por causa de um push errado, isso é sintoma de fluxo mal estruturado, não de azar. Posso ajudar a organizar isso: desenvolvimento freelancer para estruturar repositório, branches e deploy, e manutenção de sites WordPress para manter tema e plugins versionados e atualizados com segurança.