Pour en savoir plus sur le force pushing, consultez ma autre réponse.
Cette réponse explique que le problème fondamental du demandeur original n'est pas la manière dont il effectue le force pushing, mais plutôt qu'il pousse vers un dépôt non-bare (au lieu d'un dépôt bare).
Comme indiqué dans d'autres réponses1, il n'est généralement pas recommandé de pousser vers des dépôts non-bare, car bien que vous puissiez mettre à jour une référence de branche distante en poussant, les poussées Git n'actualiseront pas réellement la copie de travail ni l'index/zone de staging du dépôt non-bare distant.
Cela signifie que la copie de travail et l'index/zone de staging du dépôt distant seront toujours définis sur les versions des fichiers qui existaient avant que vous ayez poussé, tandis que la référence symbolique HEAD
sera définie sur la nouvelle pointe de la branche poussée. Il semblera donc que les modifications du commit précédent soient toujours en attente, ce qui correspond à ce que vous avez indiqué dans votre question :
Je remarque que les fichiers contiennent des modifications obsolètes (celles qui existaient précédemment dans le dépôt distant principal).
1Voir <a href="http://stackoverflow.com/a/5509650/456814">la réponse de VonC</a> et <a href="http://stackoverflow.com/a/5509588/456814">la réponse de ubik</a>.
Comment reproduire le problème
Tout d'abord, créez un nouveau dépôt Git non-bare, et remplissez-le avec quelques fichiers :
mkdir main
cd main
git init
echo hello > hello.txt
git add .
git commit -m "Hello"
echo bye > bye.txt
git add .
git commit -m "Bye"
Les versions actuelles de Git ne vous permettent pas de pousser vers un dépôt non-bare par défaut, vous devrez donc configurer ce dépôt pour le permettre :
git config receive.denyCurrentBranch warn
Ensuite, faites un autre clone, et effectuez un hard reset d'un commit en arrière, puis poussez :
$ cd ..
$ git clone main second
$ git reset --hard head^
$ git push --force
Total 0 (delta 0), reused 0 (delta 0)
remote: avertissement : mise à jour de la branche actuelle
To main
+ 3c3ee2b...2f34b62 master -> master (mise à jour forcée)
Retournez maintenant au dépôt principal, et vérifiez à la fois les journaux et l'état des fichiers. Vous verrez que même si la branche master est revenu au commit original, la zone de staging contient toujours les modifications du deuxième commit qui ont été annulées dans la branche par le force push :
$ cd ../main
$ git log --oneline --decorate
2f34b62 (HEAD, master) Ajout de hello.txt
$ git status
On branch master
Changes to be committed:
(utilisez "git reset HEAD ..." pour désindexer)
nouveau fichier: bye.txt
Solution
Si vous souhaitez que la copie de travail et l'index/zone de staging dans le dépôt distant non-bare correspondent au commit actuel de la branche actuelle (représenté par la référence symbolique HEAD
), il vous suffit de faire un hard reset à HEAD
:
git reset --hard HEAD
Enfin, il est recommandé de pousser uniquement vers des dépôts bare au lieu de dépôts non-bare, en raison de ce problème de désynchronisation de la copie de travail et de l'index par rapport à la branche et aux références symboliques de votre dépôt.
Une manière simple de convertir un dépôt non-bare en un dépôt bare est simplement de le recréer :
git clone --bare originalRepo bareRepo
cd bareRepo
git remote rm origin
Documentation
D'après la page manuelle git-config(1) (mes emphases) :
receive.denyCurrentBranch
Si défini sur true ou "refuse", git-receive-pack refusera une mise à jour de référence vers la branche actuellement vérifiée d'un dépôt non-bare. Un tel push est potentiellement dangereux car il désynchronise HEAD avec l'index et l'arborescence de travail. S'il est défini sur "warn", imprime un avertissement de ce type de poussée sur stderr, mais permet à la poussée de continuer. S'il est défini sur false ou "ignore", autorise de tels push sans message. Par défaut, il est défini sur "refuse".
Ce paramètre est disponible dans Git depuis la version 1.6.2, qui a été publiée le 4 mars 2009. Comme l'indique la documentation ci-dessus, le paramètre se configure par défaut pour refuser les poussées, même les force-pushes.
Démonstration que le force pushing n'est pas une solution au problème réel du demandeur original
Parce qu'il semble y avoir beaucoup de confusion à ce sujet, je vais démontrer que le force-pushing n'est pas la bonne solution pour résoudre votre problème.
Tout d'abord, créez un dépôt principal exactement comme dans les étapes de reproduction précédentes ci-dessus, ainsi qu'un deuxième clone de ce dépôt principal, mais cette fois, ne définissez pas receive.denyCurrentBranch
. Ce paramètre refusera les poussées, même les force-pushes, vers les dépôts distants non-bare, et a été le paramètre par défaut depuis Git version 1.6.2.
Ensuite, depuis le deuxième clone, essayez de forcer la poussée vers le dépôt principal :
$ git push --force
Total 0 (delta 0), reused 0 (delta 0)
error: refus de mettre à jour la branche actuellement vérifiée : refs/heads/master
error: Par défaut, la mise à jour de la branche actuelle dans un dépôt non-bare
error: est refusée, car elle rendra l'index et l'arborescence de travail inconsistants
error: avec ce que vous avez poussé, et nécessitera 'git reset --hard' pour correspondre
error: l'arborescence de travail à HEAD.
error:
error: Vous pouvez définir la variable de configuration 'receive.denyCurrentBranch' à
error: 'ignore' ou 'warn' dans le dépôt distant pour autoriser une poussée dans
error: sa branche actuelle ; cependant, ce n'est pas recommandé à moins que vous
error: ne puissiez mettre à jour son arborescence de travail pour correspondre à ce que vous avez poussé d'une autre manière.
error:
error: Pour masquer ce message tout en conservant le comportement par défaut, définissez
error: la variable de configuration 'receive.denyCurrentBranch' sur 'refuse'.
To main
! [rejected] master -> master (la branche est actuellement vérifiée)
error: échec de la transmission de certaines références vers 'main'
Ou avec git push origin master --force
:
$ git push origin master --force
Total 0 (delta 0), reused 0 (delta 0)
error: refus de mettre à jour la branche actuellement vérifiée : refs/heads/master
error: Par défaut, la mise à jour de la branche actuelle dans un dépôt non-bare
error: est refusée, car elle rendra l'index et l'arborescence de travail inconsistants
error: avec ce que vous avez poussé, et nécessitera 'git reset --hard' pour correspondre
error: l'arborescence de travail à HEAD.
error:
error: Vous pouvez définir la variable de configuration 'receive.denyCurrentBranch' à
error: 'ignore' ou 'warn' dans le dépôt distant pour autoriser une poussée dans
error: sa branche actuelle ; cependant, ce n'est pas recommandé à moins que vous
error: ne puissiez mettre à jour son arborescence de travail pour correspondre à ce que vous avez poussé d'une autre manière.
error:
error: Pour masquer ce message tout en conservant le comportement par défaut, définissez
error: la variable de configuration 'receive.denyCurrentBranch' sur 'refuse'.
To main
! [rejected] master -> master (la branche est actuellement vérifiée)
error: échec de la transmission de certaines références vers 'main'
Comme vous pouvez le constater ci-dessus, peu importe la manière dont vous essayez de forcer la poussée vers le dépôt distant non-bare, Git ne l'autorisera pas par défaut. Même lorsque vous définissez receive.denyCurrentBranch
pour autoriser les force-pushes, cela ne touchera toujours pas à l'index et l'arborescence de travail du dépôt non-bare distant, ce qui rend
git reset --hard HEAD
la solution correcte lorsque la copie de travail et l'index/zone de staging d'un dépôt distant non-bare deviennent désynchronisés avec la référence symbolique HEAD
.
4 votes
Vous pourrez bientôt (git1.8.5, T4 2013) faire un
git push -force
de manière plus prudente.1 votes
Lié: Forcer git à écraser les fichiers distants lors de la poussée.
14 votes
Comme je le détaille dans ma propre réponse,
git push --force
est en effet une autre façon valide de forcer la poussée et poussera les branches aussi bien quegit push origin master --force
avec les paramètres de configuration par défaut depush.default
de Git, bien que les branches spécifiquement poussées diffèrent entre les versions de Git antérieures à 2.0 et celles après 2.0.9 votes
git push --force
fonctionne bien ces jours-ci, FWIW...11 votes
git push --force-with-lease
fonctionne encore mieux :), il refusera de mettre à jour une branche à moins que ce ne soit l'état que vous attendez. (voir developer.atlassian.com/blog/2015/04/force-with-lease)0 votes
faites attention les gens > Lorsque vous poussez du code en force vers la branche principale [dépôt distant], cela supprime le code précédent présent dans la branche principale. > et met à jour votre code avec le code précédent. c'est-à-dire que lorsque vous poussez en force, le code précédent sera remplacé par votre code. [consultez ce blog pour plus d'informations](evilmartians.com/chronicles/…) Bon codage :)
0 votes
J'ai essayé
git push origin some_branch --force
mais il a toujours renvoyé le messageEverything is up-to-date
. Cependant,git push origin your_branch:some_branch --force
et c'était ce que je manquais. J'espère que cela vous aidera!