1499 votes

Comment faire pour forcer un push Git ?

J'ai mis en place un dépôt "principal" distant non dénudé et l'ai cloné sur mon ordinateur. J'ai fait quelques modifications locales, mis à jour mon dépôt local, et poussé les modifications vers mon dépôt distant. Tout allait bien jusqu'à ce moment-là.

Maintenant, je devais changer quelque chose dans le dépôt distant. Puis j'ai changé quelque chose dans mon dépôt local. J'ai réalisé que la modification dans le dépôt distant n'était pas nécessaire. J'ai donc essayé de git push de mon repo local vers mon repo distant, mais j'ai obtenu une erreur comme :

Pour éviter de perdre l'historique, les mises à jour non rapides ont été rejetées Fusionner les changements distants avant de pousser à nouveau. Voir la section 'Note sur les fast-forwards' de git push --help pour les détails.

Je pensais que probablement un

git push --force

forcerait ma copie locale à pousser les changements vers la copie distante et la rendrait identique. Il force la mise à jour Mais lorsque je retourne dans le dépôt distant et que j'effectue une livraison, je remarque que les fichiers contiennent des modifications périmées (celles qui étaient déjà présentes dans le dépôt distant principal).

Comme je l'ai mentionné dans le commentaires sur l'une des réponses :

J'ai essayé de forcer, mais lorsque je retourne sur le serveur maître pour sauvegarder les changements, j'obtiens une mise en scène périmée. Ainsi, lorsque je commite, les dépôts ne sont pas les mêmes. Et quand j'essaie d'utiliser à nouveau git push, j'obtiens la même erreur.

Comment puis-je résoudre ce problème ?

4 votes

Vous serez bientôt (git1.8.5, Q4 2013) en mesure de faire un git push -force plus attentivement .

1 votes

9 votes

Comme je le détaille dans ma propre réponse , git push --force est en effet une autre manière valide de forcer le push, et poussera les branches tout aussi bien que git push origin master --force avec la méthode par défaut de Git push.default config settings Les branches qui sont poussées diffèrent selon que la version de Git est antérieure ou postérieure à la version 2.0.

2685voto

Kayvar Points 4239

Fais-le :

git push origin <your_branch_name> --force

ou si vous avez un repo spécifique :

git push https://git.... --force

Cela supprimera votre/vos commit(s) précédent(s) et poussera votre commit actuel.

Ce n'est peut-être pas approprié, mais si quelqu'un tombe sur cette page, il pense qu'il pourrait vouloir une solution simple...

Drapeau court

Notez également que -f est le diminutif de --force donc

git push origin <your_branch_name> -f

fonctionnera également.

68 votes

Vous pouvez utiliser git push origin +master à la place, qui vous permettent de pousser plusieurs refspecs sans les forcer toutes.

6 votes

Soyez conscient que, si vous faites accidentellement juste git push --force vous pourriez finir par perturber votre branche principale (en fonction du comportement par défaut de votre push) Ce qui pourrait craindre un peu :D

12 votes

@Jeewes à partir de la version 2.0 de Git, l'option par défaut comportement de git push --force consiste essentiellement à pousser la branche actuellement extraite vers sa partie distante, donc si vous avez la branche principale extraite, c'est identique à git push origin master --force . Ce sera différent si vous utilisez l'option matching pour push.default qui est la valeur par défaut pour les versions de Git antérieures à 2.0. matching pousse tous des branches locales vers des branches distantes qui ont le même nom, donc les pousser de force pourrait ne pas être ce que vous voulez faire...

280voto

Cawas Points 3303

Et si push --force ne fonctionne pas vous pouvez faire push --delete . Regardez 2 et sur cette instance :

git reset --hard HEAD~3  # reset current branch to 3 commits ago
git push origin master --delete  # do a very very bad bad thing
git push origin master  # regular push

Mais attention...

Ne jamais revenir en arrière sur un historique git public !

En d'autres termes :

  • N'essayez jamais force pousser sur un dépôt public.
  • Ne fais pas ça ou quoi que ce soit qui puisse briser la vie de quelqu'un. pull .
  • N'essayez jamais reset ou rewrite l'histoire dans un repo quelqu'un a peut-être déjà tiré.

Bien sûr, il existe des exceptions exceptionnellement rares à cette règle, mais dans la plupart des cas, il n'est pas nécessaire de le faire et cela génère des problèmes pour tous les autres.

Faites un revert à la place.

Et soyez toujours prudent avec ce que vous poussez vers un dépôt public. . Retour en arrière :

git revert -n HEAD~3..HEAD  # prepare a new commit reverting last 3 commits
git commit -m "sorry - revert last 3 commits because I was not careful"
git push origin master  # regular push

En effet, les deux HEADs d'origine (de la revenir à et de la mauvaise réinitialisation ) contiendront les mêmes fichiers.


modifier pour ajouter des informations mises à jour et plus d'arguments autour push --force

Envisager de pousser la force avec lease au lieu de push, mais toujours préférer revert

Un autre problème push --force may bring, c'est quand quelqu'un pousse quelque chose avant vous, mais après que vous ayez déjà récupéré. Si vous forcez votre rebasé version actuelle, vous remplacer le travail des autres .

git push --force-with-lease introduit dans le git 1.8.5 ( grâce à @VonC commentaire sur la question) tente d'aborder cette question spécifique. Fondamentalement, il produira une erreur et ne poussera pas si la télécommande a été modifiée depuis votre dernière récupération.

C'est bien si vous êtes vraiment sûr qu'une push --force est nécessaire, mais je veux quand même éviter d'autres problèmes. J'irais même jusqu'à dire que cela devrait être la solution par défaut. push --force comportement. Mais c'est encore loin d'être une excuse pour forcer l'adoption d'une loi sur la protection de l'environnement. push . Les personnes qui récupéré avant votre rebasement vous aurez encore beaucoup de problèmes, qui pourraient être facilement évités si vous aviez inversé à la place.

Et puisque nous parlons de git --push les instances...

Pourquoi voudrait-on forcer le passage ?

@linquize a apporté un bon exemple de force de poussée sur les commentaires : données sensibles . Tu as divulgué à tort des données qui ne devraient pas être poussées. Si tu es assez rapide, tu peux "fixer" * en forçant une poussée sur le dessus.

* Le site les données seront toujours sur la télécommande à moins que vous ne fassiez également un collecte des déchets ou le nettoyer d'une manière ou d'une autre . Il y a aussi le potentiel évident pour qu'il soit répandu par d'autres qui récupéré déjà, mais vous voyez l'idée.

0 votes

On dirait qu'il est possible de faire une traction après ça, mais c'est difficile. stackoverflow.com/questions/9813816/

1 votes

Le problème, @rogerdpack, n'est pas de savoir si c'est faisable. C'est faisable. Mais cela peut se résumer à un gros désastre. Plus les gens le font (forcer le push) et moins vous mettez à jour (pull) le repo public, plus le désastre est grand. Cela peut démanteler le monde tel que vous le connaissez !!!111 Du moins le monde comprenant ce dépôt particulier.

3 votes

Si vous avez des données sensibles, forcez-les

22voto

Cupcake Points 22154

Pour en savoir plus sur la poussée des forces, voir mon autre réponse .

Cette réponse explique que le problème fondamental de l'affiche originale est pas la façon dont il pousse la force, mais plutôt qu'il pousse à une non dénudé (au lieu d'un repo nu).

Comme indiqué dans d'autres réponses 1 il n'est généralement pas recommandé de pousser vers des dépôts non dénudés, car bien que vous puissiez mettre à jour une branche distante référence en poussant, Les pushes Git ne mettront pas réellement à jour la copie de travail ni l'index/staging-area du repo distant non-bare.

Cela signifie que la copie de travail et l'index/staging-area du dépôt distant seront toujours définis comme les versions des fichiers qui existaient. avant que vous avez poussé, tandis que la référence symbolique HEAD sera placé à l'extrémité de la branche nouvellement poussée. Ainsi, il apparaîtra que les changements du commit précédent sont toujours mis à disposition. ce qui correspond à ce que vous avez indiqué dans votre question :

Je remarque que les fichiers contiennent des modifications périmées (celles que le repo principal distant avait auparavant).

1 Voir <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 dénudé, 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 nu par défaut, vous devrez donc configurer ce dépôt pour l'autoriser :

git config receive.denyCurrentBranch warn

Ensuite, faites un autre clone, et faites un hard reset en revenant à un commit, puis poussez :

$ cd ..
$ git clone main second
$ git reset --hard head^
$ git push --force
Total 0 (delta 0), reused 0 (delta 0)
remote: warning: updating the current branch
To main
 + 3c3ee2b...2f34b62 master -> master (forced update)

Retournez maintenant au dépôt principal et vérifiez les journaux et l'état des fichiers. Vous verrez que même si la branche principale est revenue au commit original, la zone de transit contient toujours les changements du second commit qui a été annulé dans la branche par le push forcé :

$ cd ../main
$ git log --oneline --decorate
2f34b62 (HEAD, master) Add hello.txt

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   bye.txt

Solution

Si vous voulez que la copie de travail et l'index/zone d'échelonnement dans le distant non nu correspondent au commit actuel de la branche courante (représenté par la référence symbolique HEAD ), alors vous devez simplement faire une réinitialisation dure pour HEAD :

git reset --hard HEAD

Enfin, il est recommandé de ne pousser qu'à nu au lieu de dépôts non nus, à cause de ce même problème de la copie de travail et de l'index qui ne sont plus synchronisés avec la branche de votre dépôt et les références symboliques.

Un moyen simple de convertir un repo non nu en repo nu est de le reclasser :

git clone --bare originalRepo bareRepo
cd bareRepo
git remote rm origin

Documentation

De la Page de manuel de git-config(1) (c'est moi qui souligne) :

receive.denyCurrentBranch

S'il est défini à true ou "refuse", git-receive-pack refusera une mise à jour de la référence à la branche actuellement extraite d'un dépôt non nu. Une telle poussée est potentiellement dangereuse car elle désynchronise le HEAD avec l'index et l'arbre de travail. S'il est défini à "warn", un message d'avertissement sera affiché sur stderr, mais le push sera autorisé. S'il est défini à false ou "ignore", autorise de telles poussées sans message. La valeur par défaut est "refuse".

Ce paramètre est disponible dans Git depuis la version 1.6.2 qui est sorti le 4 mars th , 2009. Comme l'indique la documentation ci-dessus, le réglage par défaut consiste à refuser les poussées, même les poussées forcées.

Démonstration que la force qui pousse n'est pas une solution au problème réel de l'affiche originale

Parce qu'il semble y avoir beaucoup de confusion à ce sujet, je vais démontrer que la poussée de force n'est pas la bonne solution pour résoudre votre problème.

Tout d'abord, créez un dépôt principal comme dans les étapes précédentes de reproduction ci-dessus, ainsi qu'un second clone de ce dépôt principal, mais cette fois, ne mettez pas receive.denyCurrentBranch . Ce paramètre refuser des poussées, même des poussées forcées, vers des dépôts distants non dénudés, et a été le paramètre par défaut depuis la version 1.6.2 de Git .

Ensuite, à partir du second clone, essayez de forcer le push vers le repo principal :

$ git push --force
Total 0 (delta 0), reused 0 (delta 0)
error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.
error:
error: You can set 'receive.denyCurrentBranch' configuration variable to
error: 'ignore' or 'warn' in the remote repository to allow pushing into
error: its current branch; however, this is not recommended unless you
error: arranged to update its work tree to match what you pushed in some
error: other way.
error:
error: To squelch this message and still keep the default behaviour, set
error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To main
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'main'

Ou avec git push origin master --force :

$ git push origin master --force
Total 0 (delta 0), reused 0 (delta 0)
error: refusing to update checked out branch: refs/heads/master
error: By default, updating the current branch in a non-bare repository
error: is denied, because it will make the index and work tree inconsistent
error: with what you pushed, and will require 'git reset --hard' to match
error: the work tree to HEAD.
error:
error: You can set 'receive.denyCurrentBranch' configuration variable to
error: 'ignore' or 'warn' in the remote repository to allow pushing into
error: its current branch; however, this is not recommended unless you
error: arranged to update its work tree to match what you pushed in some
error: other way.
error:
error: To squelch this message and still keep the default behaviour, set
error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To main
 ! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to 'main'

Comme vous le verrez ci-dessus, quelle que soit la façon dont vous essayez de pousser vers le repo distant non dénudé, Git ne l'autorise pas par défaut. Même si vous définissez receive.denyCurrentBranch pour permettre la poussée de la force, il ne touchera toujours pas l'index et l'arbre de travail du repo distant non-bare ce qui rend

git reset --hard HEAD

la solution correcte lorsque la copie de travail et la zone d'indexation/staging d'un distant non nu ne sont plus synchronisées avec la copie symbolique. HEAD référence.

18voto

ubik Points 2361

Tout d'abord, je ne ferais pas de changements directement dans le dépôt "principal". Si vous voulez vraiment avoir un dépôt "principal", alors vous devez seulement pousser vers ce dépôt, sans jamais le modifier directement.

En ce qui concerne l'erreur que vous obtenez, avez-vous essayé de git pull de votre dépôt local, et ensuite git push dans le dépôt principal ? Ce que vous faites actuellement (si j'ai bien compris) est de forcer le push et ensuite de perdre vos modifications dans le repo "principal". Vous devriez d'abord fusionner les changements localement.

0 votes

Oui, j'ai essayé de tirer, mais je perds des données à cause de ce tirage. Je veux que mon dépôt principal soit identique à mon dépôt local, sans avoir à le mettre à jour depuis le dépôt principal.

1 votes

Dans ce cas, utilisez git push -f mais ensuite, si vous modifiez à nouveau votre dépôt principal, vous devez revenir à votre dépôt local et git pull pour qu'il soit synchronisé avec les dernières modifications. Ensuite, vous pouvez faire votre travail, et pousser à nouveau. Si vous suivez ce flux de travail "push-pull", vous n'obtiendrez pas le type d'erreur dont vous vous êtes plaint.

0 votes

Oui, je comprends que c'était ma faute :/ Je vais essayer et je reviendrai dans un petit moment merci

12voto

Cupcake Points 22154

Cette réponse explique force poussant . Le problème réel de l'affiche originale n'était pas de forcer le push lui-même, mais de pousser vers un repo non nu (au lieu d'un repo nu), comme expliqué dans mon autre réponse .

Vous avez 4 façons de forcer le push avec Git

git push <remote> <branch> -f
git push origin master -f # Example

git push <remote> -f
git push origin -f # Example

git push -f

git push <remote> <branch> --force-with-lease

Si vous souhaitez une explication plus détaillée de chaque commande, consultez la section des réponses longues ci-dessous.

Attention : Force pushing va écraser la branche distante avec l'état de la branche que vous êtes en train de pousser. Assurez-vous que c'est bien ce que vous voulez faire avant de l'utiliser, sinon vous risquez d'écraser des commits que vous souhaitez conserver.

Détails de la poussée des forces

Spécifier la distance et la branche

Vous pouvez spécifier complètement des branches spécifiques et une télécommande. Le site -f est la version abrégée de --force

git push <remote> <branch> --force
git push <remote> <branch> -f

Omettre la branche

Lorsque la branche à pousser est omise, Git s'en charge en se basant sur vos paramètres de configuration. Dans les versions de Git postérieures à 2.0, un nouveau repo aura des paramètres par défaut pour pousser la branche actuellement extraite :

git push <remote> --force

alors qu'avant la 2.0, les nouveaux dépôts auront des paramètres par défaut pour pousser plusieurs branches locales. Les paramètres en question sont les suivants remote.<remote>.push et push.default (voir ci-dessous).

Omettre le distant et la branche

Lorsque le distant et la branche sont omis, le comportement de seulement git push --force est déterminé par votre push.default Paramètres de configuration de Git :

git push --force
  • À partir de Git 2.0, le paramètre par défaut, simple En fait, il suffit de pousser votre branche actuelle vers sa contrepartie distante en amont. Le distant est déterminé par le paramètre branch.<remote>.remote et, par défaut, le repo d'origine.

  • Avant la version 2.0 de Git, le paramètre par défaut, matching En gros, il s'agit de pousser toutes vos branches locales vers les branches portant le même nom sur le serveur distant (qui est par défaut origin).

Vous pouvez lire la suite push.default les paramètres en lisant git help config ou une version en ligne de la page de manuel git-config(1) .

Pousser plus sûrement avec --force-with-lease

Le forçage avec un "bail" permet au forçage d'échouer s'il y a de nouveaux commits sur la branche distante que vous n'attendiez pas (techniquement, si vous ne les avez pas encore récupérés dans votre branche de suivi distante), ce qui est utile si vous ne voulez pas accidentellement écraser les commits de quelqu'un d'autre que vous ne connaissez même pas encore, et que vous voulez juste écraser les vôtres :

git push <remote> <branch> --force-with-lease

Note Cependant, étant donné qu'il s'agit toujours d'un push forcé, cela peut toujours être un problème pour vos collaborateurs s'ils ne peuvent ou ne veulent pas resynchroniser leurs commits existants avec ceux du push forcé.

Vous pouvez obtenir plus de détails sur la façon d'utiliser --force-with-lease en lisant l'un des documents suivants :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X