Git non-avance rapide rejeté

88

J'ai l'impression que cette question a été posée à plusieurs reprises, mais la solution est généralement «J'ai supprimé le répertoire et refait mon travail avec une nouvelle caisse». J'ai fait un commit et un push mais j'ai réalisé que je faisais référence au mauvais numéro de ticket dans le message de commit. J'ai donc cherché une solution rapide sur SO et j'ai fini par taper ce qui suit dans le terminal:

$ git reset --soft HEAD^
$ git commit -m "... correct message ..."

Le seul problème est que j'obtiens le message d'erreur suivant:

To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

J'utilise le modèle git-flow et je travaille sur la branche develop. Comment puis-je fusionner les choses pour rendre git heureux à nouveau?

rynmrtn
la source

Réponses:

52

Force git push:

git push origin +develop
Alan Haggai Alavi
la source
24
C'est une solution, mais lisez le commentaire de Brian Campbell pour comprendre ce que vous faites avant de l'utiliser.
thelem
5
git push origin + master
Aniket Thakur
Voir aussi la note à propos receive.denyNonFastForwardsde la réponse de Brian Campbell : +ou --forcepeut ne pas être suffisamment énergique, selon la façon dont ils - qui qu'ils soient - ont configuré leur référentiel Git.
torek le
174

Si vous appuyez sur une validation au serveur, puis réécriture qui engagent localement (avec git reset, git rebase, git filter-branchou toute autre manipulation de l' histoire), puis poussé que réécrite commettre de nouveau sur le serveur, vous bousiller toute autre personne qui avait tiré. Voici un exemple; disons que vous avez validé A et que vous l'avez poussé vers le serveur.

- * - * - A <- maître

- * - * - A <- origine / maître

Vous décidez maintenant de réécrire A, comme vous l'avez mentionné, de réinitialiser et de réengager. Notez que cela laisse un commit suspendu, A, qui sera finalement récupéré car il n'est pas accessible.

-*-*-UNE
    \
     Un '<- maître

- * - * - A <- origine / maître

Si quelqu'un d'autre, disons Fred, sort masterdu serveur pendant que vous faites cela, il aura une référence à A, à partir de laquelle il pourra commencer à travailler:

- * - * - A '<- maître

- * - * - A <- origine / maître

- * - * - AB <- fred / master

Maintenant, si vous pouviez pousser votre A 'à l'origine / maître, ce qui créerait une avance non rapide, il n'aurait pas A dans son histoire. Donc, si Fred essayait à nouveau de tirer, il devrait soudainement fusionner et réintroduire le commit A:

- * - * - A '<- maître

- * - * - A <- origine / maître

-*-*-UN B-\ 
    \ * <- fred / master
     UNE'--/

Si Fred remarque cela, alors il pourrait faire un rebase, ce qui empêcherait le commit A de réapparaître. Mais il devrait le remarquer et se souvenir de le faire; et si vous avez plus d'une personne qui a abaissé A, elles devraient toutes rebaser pour éviter d'avoir le commit A supplémentaire dans l'arbre.

Donc, ce n'est généralement pas une bonne idée de changer l'historique d'un repo dont d'autres personnes tirent. Si, cependant, vous savez que personne d'autre ne tire de ce dépôt (par exemple, c'est votre propre dépôt privé, ou vous n'avez qu'un seul autre développeur travaillant sur le projet avec lequel vous pouvez vous coordonner facilement), alors vous pouvez forcer mettre à jour en exécutant:

git push -f

ou

git push origin +master

Ceux-ci ignoreront à la fois la vérification d'une poussée non rapide et mettront à jour ce qui se trouve sur le serveur avec votre nouvelle révision A ', abandonnant la révision A afin qu'elle soit finalement ramassée.

Il est possible que les poussées forcées soient entièrement désactivées avec l' receive.denyNonFastForwardsoption config. Cette option est activée par défaut sur les référentiels partagés. Dans ce cas, si vous voulez vraiment, vraiment forcer un push, la meilleure option est de supprimer la branche et de la recréer, avec git push origin :master; git push origin master:master. Cependant, l' denyNonFastForwardsoption est activée pour une raison, qui est décrite ci-dessus; sur un référentiel partagé, cela signifie que désormais, tous ceux qui l'utilisent doivent s'assurer qu'ils rebasent sur le nouvel historique.

Sur un référentiel partagé, il est généralement préférable de simplement pousser de nouveaux commits par-dessus pour résoudre le problème que vous rencontrez; vous pouvez utiliser git revertpour générer des commits qui annuleront les modifications des commits précédents.

Brian Campbell
la source
excellente éducation, et vous avez obtenu la commande à la fin, mais ma branche s'appelait develop (basée sur git-flow), et l'autre gars a mis +developsa commande - donc le chèque lui revient. Vous avez quand même un nombre astronomique de points: P
rynmrtn
7
Ou utilisez le moins cryptiquegit push --force
Bennett McElwee
3
@Panique Vous essayez de permettre à plusieurs personnes de travailler sur une base de code volumineuse et complexe en même temps, sans se bloquer (permettant à une seule personne de travailler dessus à la fois), et sans que les changements se remplacent. Il faut que chaque personne puisse apporter des modifications de manière indépendante et fusionner ces modifications. La fusion (manuelle ou automatique) peut introduire des problèmes inattendus; vous voulez donc conserver autant d'informations que possible afin de pouvoir comprendre ce qui s'est passé en cas de problème. Ceci est intrinsèquement complexe; ce n'est pas sale, juste un problème difficile.
Brian Campbell
J'ai essayé les options -f et + pour réécrire l'historique du repo distant. Dans les deux options, j'ai rencontré un problème de non-avance rapide. [17:05] $ git push -f origine local_A: remote_A Comptage des objets: 35, terminé. Compression delta utilisant jusqu'à 2 threads. Compression d'objets: 100% (18/18), terminé. Écriture d'objets: 100% (21/21), 7,41 Kio, terminé. Total 21 (delta 9), réutilisé 0 (delta 0) à distance: pour vous éviter de perdre l'historique, les mises à jour non rapides ont été rejetées. Fusionnez les modifications distantes (par exemple 'git pull') avant de pousser à nouveau. Voir la section 'Remarque sur les avances rapides' de 'git push --help' pour plus de détails.
Srikanth
4
@Srikanth Il est possible de désactiver entièrement les poussées forcées avec l' receive.denyNonFastForwardsoption de configuration. Cette option est activée par défaut sur les référentiels partagés. Dans ce cas, si vous voulez vraiment, vraiment forcer un push, la meilleure option est de supprimer la branche et de la recréer, avec git push origin :remote_A; git push origin local_A:remote_A. Mais lisez ce que j'ai écrit ci-dessus pour expliquer pourquoi c'est une mauvaise idée de faire ce type de flux de travail sur un référentiel partagé. Vous ne devriez essayer de faire cela que si vous avez quelque chose qui pose de sérieux problèmes dans le commit que vous essayez de vous débarrasser ou de réécrire.
Brian Campbell
14

Vous devrez peut-être faire un git pull, qui PEUT fusionner automatiquement des trucs pour vous. Ensuite, vous pouvez vous engager à nouveau. Si vous avez des conflits, il vous demandera de les résoudre.

Gardez à l'esprit que vous devez spécifier la branche à partir de laquelle extraire si vous n'avez pas mis à jour votre gitconfig pour spécifier ...

Par exemple:

git pull origin develop:develop
Tony
la source
Toujours fou de non-avance rapide. Des réflexions sur la façon de le forcer à fusionner? ! [rejected] develop -> develop (non-fast-forward)
rynmrtn le
Je pense que le commutateur est -f mais je peux me tromper. kernel.org/pub/software/scm/git/docs/git-pull.html
Tony
Cela fonctionne partiellement. Je ne vois pas les mises à jour sur github, cependant (il montre le commit précédent comme le dernier même avec a git push origin develop)
rynmrtn
7

J'utilisais EGit et j'ai également rencontré ce problème. Je viens d'essayer rebasela branche actuelle et cela a fonctionné.

Nguyen Minh Binh
la source