Git reset --hard et push vers le référentiel distant


200

J'avais un dépôt qui avait de mauvais commits (D, E et F pour cet exemple).

Master ABCDEF et origine / master

J'ai modifié le référentiel local spécifiquement avec a git reset --hard. J'ai pris une branche avant la réinitialisation alors maintenant j'ai un dépôt qui ressemble à:

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

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

Maintenant, j'avais besoin de certaines parties de ces mauvais commits, alors j'ai choisi les bits dont j'avais besoin et j'ai fait de nouveaux commits alors maintenant, j'ai les éléments suivants localement:

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

Maintenant, je veux pousser cet état de choses au référentiel distant. Cependant, lorsque j'essaie de faire un git pushGit, je me donne poliment le coup de pinceau:

$ 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 git@git.example.com:myrepo.git  
! [remote rejected] master -> master (non-fast forward)  
error: failed to push some refs to 'git@git.example.com:myrepo.git'  

Comment obtenir le référentiel distant pour prendre l'état actuel du référentiel local?


2
Il s'agit d'un doublon «presque» de plusieurs «comment puis-je pousser les questions d'historique modifiées», par exemple, voir la réponse ici stackoverflow.com/questions/253055/…
CB Bailey

2
C'est vrai et j'avais recherché une réponse sur StackOverflow avant de poster. Cependant, ma recherche n'avait trouvé que des réponses dans lesquelles un git push --force avait résolu le problème. Merci d'avoir
créé un

2
Vous pourrez bientôt (git1.8.5, Q4 2013) pouvoir faire git push -forceplus attentivement .
VonC

Réponses:


287

Si forcer une poussée n'aide pas (" git push --force origin" ou " git push --force origin master" devrait être suffisant), cela peut signifier que le serveur distant refuse les poussées non rapides via la réception .denyNonFastForwards (voir git config pour la description manpage), ou via le crochet 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 " la branche donnée.

Si vous ne pouvez pas changer cela, alors la seule solution serait au lieu de réécrire l'historique pour créer un commit annulant les modifications dans DEF :

ABCDEF - [(DEF) ^ - 1] maître

Origine / maître ABCDEF

2
@ JakubNarębski, merci. get revert HEAD~Naidé. Nest le nombre de validations. Par exemple, si j'ai besoin du commit précédent, je vais utilisergit revert HEAD~1
Maksim Dmitriev

1
Et sachez que vous briserez tous les autres maîtres locaux en faisant cela.
Tom Brito

24

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

user@remote$ git config receive.denyNonFastforwards false

Revenez ensuite à votre référentiel local, essayez à nouveau de faire votre commit avec --force:

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

Et enfin, rétablissez les paramètres du serveur dans l'état protégé d'origine:

user@remote$ git config receive.denyNonFastforwards true

Voir aussi pete.akeo.ie/2011/02/denying-non-fast-forward-and.html pour des informations personnalisées sur Sourceforge à ce sujet.
hlovdal

Des instructions détaillées sur la façon de désactiver denyNonFastForwards à l'aide visont fournies sur ce message SO: stackoverflow.com/a/43721579/2073804
ron190

2

Au lieu de réparer votre branche "maître", il est beaucoup plus facile de l'échanger avec votre "maître souhaité" en renommant les branches. Voir https://stackoverflow.com/a/2862606/2321594 . De cette façon, vous ne laisseriez même aucune trace de plusieurs journaux de retour.


1

Toute l'entreprise de réinitialisation de Git semblait loin de me compliquer les choses.

J'ai donc fait quelque chose le long des lignes pour obtenir mon dossier src dans l'état que j'avais il y a quelques commits

# 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, la situation dans le src est conservée dans un fichier tar et git est obligé d'accepter cet état sans trop jouer, le répertoire src est remplacé par l'état qu'il avait plusieurs validations il y a plusieurs années.


0

Pour les utilisateurs de GitHub, cela a fonctionné pour moi:

  1. Dans toutes les règles de protection de branche où vous souhaitez effectuer la modification, assurez-vous que l' option Autoriser les poussées forcées est activée
  2. git reset --hard <full_hash_of_commit_to_reset_to>
  3. git push --force

Cela "corrigera" l'historique des branches sur votre machine locale et le serveur GitHub, mais quiconque a synchronisé cette branche avec le serveur depuis le mauvais commit aura l'historique sur leur machine locale. S'ils sont autorisés à envoyer directement à la branche, ces validations s'afficheront directement lors de la synchronisation.

Tout ce que tout le monde doit faire, c'est la git resetcommande d'en haut pour "corriger" la branche sur sa machine locale. Bien sûr, ils devraient se méfier de tout commit local effectué sur cette branche après le hachage cible. Cherry pick / backup et réappliquez ceux-ci si nécessaire, mais si vous êtes dans une branche protégée, le nombre de personnes qui peuvent s'y engager directement est probablement limité.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.