2009-09-04 7 views
171

J'avais un référentiel qui avait quelques mauvais commits (D, E et F pour cet exemple).Git reset --hard et un dépôt distant

maître A-B-C-D-E-F et origine/de maître

j'ai modifié le référentiel local spécifiquement avec un git reset --hard. Je pris une branche avant la remise à zéro maintenant j'ai une pension qui ressemble à:

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

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

Maintenant je besoin de quelques parties de ces mauvais commits donc je cerise cueillies les morceaux que je avais besoin et fait une nouvelle engage maintenant je ce qui suit localement:

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

Maintenant, je veux pousser cet état de choses au repo distant. Cependant, lorsque je tente de faire une git push Git me donne poliment la brosse au large:

$ 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' 

Comment puis-je obtenir la prise en pension à distance pour prendre l'état actuel du repo local?

+2

Le est un «presque» doublon de plusieurs «comment puis-je pousser les questions d'histoire modifiées», par ex. voir la réponse ici http://stackoverflow.com/questions/253055/how-do-i-push-amended-commit-to-the-remote-git-repo/255080#255080 –

+2

C'est vrai et j'avais cherché StackOverflow pour une réponse avant de poster. Cependant, ma recherche n'avait trouvé que des réponses dans lesquelles un git push --force a résolu le problème. Merci pour le lien vers votre message :) – robertpostill

+1

Vous allez bientôt (git1.8.5, Q4 2013) être capable de [faire un 'git push -force' plus attentivement] (http://stackoverflow.com/a/18505634/6309) . – VonC

Répondre

251

Si forcer une poussée ne permet pas (« git push --force origin « ou » git push --force origin master » devrait être suffisant), cela pourrait signifier que le serveur distant refuse non rapide vers l'avant soit par pousse receive.denyNonFastForwards variable de configuration (voir git config pour la description), ou via le hook de mise à jour/pré-réception.

Avec Git plus ancien, vous pouvez contourner cette restriction en supprimant "git push origin :master" (voir le ':' avant le nom de la branche), puis en recréant "git push origin master" étant donné la branche.

Si vous ne pouvez pas changer cela, la seule solution serait au lieu de réécrire l'histoire à créer un commettras reverting changements dans DEF:

 
A-B-C-D-E-F-[(D-E-F)^-1] master 

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

@ JakubNarębski, merci. 'REVER REVER ~ N' a aidé. 'N' est le nombre de commits. Par exemple, si j'ai besoin du commit précédent, j'utiliserai 'git revert HEAD ~ 1' –

+4

Je peux attester que' '' git push --force origin master''' fonctionne et m'a sauvé beaucoup de maux de tête. – FearlessFuture

23

Pour compléter la réponse de Jakub, si vous avez accès à le serveur distant git dans ssh, vous pouvez aller dans le répertoire distant git et définir:

[email protected]$ git config receive.denyNonFastforwards false 

Revenez ensuite à votre repo local, essayez à nouveau de faire votre engagement avec --force:

[email protected]$ git push origin +master:master --force 

Et enfin revenir à l'état protégé d'origine le réglage du serveur:

[email protected]$ git config receive.denyNonFastforwards true 
+0

Voir aussi http://pete.akeo.ie/2011/02/denying-non-fast-forward-and.html pour des informations sur sourceforge sur ce sujet. – hlovdal

+0

Des instructions détaillées sur la façon de désactiver denyNonFastForwards en utilisant 'vi' sont fournies sur ce message SO: stackoverflow.com/a/43721579/2073804 – ron190

1

Au lieu de fixer votre branche « maître », il est beaucoup plus facile d'échanger avec votre « maître désiré » en renommant les branches. Voir https://stackoverflow.com/a/2862606/2321594. De cette façon, vous ne laisserez même aucune trace de plusieurs journaux de retour.

0

L'ensemble de l'entreprise de réinitialisation git semblait loin de me compliquer la tâche.

Alors je faisais quelque chose le long des lignes pour obtenir mon dossier src dans l'état où j'eu quelques commits il y a

# 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 

De cette façon, l'état des choses dans le src est conservé dans un fichier de goudron et git est contraint d'accepter cet état sans trop de manipuler fondamentalement le répertoire src est remplacé par l'état qu'il avait plusieurs commits il y a.

Questions connexes