Git reset --hard e push para repositório remoto

200

Eu tinha um repositório que tinha alguns commits ruins (D, E e F para este exemplo).

Mestre ABCDEF e origem / mestre

Eu modifiquei o repositório local especificamente com a git reset --hard. Peguei uma ramificação antes da redefinição, agora tenho um repositório que se parece com:

A-B-C master  
     \ D-E-F old_master

A-B-C-D-E-F origin/master

Agora, eu precisava de algumas partes desses commits ruins, então selecionei os bits necessários e fiz alguns novos commits; agora, tenho o seguinte localmente:

A-B-C-G-H master
     \ D-E-F old_master

Agora, quero levar esse estado de coisas ao repositório remoto. No entanto, quando tento fazer um git pushGit educadamente , não consigo :

$ git push origin +master:master --force  
Total 0 (delta 0), reused 0 (delta 0)  
error: denying non-fast forward refs/heads/master (you should pull first)  
To [email protected]:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to '[email protected]:myrepo.git'  

Como faço para que o repositório remoto assuma o estado atual do repositório local?

robertpostill
fonte
2
Esta é uma duplicata 'quase' de várias "como eu envio perguntas de histórico alteradas", por exemplo, veja a resposta aqui stackoverflow.com/questions/253055/…
CB Bailey
2
Isso é verdade e eu tinha pesquisado no StackOverflow por uma resposta antes de postar. No entanto, minha pesquisa só encontrou respostas nas quais uma força de empurrão do git corrigiu o problema. Obrigado por ligar para o seu post :)
robertpostill
2
Em breve (git1.8.5, quarto trimestre de 2013), você poderá fazer um processo com git push -forcemais cuidado .
VonC 10/09/13

Respostas:

287

Se forçar um push não ajuda (" git push --force origin" ou " git push --force origin master" deve ser suficiente), pode significar que o servidor remoto está recusando impulsos sem avanço rápido por meio da variável de configuração receive.denyNonFastForwards (consulte a página de manual do git config para obter descrição) ou via gancho de atualização / pré-recebimento.

Com o Git mais antigo, você pode contornar essa restrição excluindo " git push origin :master" (consulte ':' antes do nome do ramo) e " git push origin master" recriando o ramo específico.

Se você não pode alterar isso, a única solução seria, em vez de reescrever o histórico, criar uma confirmação de alterações reversas no DEF :

ABCDEF - [(DEF) ^ - 1] mestre

Origem / mestre ABCDEF
Jakub Narębski
fonte
2
@ JakubNarębski, obrigado. get revert HEAD~Najudou. Né o número de confirmações. Por exemplo, se eu precisar cometer o anterior, eu vou usargit revert HEAD~1
Maksim Dmitriev
1
E lembre-se de que você quebrará o mestre local de todos os outros fazendo isso.
Tom Brito
24

Para complementar a resposta de Jakub, se você tiver acesso ao servidor remoto do git no ssh, poderá acessar o diretório remoto do git e definir:

user@remote$ git config receive.denyNonFastforwards false

Em seguida, volte ao seu repositório local, tente novamente fazer o seu commit com --force:

user@local$ git push origin +master:master --force

E, finalmente, reverta a configuração do servidor no estado protegido original:

user@remote$ git config receive.denyNonFastforwards true
Jealie
fonte
Consulte também pete.akeo.ie/2011/02/denying-non-fast-forward-and.html para obter informações personalizadas sobre o sourceforge sobre isso.
precisa saber é o seguinte
Instruções detalhadas sobre como denyNonFastForwards desativar usando visão fornecidos neste SO post: stackoverflow.com/a/43721579/2073804
ron190
2

Em vez de consertar sua ramificação "principal", é muito mais fácil trocá-la por sua "mestre desejada" renomeando as ramificações. Consulte https://stackoverflow.com/a/2862606/2321594 . Dessa forma, você não deixaria nenhum vestígio de vários logs de reversão.

Auxílio em
fonte
1

Todo o negócio de redefinição de tarefas parecia muito complicado para mim.

Então, eu fiz algo semelhante para obter minha pasta src no estado que eu tinha alguns commits atrás

# reset the local state
git reset <somecommit> --hard 
# copy the relevant part e.g. src (exclude is only needed if you specify .)
tar cvfz /tmp/current.tgz --exclude .git  src
# get the current state of git
git pull
# remove what you don't like anymore
rm -rf src
# restore from the tar file
tar xvfz /tmp/current.tgz
# commit everything back to git
git commit -a
# now you can properly push
git push

Dessa forma, o estado de coisas no src é mantido em um arquivo tar e o git é forçado a aceitar esse estado sem muita preocupação, basicamente o diretório src é substituído pelo estado que ele tinha vários commits atrás.

Wolfgang Fahl
fonte
0

Para os usuários do GitHub, isso funcionou para mim:

  1. Em todas as regras de proteção de ramificação nas quais você deseja fazer a alteração, verifique se a opção Permitir envio forçado está ativada
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

Isso "corrigirá" o histórico da ramificação em sua máquina local e no servidor GitHub, mas qualquer pessoa que sincronizou essa ramificação com o servidor desde a confirmação incorreta terá o histórico em sua máquina local. Se eles tiverem permissão para enviar diretamente para a ramificação, essas confirmações aparecerão imediatamente quando forem sincronizadas.

Tudo o que todo mundo precisa fazer é o git resetcomando acima para "corrigir" a ramificação em sua máquina local. É claro que eles precisariam ter cuidado com quaisquer confirmações locais feitas neste ramo após o hash de destino. Escolha / faça backup da cereja e aplique novamente conforme necessário, mas se você estiver em uma ramificação protegida, o número de pessoas que podem se comprometer diretamente com ela provavelmente será limitado.

Jason Faulkner
fonte