J'ai un collègue qui prétend que git pull
est nocif et se fâche lorsque quelqu'un l'utilise.
La commande git pull
semble être le moyen canonique de mettre à jour votre référentiel local. L'utilisation de git pull
crée-t-elle des problèmes? Quels problèmes crée-t-il? Existe-t-il un meilleur moyen de mettre à jour un référentiel git?
Par défaut, git pull
crée des validations de fusion qui ajoutent du bruit et de la complexité à l'historique du code. De plus, pull
permet de ne pas trop penser aux conséquences que vos modifications pourraient avoir sur vos modifications.
La commande git pull
est sécurisée tant qu'elle effectue uniquement des fusions en avance rapide. Si git pull
est configuré pour effectuer uniquement des fusions en avance rapide et lorsqu'une fusion en avance rapide n'est pas possible, alors Git se fermera avec une erreur. Cela vous donnera l'occasion d'étudier les commits entrants, de réfléchir à la manière dont ils pourraient affecter vos commits locaux et de décider du meilleur plan d'action (fusion, refonte, réinitialisation, etc.).
Avec Git 2.0 et plus récent, vous pouvez exécuter:
git config --global pull.ff only
pour modifier le comportement par défaut uniquement en avance rapide. Avec les versions de Git comprises entre 1.6.6 et 1.9.x, vous devrez prendre l’habitude de taper:
git pull --ff-only
Cependant, avec toutes les versions de Git, je vous recommande de configurer un alias git up
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
parce que:
Origin/*
branches qui n'existent plus en amont.git pull
git pull
n'est pas mauvais s'il est utilisé correctement. Plusieurs modifications récentes de Git ont rendu plus facile l'utilisation de git pull
correctement, mais malheureusement, le comportement par défaut d'un simple git pull
pose plusieurs problèmes:
git pull
Ces problèmes sont décrits plus en détail ci-dessous.
Par défaut, la commande git pull
équivaut à exécuter git fetch
suivi de git merge @{u}
. S'il existe des validations non traitées dans le référentiel local, la partie fusion de git pull
crée une validation de fusion.
Les commits de fusion n'ont rien de intrinsèquement mauvais, mais ils peuvent être dangereux et doivent être traités avec respect:
Bien sûr, il y a un moment et un lieu pour les fusions, mais comprendre quand et où fusionner devrait ou ne devrait pas être utilisé peut améliorer l'utilité de votre référentiel.
Notez que le but de Git est de faciliter le partage et l'utilisation de l'évolution d'une base de code, et non d'enregistrer avec précision l'historique tel qu'il s'est déroulé. (Si vous êtes en désaccord, considérez la commande rebase
et la raison de sa création.) Les validations de fusion créées par git pull
ne transmettent pas de sémantique utile aux autres. Elles indiquent simplement que quelqu'un d'autre est arrivé. Push to the repository avant d'avoir terminé vos modifications. Pourquoi ces commits de fusion s’ils n’ont pas de sens pour les autres et peuvent être dangereux?
Il est possible de configurer git pull
de manière à rebaser au lieu de fusionner, mais cela pose également des problèmes (voir plus loin). Au lieu de cela, git pull
devrait être configuré pour effectuer uniquement des fusions à avance rapide.
Supposons que quelqu'un rebase une branche et la force la pousse. Cela ne devrait généralement pas se produire, mais il est parfois nécessaire (par exemple, de supprimer un fichier journal de 50 Go qui a été validé et poussé par inadvertance). La fusion effectuée par git pull
fusionnera la nouvelle version de la branche en amont avec l'ancienne version qui existe toujours dans votre référentiel local. Si vous poussez le résultat, les fourches et les flambeaux commenceront à venir.
Certains pourraient soutenir que le vrai problème est la mise à jour forcée. Oui, il est généralement conseillé d'éviter les poussées de force chaque fois que possible, mais elles sont parfois inévitables. Les développeurs doivent être prêts à gérer les mises à jour de force, car elles se produiront parfois. Cela signifie ne pas fusionner aveuglément les anciens commits via un git pull
ordinaire.
Il n'y a aucun moyen de prédire à quoi ressemblera le répertoire de travail ou l'index 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, cela pourrait introduire un fichier journal 50GiB dans votre répertoire de travail parce que quelqu'un l'a poussé accidentellement, cela pourrait renommer un répertoire dans lequel vous travaillez, etc.
git remote update -p
(ou git fetch --all -p
) vous permet de consulter les validations d'autrui avant de décider de fusionner ou de rebaser, ce qui vous permet de créer un plan avant de prendre des mesures.
Supposons que vous soyez en train de faire des changements et que quelqu'un d'autre veuille que vous examiniez certains commits qu'ils ont simplement envoyés. L'opération de fusion (ou de refonte) de git pull
modifie le répertoire de travail et l'index, ce qui signifie que votre répertoire de travail et votre index doivent être propres.
Vous pouvez utiliser git stash
puis git pull
, mais que faites-vous lorsque vous avez terminé votre révision? Pour revenir à votre position actuelle, vous devez annuler la fusion créée par git pull
et appliquer la stash.
git remote update -p
(ou git fetch --all -p
) ne modifie pas le répertoire de travail ni l'index, de sorte que son exécution est sûre à tout moment, même si vous avez effectué des modifications planifiées et/ou non mises en scène. Vous pouvez mettre en pause ce que vous faites et consulter le commit de quelqu'un d'autre sans vous soucier de cacher ou de terminer le commit sur lequel vous travaillez. git pull
ne vous donne pas cette souplesse.
Un modèle d'utilisation courant de Git consiste à faire un git pull
pour intégrer les dernières modifications suivies d'un git rebase @{u}
pour éliminer la validation de fusion introduite par git pull
. Il est assez courant que Git ait quelques options de configuration pour réduire ces deux étapes à une seule étape en demandant à git pull
d’effectuer une rebase au lieu d’une fusion (voir le branch.<branch>.rebase
, branch.autosetuprebase
et pull.rebase
options).
Malheureusement, si vous souhaitez conserver une validation de fusion non validée (par exemple, une validation fusionnant une branche de fonctionnalité injectée dans master
), ni un rebase-pull (git pull
avec branch.<branch>.rebase
set to true
) ni une fusion-extraction (le comportement par défaut de git pull
_) suivie d'un rebase fonctionnera. En effet, git rebase
élimine les fusions (il linéarise le DAG) sans l'option --preserve-merges
. L'opération d'extraction de base ne peut pas être configurée pour conserver les fusions et une fusion d'extraction suivie d'un git rebase -p @{u}
n'éliminera pas la fusion provoquée par l'extraction de fusion. Mise à jour: Git v1.8.5 ajouté git pull --rebase=preserve
et git config pull.rebase preserve
. Ceci provoque git pull
à faire git rebase --preserve-merges
après avoir récupéré les commits en amont. (Merci à funkaster pour le heads-up!)
git pull
ne supprime pas les branches de suivi distantes correspondant aux branches supprimées du référentiel distant. Par exemple, si quelqu'un supprime la branche foo
du référentiel distant, vous verrez toujours Origin/foo
.
Cela conduit les utilisateurs à ressusciter accidentellement des branches mortes, car ils pensent être toujours actifs.
git up
au lieu de git pull
Au lieu de git pull
, je vous recommande de créer et d'utiliser l'alias git up
suivant:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
Cet alias télécharge tous les derniers commits de toutes les branches en amont (élagage des branches mortes) et tente d'avancer rapidement la branche locale vers la dernière validation sur la branche en amont. En cas de succès, il n'y a pas d'engagement local, il n'y a donc pas de risque de fusion. L'avance rapide échouera s'il existe des validations locales (non impulsées), ce qui vous donne l'occasion d'examiner les validations en amont avant de prendre des mesures.
Cela modifie toujours votre répertoire de travail de manière imprévisible, mais uniquement si vous n'avez aucune modification locale. Contrairement à git pull
, git up
ne vous laissera jamais tomber dans une invite qui vous demandera de résoudre un conflit de fusion.
git pull --ff-only --all -p
Voici une alternative à l'alias git up
ci-dessus:
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 alias git up
, sauf que:
-p
, transmise à fetch
), susceptible de changer dans les futures versions de GitAvec Git 2.0 et les versions plus récentes, vous pouvez configurer git pull
pour n'effectuer que des fusions en avance rapide par défaut:
git config --global pull.ff only
Cela fait que git pull
se comporte comme git pull --ff-only
, mais il ne récupère toujours pas tous les commits en amont ni ne nettoie les anciennes branches Origin/*
, aussi je préfère toujours git up
.
Ma réponse, tirée de la discussion qui est survenue sur HackerNews:
Je suis tenté de simplement répondre à la question en utilisant la loi de Betteridge: Pourquoi est-ce que _git pull
_ est considéré comme dangereux? Ce n'est pas.
Ce n'est pas considéré comme dangereux si vous utilisez correctement Git. Je vois en quoi cela vous affecte négativement compte tenu de votre cas d'utilisation, mais vous pouvez éviter les problèmes simplement en ne modifiant pas l'historique partagé.
La réponse acceptée prétend
L'opération rebase-pull ne peut pas être configurée pour conserver les fusions
mais à partir de Git 1.8.5 , qui postdate cette réponse, vous pouvez le faire
git pull --rebase=preserve
ou
git config --global pull.rebase preserve
ou
git config branch.<name>.rebase preserve
Le docs dire
Lorsque
preserve,
transmet également--preserve-merges
à 'git rebase' afin que les commits de fusion validés localement ne soient pas aplatis par l'exécution de 'git pull'.
Cette discussion précédente contient des informations plus détaillées et des diagrammes: git pull --rebase --preserve-merges . Cela explique également pourquoi git pull --rebase=preserve
n'est pas la même chose que git pull --rebase --preserve-merges
, qui ne fait pas ce qui est bien.
Cette autre discussion précédente explique ce que fait la variante de préservation et de fusion de la base, et en quoi elle est beaucoup plus complexe que celle de base: Que fait exactement la "base" de réserve - fusion de git) (et pourquoi?). )
Si vous allez dans l'ancien dépôt git , git up , le pseudonyme suggéré par ceux-ci est différent. https://github.com/aanand/git-up
git config --global alias.up 'pull --rebase --autostash'
Cela fonctionne parfaitement pour moi.