Dans quels cas "git pull" pourrait-il être nuisible?

j'ai un collègue qui prétend que git pull est nuisible, et se fâche chaque fois que quelqu'un l'utilise.

la commande git pull semble être la façon canonique de mettre à jour votre dépôt local. L'utilisation de git pull crée-t-elle des problèmes? Quels sont les problèmes qu'il crée? Y a-t-il une meilleure façon de mettre à jour un dépôt git?

393
demandé sur Max 2013-03-10 02:23:10

4 réponses

résumé

par défaut, git pull crée des propagations de fusion qui ajoutent du bruit et de la complexité à l'histoire du code. En outre, pull rend facile de ne pas penser à la façon dont vos changements pourraient être affectés par les changements entrants.

la commande git pull est sûre tant qu'elle n'effectue que des fusions rapides. Si git pull est configuré pour ne faire que des fusions fast-forward et qu'une fusion fast-forward n'est pas possible, alors Git va sortie avec une erreur. Cela vous donnera l'occasion d'étudier les entrants s'engage, pensez à comment ils pourraient affecter votre local s'engage, et de décider du meilleur plan d'action (fusion, rebase, reset, etc).

avec Git 2.0 et plus récent, vous pouvez lancer:

git config --global pull.ff only

pour modifier le comportement par défaut à fast-forward. Avec des versions Git entre 1.6.6 et 1.9.x vous devez prendre l'habitude de taper:

git pull --ff-only

cependant, avec toutes les versions de Git, je recommande de configurer un git up alias comme ceci:

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

et en utilisant git up au lieu de git pull . Je préfère cet alias à git pull --ff-only car:

  • il fonctionne avec toutes les versions (non-antiques) de Git,
  • il récupère toutes les branches en amont (pas seulement la branche sur laquelle vous travaillez actuellement), et
  • il nettoie sur les vieilles branches origin/* qui n'existent plus en amont.

Problèmes avec les git pull

git pull n'est pas mauvais s'il est utilisé correctement. Plusieurs modifications récentes à Git ont rendu plus facile d'utiliser git pull correctement, mais malheureusement le comportement par défaut d'un simple git pull a plusieurs problèmes:

  • il introduit des non-linéarités inutiles dans l'histoire
  • il est facile de réintroduire accidentellement des commit qui ont été intentionnellement rebasés en amont
  • , il modifie votre répertoire de travail de manière imprévisible
  • interrompre ce que vous faites pour passer en revue le travail de quelqu'un d'autre est ennuyeux avec git pull
  • il est difficile de rebaser correctement sur la branche distante
  • il ne nettoie pas les branches qui ont été supprimées dans le rapport à distance

Ces problèmes sont décrits plus en détail ci-dessous.

Histoire Non Linéaire

par défaut, la commande git pull est équivalente à git fetch suivie de git merge @{u} . S'il y a des propagations non traitées dans le dépôt local, la partie fusion de git pull crée une propagation de fusion.

il n'y a rien de mal à fusionner les commits, mais ils peuvent être dangereux et doit être traité avec respect:

  • Fusionner les commits sont par nature difficiles à examiner. Pour comprendre ce que fait une fusion, vous devez comprendre les différences à tous les parents. Une différence conventionnelle ne transmet pas bien cette information multidimensionnelle. En revanche, une série de commits normaux est facile à réviser.
  • fusionner la résolution des conflits est délicate, et les erreurs passent souvent inaperçues pendant longtemps parce que la fusion les commits sont difficiles à examiner.
  • Les fusions
  • peuvent tranquillement supplanter les effets des commits réguliers. Le code n'est plus la somme des propagations incrémentielles, conduisant à des malentendus sur ce qui a réellement changé.
  • Fusion s'engage peut perturber certains continue des dispositifs d'intégration (par exemple, auto-construire le premier chemin parent en vertu de l'hypothèse de la convention que la seconde parents point incomplètes travaux en cours).

bien sûr, il y a un temps et un lieu pour les fusions, mais comprendre quand les fusions devraient et ne devraient pas être utilisées peut améliorer l'utilité de votre dépôt.

notez que le but de Git est de faciliter le partage et la consommation de l'évolution d'une base de données codée, et non d'enregistrer avec précision l'histoire telle qu'elle s'est déroulée. (Si vous n'êtes pas d'accord, considérez la commande rebase et pourquoi elle a été créée.) La fusion des propagations créées par git pull n'est pas utile la sémantique pour les autres-ils disent juste que quelqu'un d'autre a poussé au dépôt avant que vous ayez fini avec vos changements. Pourquoi ceux fusion s'engage si elles ne sont pas significatives pour les autres et peut-être dangereux?

il est possible de configurer git pull pour rebaser au lieu de fusionner, mais cela a aussi des problèmes (discutés plus loin). Au lieu de cela, git pull devrait être configuré pour ne faire que des fusions rapides.

réintroduction de Rebased-out S'engage

supposons que quelqu'un rebase une branche et la pousse de force. Cela ne devrait généralement pas se produire, mais il est parfois nécessaire (par exemple, pour supprimer un fichier journal de 50GiB qui a été accidentellement comit é et poussé). La fusion faite par git pull va fusionner la nouvelle version de la branche amont dans l'ancienne version qui existe encore dans votre dépôt local. Si vous poussez le résultat, les fourches et les torches commenceront à venir votre chemin.

certains mai soutenez que le vrai problème est les mises à jour de force. Oui, il est généralement conseillé d'éviter les coups de force dans la mesure du possible, mais ils sont parfois inévitables. Les développeurs doivent être prêts à gérer les mises à jour de force, car elles se produisent parfois. Cela signifie qu'il ne faut pas fusionner aveuglément dans les anciennes commit via un git pull ordinaire .

Modification Surprise Du Répertoire De Travail

il n'y a aucun moyen de prédire ce que le répertoire ou l'index de travail sera jusqu'à ce que git pull soit terminé. Il peut y avoir des conflits de fusion que vous devez résoudre avant de pouvoir faire quoi que ce soit d'autre, il peut introduire un fichier log de 50GiB dans votre répertoire de travail parce que quelqu'un l'a accidentellement poussé, il peut renommer un répertoire dans lequel vous travaillez, etc.

git remote update -p (ou git fetch --all -p ) vous permet de regarder les commits d'autres personnes avant de décider de fusionner ou de reformater, vous permettant de former un plan avant de prendre des mesures.

de la Difficulté de l'Examen d'Autres Personnes s'Engage

supposez que vous êtes au milieu de faire quelques changements et quelqu'un d'autre veut que vous révisiez certains commits qu'ils viennent de pousser. L'opération git pull ' s merge (or rebase) modifie le répertoire de travail et l'index, ce qui signifie que votre répertoire de travail et l'index doivent être propres.

vous pouvez utiliser git stash et ensuite git pull , mais que faites-vous quand vous avez terminé la révision? Pour revenir là où vous étiez, vous devez annuler la fusion créée par git pull et appliquer la cachette.

git remote update -p (ou git fetch --all -p ) ne modifie pas le répertoire de travail ou l'index, de sorte qu'il est sûr d'exécuter à tout moment-même si vous avez mis en scène et/ou non des changements. Vous pouvez mettre en pause ce que vous faites et passer en revue la Commission de quelqu'un d'autre sans vous soucier de cacher ou de finir la Commission sur laquelle vous travaillez. git pull ne vous donne pas cette flexibilité.

Rebasage sur une Branche Distante

un schéma d'usage courant de Git est de faire un git pull pour apporter les dernières modifications suivi d'un git rebase @{u} pour éliminer la commit de fusion que git pull introduit. Il est assez courant que Git ait quelques options de configuration pour réduire ces deux étapes à une seule étape en disant git pull pour effectuer un rebase au lieu d'une fusion (voir les options branch.<branch>.rebase , branch.autosetuprebase , et pull.rebase ).

malheureusement, si vous avez un commit de fusion non poussé que vous voulez préserver (par exemple, un commit fusionnant une branche de fonction poussée dans master ), ni un rebase-pull ( git pull avec branch.<branch>.rebase défini à true ) ni un merge-pull (le comportement par défaut git pull ) suivi d'un rebase fonctionnera. Cela est dû au fait que git rebase élimine les fusions (il linéarise le DAG) sans l'option --preserve-merges . L'opération rebase-pull ne peut pas être configurée pour préserver fusionne, et une opération de fusion-pull suivie par un git rebase -p @{u} ne suffit pas à éliminer la fusion provoquée par la fusion-pull. Update: Git v1.8.5 ajouté git pull --rebase=preserve et git config pull.rebase preserve . Ceux-ci provoquent git pull à faire git rebase --preserve-merges après avoir récupéré les commits en amont. (Merci à funkaster pour le heads-up!)

Nettoyage De Branches Supprimées

git pull ne supprime pas les branches de suivi à distance correspondant aux branches qui ont été supprimées du dépôt distant. Par exemple, si quelqu'un supprime la branche foo de la prise en pension à distance, vous verrez toujours origin/foo .

cela conduit à des utilisateurs ressuscitant accidentellement des branches tuées parce qu'ils pensent qu'elles sont toujours actives.

une meilleure Alternative: utiliser git up au lieu de git pull

au lieu de git pull , je recommande la création et l'utilisation la mention git up alias:

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

ce pseudonyme télécharge toutes les dernières propagations de toutes les branches amont (élagage des branches mortes) et tente de faire avancer la branche locale vers la dernière propagation sur la branche amont. En cas de succès, il n'y avait pas de commit local, donc il n'y avait pas de risque de conflit de fusion. Le fast-forward échouera s'il y a des commits locaux (non jetés), vous donnant l'occasion de revoir les commits amont avant de prendre action.

ceci modifie toujours votre répertoire de travail de manière imprévisible, mais seulement si vous n'avez pas de modifications locales. À la différence de git pull , git up ne vous laissera jamais tomber à une invite s'attendant à ce que vous corrigiez un conflit de fusion.

Une Autre Option: git pull --ff-only --all -p

ce qui suit est une alternative à la ci-dessus git up alias:

git config --global alias.up 'pull --ff-only --all -p'

cette version de git up a le même comportement que le précédent git up alias, sauf:

  • le message d'erreur est un peu plus cryptique si votre branche locale n'est pas configurée avec une branche amont
  • il s'appuie sur une caractéristique non documentée (l'argument -p , qui est passé à fetch ) qui peut changer dans les versions futures de Git

si vous utilisez Git 2.0 ou plus récent

Avec Git 2.0 et plus récente, vous pouvez configurer git pull pour ne faire des fusions fast-forward que par défaut:

git config --global pull.ff only

Cela provoque des git pull agir comme git pull --ff-only , mais elle n'est pas toujours chercher tous en amont s'engage ou nettoyer vieux origin/* branches donc je préfère encore git up .

530
répondu Richard Hansen 2014-11-07 15:04:20

ma réponse, tirée de la discussion que surgit sur HackerNews:

je me sens tenté de répondre simplement à la question en utilisant la loi Betteridge des gros titres: pourquoi git pull est considéré comme nocif? Ce n'est pas le cas.

  • les non-linéarités ne sont pas intrinsèquement mauvaises. S'ils représentent l'histoire réelle ils sont ok.
  • réintroduction accidentelle de commits rebasé en amont est le résultat d'une réécriture erronée de l'histoire en amont. Vous ne pouvez pas réécrire l'histoire, quand l'histoire est répliquée sur plusieurs titres.
  • modifier le répertoire de travail est un résultat attendu; d'une utilité discutable, notamment face au comportement de hg/monotone/darcs/other_dvcs_predating_git, mais là encore pas intrinsèquement mauvais.
  • S'arrêter pour passer en revue les travaux des autres est nécessaire pour une fusion, et est encore une fois un comportement attendu sur git pull. Si vous ne si vous ne voulez pas fusionner, vous devriez utiliser git fetch. Encore une fois, il s'agit d'une idiosyncrasie de git par rapport aux dvcs populaires précédents, mais c'est un comportement attendu et pas intrinsèquement mauvais.
  • c'est bien de rendre difficile de rebaser contre une branche éloignée. Ne réécrivez pas l'histoire sauf si vous en avez absolument besoin. Je ne peux pas pour la vie de moi comprendre cette poursuite d'une (fausse) histoire linéaire
  • ne pas nettoyer les branches est bon. Chaque agent sait ce qu'il veut garder. Git n'a aucune notion des relations maître-esclave.
194
répondu Sérgio Carvalho 2014-03-12 16:11:53

ce n'est pas considéré comme nocif si vous utilisez Git correctement. Je vois comment il vous affecte négativement compte tenu de votre cas d'utilisation, mais vous pouvez éviter les problèmes simplement en ne modifiant pas l'histoire partagée.

26
répondu Hunt Burdick 2014-03-14 16:11:45

les revendications de réponse acceptées

l'opération rebase-pull ne peut pas être configurée pour préserver les fusions

mais à partir de Git 1.8.5 , qui postdates cette réponse, vous pouvez faire

git pull --rebase=preserve

ou

git config --global pull.rebase preserve

ou

git config branch.<name>.rebase preserve

Le docs dire

Lorsque preserve, passe aussi --preserve-merges à "git rebase" de sorte que la fusion engagée localement ne sera pas aplatie en lançant "git pull".

Cette discussion précédente contient des informations plus détaillées et des schémas: git pull --rebase --preserve-fusionne . Cela explique aussi pourquoi git pull --rebase=preserve n'est pas la même chose que git pull --rebase --preserve-merges , ce qui ne fait pas la bonne chose.

cette autre discussion précédente explique qu'est-ce que la variante préserve-merges de rebase fait réellement, et comment elle est beaucoup plus complexe qu'un rebase régulier: Qu'est-ce que "rebase --preserve-merges" de git (et pourquoi?)

17
répondu Marc Liyanage 2017-05-23 11:33:15