Git non-fast-forward rejeitado

88

Acho que essa pergunta foi feita muitas vezes, mas a solução normalmente é "Excluí o diretório e refiz meu trabalho com uma nova verificação." Fiz um commit e push, mas percebi que me referi ao número do ticket errado na mensagem de commit. Procurei uma solução rápida no SO e acabei digitando o seguinte no terminal:

$ git reset --soft HEAD^
$ git commit -m "... correct message ..."

O único problema é que estou recebendo a seguinte mensagem de erro:

To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

Estou usando o modelo git-flow e trabalhando no branch de desenvolvimento. Como posso mesclar as coisas de volta para deixar o git feliz novamente?

rynmrtn
fonte

Respostas:

52

Força git push:

git push origin +develop
Alan Haggai Alavi
fonte
24
Essa é uma solução, mas leia o comentário de Brian Campbell para entender o que está fazendo antes de usá-lo.
thelem de
5
git push origin + master
Aniket Thakur
Veja também a nota sobre receive.denyNonFastForwardsna resposta de Brian Campbell : +ou --forcepode não ser suficientemente forte, dependendo de como eles - quem quer que sejam - configuraram seu repositório Git.
Torek em
174

Se você empurrar um commit para o servidor, e depois reescrever que cometem localmente (com git reset, git rebase, git filter-branchou qualquer outra manipulação história) e, em seguida, empurrou o reescrito cometer volta para o servidor, você vai estragar qualquer outra pessoa que tinha puxado. Aqui está um exemplo; digamos que você tenha confirmado A e enviado para o servidor.

- * - * - A <- mestre

- * - * - A <- origem / mestre

Agora você decide reescrever A, da maneira que você mencionou, redefinindo e confirmando. Observe que isso deixa um commit pendente, A, que eventualmente será coletado como lixo, pois não está acessível.

-*-*-UMA
    \
     A '<- mestre

- * - * - A <- origem / mestre

Se outra pessoa, digamos Fred, puxar para baixo masterdo servidor enquanto você estiver fazendo isso, eles terão uma referência a A, a partir da qual eles podem começar a trabalhar:

- * - * - A '<- mestre

- * - * - A <- origem / mestre

- * - * - AB <- fred / master

Agora, se você pudesse empurrar seu A 'para origin / master, o que criaria um avanço não rápido, ele não teria A em seu histórico. Então, se Fred tentasse puxar novamente, ele de repente teria que fundir e reintroduziria o commit A:

- * - * - A '<- mestre

- * - * - A <- origem / mestre

- * - * - AB- \ 
    \ * <- fred / master
     UMA'--/

Se Fred perceber isso, ele poderia fazer um rebase, o que evitaria que o commit A reaparecesse. Mas ele teria que perceber isso e se lembrar de fazer isso; e se você tiver mais de uma pessoa que puxou A para baixo, todos eles teriam que rebase para evitar o commit extra de A na árvore.

Portanto, geralmente não é uma boa ideia alterar o histórico em um repo que outras pessoas usam. Se, no entanto, você souber que ninguém mais está puxando desse repo (por exemplo, é o seu próprio repo privado ou você só tem um outro desenvolvedor trabalhando no projeto com quem você pode coordenar facilmente), então você pode forçar atualizar executando:

git push -f

ou

git push origin +master

Ambos irão ignorar a verificação de um push não acelerado e atualizar o que está no servidor para sua nova revisão A ', abandonando a revisão A para que eventualmente seja coletada como lixo.

É possível que os push push sejam totalmente desativados com a receive.denyNonFastForwardsopção config. Esta opção é habilitada por padrão em repositórios compartilhados. Nesse caso, se você realmente quiser forçar um push, a melhor opção é excluir o branch e recriá-lo com git push origin :master; git push origin master:master. No entanto, a denyNonFastForwardsopção está habilitada por um motivo, que é descrito acima; em um repositório compartilhado, isso significa que agora todos os que o usam precisam garantir que eles sejam realocados no novo histórico.

Em um repositório compartilhado, geralmente é melhor apenas enviar novos commits para corrigir qualquer problema que você tenha; você pode usar git revertpara gerar commits que irão desfazer as mudanças dos commits anteriores.

Brian Campbell
fonte
ótima educação, e você acertou o comando no final, mas meu branch foi chamado de desenvolver (baseado no git-flow), e o outro cara colocou +developo comando dele - então o cheque vai para ele. Você tem um número astronômico de pontos de qualquer maneira: P
rynmrtn
7
Ou use o menos enigmáticogit push --force
Bennett McElwee
3
@Panique Você está tentando permitir que várias pessoas trabalhem em uma base de código grande e complexa ao mesmo tempo, sem bloquear umas às outras (permitindo que apenas uma pessoa trabalhe nisso por vez) e sem alterações que substituam umas às outras. Você precisa que cada pessoa seja capaz de fazer alterações de forma independente e mesclar essas alterações. A fusão (manual ou automática) pode introduzir problemas inesperados; portanto, você deseja reter o máximo de informações possível para poder descobrir o que aconteceu se der errado. Isso é intrinsecamente complexo; não é sujo, apenas um problema difícil.
Brian Campbell
Tentei as opções -f e + para reescrever o histórico de repositório remoto. Em ambas as opções, encontrei um problema de não avanço rápido. [17h05] $ git push -f origin local_A: remote_A Contando objetos: 35, concluído. Compressão delta usando até 2 threads. Comprimir objetos: 100% (18/18), pronto. Escrevendo objetos: 100% (21/21), 7,41 KiB, pronto. Total 21 (delta 9), reutilizado 0 (delta 0) remoto: para evitar que você perca o histórico, as atualizações não aceleradas foram rejeitadas. Junte as alterações remotas (por exemplo, 'git pull') antes de pressionar novamente. Veja a seção 'Nota sobre avanços rápidos' de 'git push --help' para detalhes.
Srikanth
4
@Srikanth É possível desativar totalmente os envios forçados com a receive.denyNonFastForwardsopção de configuração. Esta opção é habilitada por padrão em repositórios compartilhados. Nesse caso, se você realmente quiser forçar um push, a melhor opção é excluir o branch e recriá-lo com git push origin :remote_A; git push origin local_A:remote_A. Mas leia o que escrevi acima sobre por que não é uma boa ideia fazer esse tipo de fluxo de trabalho em um repositório compartilhado. Você só deve tentar fazer isso se tiver algo que cause problemas sérios no commit do qual está tentando se livrar ou reescrever.
Brian Campbell
14

Você pode ter que fazer um git pull, que PODE mesclar coisas automaticamente para você. Então você pode se comprometer novamente. Se você tiver conflitos, ele solicitará que você os resolva.

Lembre-se de que você deve especificar de qual branch extrair se não tiver atualizado seu gitconfig para especificar ...

Por exemplo:

git pull origin develop:develop
Tony
fonte
Ainda bravo com o não avanço rápido. Alguma ideia de como forçá-lo a mesclar? ! [rejected] develop -> develop (non-fast-forward)
rynmrtn
Acho que a opção é -f, mas posso estar errado. kernel.org/pub/software/scm/git/docs/git-pull.html
Tony
Isso funciona parcialmente. Não estou vendo as atualizações no github, porém (ele mostra o commit anterior como o mais recente, mesmo com a git push origin develop)
rynmrtn
7

Eu estava usando o EGit e também enfrentei esse problema. Apenas tentei rebaseo ramo atual e funcionou.

Nguyen Minh Binh
fonte