git push --force-with-bail vs --force

183

J'essaye de comprendre la différence entre

git push --force

et

git push --force-with-lease

Je suppose que ce dernier ne pousse vers la télécommande que si la télécommande n'a pas de commits que la branche locale n'a pas ?

Alexander Mills
la source
la " branche locale de suivi à distance ". Cela signifie essentiellement que la télécommande doit ressembler à ce à quoi votre client s'attend. git help pusha des cas d'utilisation expliquant son objectif (essentiellement pour vous empêcher de détruire un changement que quelqu'un vient de pousser). Ce qui me semble peu clair, c'est comment fonctionne la branche de suivi à distance. Mais vraisemblablement, il devra généralement ressembler à ce à quoi il ressemblait la dernière fois que vous avez effectué un fetchou pullsans nouveaux commits.
zzxyz
4
@zzxyz: l'implémentation réelle de --force-with-leaseest similaire à celle des instructions de comparaison et d'échange sur les processeurs modernes: celui qui veut que l'échange se produise fournit la valeur attendue et la nouvelle valeur. Le système effectuant le swap compare la valeur attendue avec la valeur actuelle réelle et effectue le swap si et seulement si les deux sont égaux. Avec git push, la valeur attendue est celle qui se trouve dans le nom du suivi à distance, par exemple, git push --force-with-lease origin Xenvoie la vôtre origin/Xavec la nouvelle valeur souhaitée; origin's Git vous dit s'il a effectué l'échange ou non.
torek
1
Si le Git a originfait l'échange, vous avez terminé. Si ce n'est pas le cas, vous pouvez exécuter git fetch originpour récupérer la nouvelle valeur actuelle, retravailler vos modifications si nécessaire et exécuter une autre comparaison et permutation forcée avec bail pour réessayer.
torek

Réponses:

173

force remplace une branche distante par votre branche locale.

--force-with-leaseest une option plus sûre qui n'écrasera aucun travail sur la branche distante si plus de commits étaient ajoutés à la branche distante (par un autre membre de l'équipe ou un collègue ou autre). Cela garantit que vous n'écrasez pas le travail de quelqu'un d'autre en poussant de force.

Je pense que votre idée générale concernant la commande est correcte. Si la branche distante a la même valeur que la branche distante sur votre machine locale, vous écraserez remote. S'il n'a pas la même valeur, cela indique une modification que quelqu'un d'autre a apportée à la branche distante pendant que vous travailliez sur votre code et n'écrasera donc aucun code. Évidemment, s'il y a des validations supplémentaires dans remote, les valeurs ne seront pas les mêmes.

Je pense simplement --force-with-leaseà l'option à utiliser lorsque je veux m'assurer de ne pas écraser le code d'un coéquipier. De nombreuses équipes de mon entreprise utilisent --force-with-leasecomme option par défaut pour une sécurité intégrée. C'est inutile dans la plupart des cas, mais vous évitera beaucoup de maux de tête si vous écrasez quelque chose qu'une autre personne a contribué à distance.

Je suis sûr que vous avez regardé les documents, mais il pourrait y avoir une explication plus verbeuse contenue ici:

https://git-scm.com/docs/git-push

chevybow
la source
39

Vous cherchez une réponse tirée de sources crédibles et / ou officielles.

Le "comparer et échanger" mentionné par torek dans les commentaires et dans son autre réponse est davantage illustré par les sources de Git lui-même .

ce dernier ne pousse vers la télécommande que si la télécommande n'a pas de commits que la branche locale n'a pas?

Cette fonctionnalité a été introduite dans ce commit (décembre 2013, Git v1.8.5-rc0)

--force-with-lease protégera toutes les références distantes qui vont être mises à jour en exigeant que leur valeur actuelle soit la même qu'une valeur par défaut raisonnable, sauf indication contraire;

Pour l'instant, «un défaut raisonnable» est provisoirement défini comme « la valeur de la branche de suivi à distance que nous avons pour la référence de la télécommande en cours de mise à jour », et c'est une erreur si nous n'avons pas une telle branche de suivi à distance.

Donc, «bail» signifie:

" force-with-lease": Vous supposez que vous avez pris le bail sur l'arbitre lorsque vous avez récupéré pour décider de ce que devrait être l'historique rebasé, et vous ne pouvez repousser que si le bail n'a pas été rompu.

Les sources mentionnent encore "cas":

  • Cette option s'appelait à l'origine " cas" (pour "compare and swap"), le nom que personne n'aimait parce qu'il était trop technique.
  • La deuxième tentative l'a appelé "lockref" (parce que c'est conceptuellement comme pousser après avoir pris un verrou) mais le mot "lock" était détesté parce qu'il impliquait qu'il pouvait rejeter la poussée par d'autres, ce qui n'est pas la façon dont cette option fonctionne.
  • Cette ronde l'appelle "force-avec-bail".
    Vous supposez que vous avez pris le bail sur la référence lorsque vous avez récupéré pour décider ce que devrait être l'historique rebasé, et vous ne pouvez repousser que si le bail n'a pas été rompu.

Donc: " git push --force-with-leasevs. --force"

Comme je l'ai mentionné dans " push --force-with-leasepar défaut ", comme le mentionne Git 2.13 (Q2 2017), cette option --force-with-leasepeut être ignorée si un processus d'arrière-plan (comme ceux que vous trouvez dans un IDE avec un plugin Git) s'exécute git fetch origin.
Dans ce cas, --forceprévaut.

VonC
la source
29

git push --force est destructif car il écrase inconditionnellement le référentiel distant avec tout ce que l'on a localement. Le push --force de git est fortement déconseillé car il peut détruire d'autres commits déjà poussés vers un référentiel partagé. L'une des causes les plus courantes des poussées de force est lorsque nous sommes obligés de rebaser une branche.

Par exemple. Nous avons un projet avec une branche de fonctionnalités sur laquelle Alice et Bob vont travailler. Ils clonent tous les deux ce référentiel et commencent à travailler. Alice termine initialement sa partie de la fonctionnalité et la pousse vers le référentiel principal. Tout cela est bien et bon. Bob termine également son travail, mais avant de le pousser vers le haut, il remarque que certains changements ont été fusionnés dans master. Voulant garder un arbre propre, il effectue un rebase contre la branche maître. Bien sûr, quand il ira pousser cette branche rebasée, elle sera rejetée. Cependant, ne réalisant pas qu'Alice a déjà poussé son travail, il effectue un push -force. Malheureusement, cela effacera tous les enregistrements des modifications d'Alice dans le référentiel central.

Ce que fait --force-with-bail est de refuser de mettre à jour une branche à moins que ce ne soit l'état que nous attendons; c'est-à-dire que personne n'a mis à jour la branche en amont. En pratique, cela fonctionne en vérifiant que la référence en amont est ce à quoi nous nous attendons, car les références sont des hachages et encodent implicitement la chaîne de parents dans leur valeur.

Voici un bon article concernant git push --force et git push --force-with-bail.

Shakil
la source
2
ce que je ne comprends pas - si vous rebasez avec master, alors vous devriez pouvoir pousser sans --force-with-lease ? Pourquoi est-il --force-with-leasenécessaire après le rebasage avec master?
Alexander Mills
3
@AlexanderMills il peut y avoir une possibilité qui pose problème. Si vous êtes la dernière personne à pousser cela à maîtriser, pas de problème mais il y a une autre personne qui travaille avec une autre tâche, cela peut causer de sérieux problèmes. Dites d'abord que A - B - C était en maître, Au début, Alice fait A - B - C - D - E et en même temps Bob fait A - B - C - F - G. Si Alice est la dernière personne à pousser en maître après avoir rebasé A - B - C - D - E - F - G ne posera aucun problème mais une autre personne Tom essaiera de pousser A - B - C - D - E - R en même temps dans le désastre maître se produira !! !
Shakil
3
--forcene perd pas les commits, il les détache simplement. Ils peuvent être ressuscités par leurs hachages, comme dans git checkout <SHA>ou git reset --hard <SHA>, en supposant que le mainteneur du repo distant n'effectue aucune opération de GC ou d'élagage.
Keith Russell
1
"Le push --force de git est fortement déconseillé car il peut détruire d'autres commits déjà poussés vers un référentiel partagé" cette déclaration est fortement basée sur l'opinion. Faire une poussée forcée fait partie d'un flux de travail normal de révision de code git rebase. Comme mentionné déjà, vous pouvez récupérer les commits même après avoir fait cela.
Étienne le
9

En supposant que tous les hooks de pré-réception sur le serveur acceptent le push, cela réussira toujours:

git push --force

Alors que cela exécute une vérification côté client spécifique avant de continuer:

git push --force-with-lease

Vous pouvez exécuter la vérification spécifique vous-même manuellement. Voici l'algorithme de "vérification de bail":

  1. Déterminez votre branche actuelle.

  2. Courez git for-each-ref refs/remotes. Prenez note du commit-id que votre client git pense qu'il correspond à l'état en amont de votre branche actuelle.

Par exemple, si vous êtes sur la branche "foo", notez le commit-id associé à "refs / remotes / origin / foo".

  1. Déterminez maintenant le commit-id réel de la branche distante sur le serveur git en amont.

  2. Ne laissez le "git push" se poursuivre que si les commit-ids que vous avez extraits de l'étape 2 et de l'étape 3 sont d'accord. En d'autres termes, ne procédez que si la notion d'amont de votre clone git local correspond à l'amont réel.

Il y a une triste implication ici: puisque git fetchmet à jour toutes les références sous "refs / remotes / origin / *" vers leurs dernières versions, cette combinaison de commandes est essentiellement identique à git push --force:

git fetch

# The command below behaves identically to "git push --force"
# if a "git fetch" just happened!

git push --force-with-lease

Pour contourner cette faiblesse inhérente à git push --force-with-leasej'essaye de ne jamais courir git fetch. Au lieu de cela, je cours toujours git pull --rebasechaque fois que j'ai besoin de synchroniser avec l'amont, car git pullne met à jour qu'une seule référence sous refs / télécommandes, en gardant le «bail» d' --force-with-leaseutile.

G. Sylvie Davies
la source
1
si le contrôle est côté client, existe-t-il un potentiel de condition de concurrence? c'est-à-dire que quelqu'un d'autre pousse les changements après la vérification mais avant la poussée forcée?
craq
2

La force avec bail n'est pas nécessairement sûre. Cela fonctionne comme l'a dit Sylvie. Une note: dans git, une branche n'est qu'un pointeur sur un commit. Et les commits pointent vers zéro ou plusieurs commits parent. Même si vous avez entièrement changé la branche avec une réinitialisation matérielle et une poussée forcée ou une poussée avec - - force-avec-bail sans le vouloir, ce n'est pas nécessairement un gros problème. Vous pouvez utiliser votre git reflog local pour voir comment votre conseil local sur les branches (Où était HEAD à ce moment-là?) A changé et réinitialisé et repousser la branche. Ensuite, vous ne perdez que les nouveaux commits sur la branche distante, mais ils peuvent même être restaurés par les membres de l'équipe.

Poisson
la source