J'ai un collègue qui prétend que git pull
c'est nocif et s'énerve chaque fois que quelqu'un l'utilise.
La git pull
commande semble être le moyen canonique de mettre à jour votre référentiel local. Est-ce que l'utilisation git pull
crée des problèmes? Quels problèmes cela crée-t-il? Existe-t-il un meilleur moyen de mettre à jour un référentiel git?
git pull --rebase
et définir cette stratégie par défaut pour les nouvelles branchesgit config branch.autosetuprebase
--rebase
indicateur pourgit pull
synchroniser le local avec le distant, puis rejoue vos modifications locales en plus du local mis à jour. Ensuite, lorsque vous appuyez sur, tout ce que vous faites est d'ajouter vos nouveaux validations à la fin de la télécommande. Assez simple.Réponses:
Sommaire
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
il est facile de ne pas penser à la façon dont vos modifications pourraient être affectées par les modifications entrantes.La
git pull
commande est sûre tant qu'elle n'effectue que des fusions rapides. Sigit pull
est configuré pour effectuer uniquement des fusions d'avance rapide et lorsqu'une fusion d'avance rapide n'est pas possible, Git se terminera avec une erreur. Cela vous donnera l'occasion d'étudier les validations entrantes, de réfléchir à la façon dont elles pourraient affecter vos validations locales et de décider de la meilleure solution (fusionner, rebaser, réinitialiser, etc.).Avec Git 2.0 et plus récent, vous pouvez exécuter:
pour modifier le comportement par défaut pour une avance rapide uniquement. Avec les versions de Git entre 1.6.6 et 1.9.x, vous devrez prendre l'habitude de taper:
Cependant, avec toutes les versions de Git, je recommande de configurer un
git up
alias comme celui-ci:et en utilisant
git up
au lieu degit pull
. Je préfère cet alias àgit pull --ff-only
parce que:origin/*
branches qui n'existent plus en amont.Problèmes avec
git pull
git pull
n'est pas mauvais s'il est utilisé correctement. Plusieurs modifications récentes de Git ont facilité son utilisationgit pull
, mais malheureusement le comportement par défaut d'un plaingit pull
pose plusieurs problèmes:git pull
Ces problèmes sont décrits plus en détail ci-dessous.
Histoire non linéaire
Par défaut, la
git pull
commande équivaut à l'exécutiongit fetch
suivie degit merge @{u}
. S'il y a des validations non poussées dans le référentiel local, la partie de fusion degit 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 temps et un lieu pour les fusions, mais comprendre quand les fusions doivent et ne doivent pas être utilisées peut améliorer l'utilité de votre référentiel.
Notez que le but de Git est de faciliter le partage et la consommation de l'évolution d'une base de code, et non d'enregistrer précisément l'historique exactement tel qu'il s'est déroulé. (Si vous êtes en désaccord, considérez la
rebase
commande et pourquoi elle a été créée.) Les validations de fusion créées pargit pull
ne transmettent pas de sémantique utile aux autres: elles disent simplement que quelqu'un d'autre a poussé vers le référentiel avant que vous ayez terminé vos modifications. Pourquoi ces fusions sont-elles commises si elles n'ont pas de sens pour les autres et pourraient être dangereuses?Il est possible de configurer
git pull
pour rebaser au lieu de fusionner, mais cela a aussi des problèmes (discuté plus loin). Au lieu de cela,git pull
doit être configuré pour effectuer uniquement des fusions à avance rapide.Réintroduction des engagements remaniés
Supposons que quelqu'un rebase une branche et que la force la pousse. Cela ne devrait généralement pas se produire, mais c'est parfois nécessaire (par exemple, pour supprimer un fichier journal de 50 Go qui a été accidentellement validé et poussé). La fusion effectuée par
git pull
fusionnera la nouvelle version de la branche en amont dans l'ancienne version qui existe toujours dans votre référentiel local. Si vous poussez le résultat, les fourches et les torches commenceront à venir.Certains diront que le vrai problème est de forcer les mises à jour. Oui, il est généralement conseillé d'éviter autant que possible les poussées de force, mais elles sont parfois inévitables. Les développeurs doivent être prêts à gérer les mises à jour forcées, car elles se produisent parfois. Cela signifie ne pas fusionner aveuglément dans les anciens commits via un ordinaire
git pull
.Modifications du répertoire de travail surprise
Il n'y a aucun moyen de prédire à quoi ressemblera le répertoire de travail ou l'index jusqu'à ce qu'il
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 journal de 50 Go dans votre répertoire de travail parce que quelqu'un l'a poussé accidentellement, il peut renommer un répertoire dans lequel vous travaillez, etc.git remote update -p
(ougit fetch --all -p
) vous permet de consulter les validations des autres avant de décider de fusionner ou de rebaser, ce qui vous permet d'élaborer un plan avant de prendre des mesures.Difficulté à revoir les engagements des autres
Supposons que vous êtes en train de faire des changements et que quelqu'un d'autre veuille que vous passiez en revue certains commits qu'ils viennent de pousser.
git pull
L'opération de fusion (ou de rebase) de 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 pourriez utiliser
git stash
et ensuitegit pull
, mais que faites-vous lorsque vous avez terminé la révision? Pour revenir à l'endroit où vous vous trouviez, vous devez annuler la fusion créée pargit pull
et appliquer la cachette.git remote update -p
(ougit fetch --all -p
) ne modifie pas le répertoire de travail ou l'index, il est donc sûr de l'exécuter à tout moment, même si vous avez des modifications par étapes et / ou par étapes. Vous pouvez suspendre ce que vous faites et revoir 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 flexibilité.Rebasage sur une branche distante
Un modèle d'utilisation Git courant consiste à effectuer un
git pull
pour apporter les dernières modifications, suivi d'ungit rebase @{u}
pour éliminer le commit de fusiongit pull
introduit. Il est assez courant que Git a quelques options de configuration pour réduire ces deux étapes à une seule étape en disantgit pull
d'effectuer un rebasage au lieu d'une fusion (voirbranch.<branch>.rebase
,branch.autosetuprebase
etpull.rebase
options).Malheureusement, si vous avez un commit de fusion non poussé que vous souhaitez conserver (par exemple, un commit fusionnant une branche de fonctionnalité poussée dans
master
), ni un rebase-pull (git pull
avecbranch.<branch>.rebase
défini surtrue
) ni un merge-pull (legit pull
comportement par défaut ) suivi d'un rebase fonctionnera. En effet,git rebase
élimine les fusions (il linéarise le DAG) sans--preserve-merges
option. L'opération de rebase-pull ne peut pas être configurée pour conserver les fusions, et un merge-pull suivi d'ungit rebase -p @{u}
ne éliminera pas la fusion provoquée par le merge-pull. Mise à jour: Git v1.8.5 ajoutégit pull --rebase=preserve
etgit config pull.rebase preserve
. Celles-ci entraînentgit pull
une opérationgit rebase --preserve-merges
après avoir récupéré les validations en amont. (Merci à funkaster pour le heads-up!)Nettoyage des branches supprimées
git pull
ne taille pas les branches de suivi à distance correspondant aux branches qui ont été supprimées du référentiel distant. Par exemple, si quelqu'un supprime une branchefoo
du référentiel distant, vous verrez toujoursorigin/foo
.Cela conduit les utilisateurs à ressusciter accidentellement des branches tuées car ils pensent qu'ils sont toujours actifs.
Une meilleure alternative: utiliser
git up
au lieu degit pull
Au lieu de
git pull
, je recommande de créer et d'utiliser l'git up
alias suivant :Cet alias télécharge toutes les dernières validations de toutes les branches en amont (élagage des branches mortes) et tente d'avancer rapidement la branche locale vers la dernière validation de la branche en amont. En cas de succès, il n'y avait pas de commits locaux, il n'y avait donc aucun risque de conflit de fusion. L'avance rapide échouera s'il y a des validations locales (non poussées), ce qui vous donne la possibilité de revoir 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 aucun changement local. Contrairement à
git pull
,git up
ne vous déposera jamais à une invite vous demandant de résoudre un conflit de fusion.Une autre option:
git pull --ff-only --all -p
Ce qui suit est une alternative à l'
git up
alias ci-dessus :Cette version de
git up
a le même comportement que l'git up
alias précédent , sauf:-p
argument, qui est passé àfetch
) qui pourrait changer dans les futures versions de GitSi vous utilisez Git 2.0 ou une version plus récente
Avec Git 2.0 et plus récent, vous pouvez configurer
git pull
pour ne faire que des fusions à avance rapide par défaut:Cela provoque un
git pull
comportement similairegit pull --ff-only
, mais il ne récupère toujours pas toutes les validations en amont ni ne nettoie les anciennesorigin/*
branches, donc je préfère toujoursgit up
.la source
git remote update -p
est équivalent àgit fetch --all -p
. Je suis l'habitude de tapergit remote update -p
parce que une foisfetch
ne pas l'-p
option. Concernant l'interligne!
, voir la description dealias.*
dansgit help config
. Il dit: "Si l'extension d'alias est précédée d'un point d'exclamation, elle sera traitée comme une commande shell."pull.ff
configuration qui semble réaliser la même chose, sans alias.git up
ressemble à une alternative intéressante.git pull
.Ma réponse, tirée de la discussion qui a surgi sur HackerNews:
Je me sens tenté de répondre à la question en utilisant la loi Betteridge des titres: pourquoi est-ce
git pull
considéré comme dangereux? Ça ne l'est pas.la source
git pull
. Cependant, cela pourrait entrer en conflit avec certaines pratiques nuisibles, comme vouloir réécrire l'histoire plus que ce qui est strictement nécessaire. Mais git est flexible, donc si vous voulez l'utiliser d'une manière différente, faites-le par tous les moyens. Mais c'est parce que vous (enfin, @Richard Hansen) voulez faire quelque chose d'inhabituel dans git, et non pas parce quegit pull
c'est dangereux.git rebase
et considèrentgit pull
nuisibles? Vraiment?git pull
sans l'--rebase
option est la direction de fusion qu'elle crée. Lorsque vous regardez la différence, toutes les modifications de cette fusion appartiennent désormais à la personne qui a tiré, plutôt qu'à la personne qui a apporté les modifications. J'aime un flux de travail où la fusion est réservée à deux branches distinctes (A -> B), donc la validation de la fusion est claire sur ce qui a été introduit, et le rebasage est réservé à la mise à jour sur la même branche (distant A -> local A )Il n'est pas considéré comme dangereux si vous utilisez correctement Git. Je vois comment 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 source
git pull
n'est pas un problème. Branchement dans git est bon marché.La réponse acceptée revendique
mais à partir de Git 1.8.5 , qui postdate cette réponse, vous pouvez le faire
ou
ou
Les docs disent
Cette discussion précédente contient des informations et des diagrammes plus détaillés: git pull --rebase --preserve-merges . Cela explique également pourquoi
git pull --rebase=preserve
n'est pas le même quegit pull --rebase --preserve-merges
, ce qui ne fait pas la bonne chose.Cette autre discussion précédente explique ce que fait réellement la variante conserve-fusionne de rebase, et comment elle est beaucoup plus complexe qu'un rebase ordinaire: que fait exactement "rebase --preserve-merges" de git (et pourquoi?)
la source
Si vous allez dans l'ancien dépôt git, git l'alias qu'ils suggèrent est différent. https://github.com/aanand/git-up
git config --global alias.up 'pull --rebase --autostash'
Cela fonctionne parfaitement pour moi.
la source