Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Visual Studio Subversion et Git : passer à Git - Partie 3

Utiliser Git est devenu une sorte de passage obligé, autant que ce passage se passe dans de bonnes conditions… Le mieux est de comprendre Git et le changement de paradigme qu’il implique…

Récapitulatif

La Partie 1 a posé les bases du principal problème du passage de Subversion vers Git : ce n’est pas un problème technique ou de mémorisation de nouvelles procédures ou commandes, c’est le changement radical de paradigme sur la façon de gérer des sources. La proximité des produits (ils gèrent du code source et ses évolutions) rend difficile au départ la compréhension de ce glissement de point de vue dont on ne s’aperçoit pas forcément.

La Partie 2 complète le tour de ce qui est différent entre les deux produits pour mieux comprendre ce fameux point de vue différent de Git.

La présente partie 3 nous convie à la fin de ce tour d’horizon en regardant plus en détail certaines commande Git et comment s’en sortir lorsqu’on connaît Subversion car si avoir une connaissance pratique des différences entre Subversion et Git facilite la transition vers l’utilisation de Git le seul véritable moyen d'apprendre Git est d'utiliser Git !

Un développeur et un référentiel local

imageTravailler avec un référentiel Git local est assez simple car il n’y a pas de serveur à configurer. L'initialisation de Git dans un dossier créera un référentiel local à utiliser, même s'il existe des fichiers dans le dossier :
 
cd mon_projet
git init

 
Le résultat est un référentiel Git pleinement opérationnel, prêt à accepter des validations, etc. Avant de pouvoir faire un commit Git doit connaître les fichiers. Pour ce faire, vous ajoutez les fichiers à Git, ce qui revient à ajouter des fichiers à un référentiel Subversion. Git exige également que vous stagiez les fichiers avant de les valider, comme le montre la figure suivante . Heureusement, le processus d'ajout de fichiers à Git et de leur préparation pour la validation est identique. Par conséquent, si un nouveau fichier doit être ajouté, mis en scène (staged) et validé, il ne s'agit que d'un processus en deux étapes, identique à la mise en place et à la validation d'un fichier que Git suit déjà :

image 
Fichiers modifiés mais par encore stagés, ils ne peuvent être committés


git add .
git commit -m “mon premier commit”

L'appel de git add ajoutera tous les nouveaux fichiers et dossiers, et mettra en scène tous les fichiers et dossiers actuellement suivis, en les préparant pour la validation. La figure suivante montre l'état d'un référentiel avec des fichiers stagés et prêts pour la validation.
   

image
Fichiers stagés et prêts pour le commit
 

L'appel de git commit fonctionne de la même manière que d'appeler svn commit pour un référentiel de subversion. Dans le cas de Git, cela va valider les fichiers stagés. L' option “m” fournit un message pour la validation. La figure suivante montre les résultats de la validation, y compris la première partie de l'ID de validation et les modifications apportées. On notera que beaucoup de shells existent pour Git dont celui intégré à Visual Studio et que j’illustre ici les propos par les commandes consoles afin d’être le plus universel possible. Mais toutes ces commandes de base sont disponibles depuis VS ce qui facilitent la vie mais ne change rien à la logique de l’ensemble.

image   
Commiter les changements qui ont été stagés
 

Il existe également un raccourci pour placer et valider un fichier en une étape. Si des modifications sont apportées à des fichiers qui font déjà l'objet d'un suivi par Git (ils ont déjà été ajoutés au référentiel), alors appeler git commit avec l' option -a  stagera et validera les fichiers en une seule commande:
  git commit -a -m “mon message de commit”
Git permet également d'empiler les options, ce qui signifie que la commande précédente est équivalente à cette commande:
  git commit -am “mon message de commit”
Notez que ce raccourci ne fonctionnera pas pour les fichiers qui ne sont pas suivis par Git, ni pour les suppressions de fichiers. Pour gérer ces scénarios, vous devez organiser les modifications manuellement.

Histoire et branches dans un référentiel

imageAu fur et à mesure que les validations sont effectuées, Git enregistre les informations ainsi que les fichiers, y compris l'ID de validation et l'ascendance de la validation. Le travail peut être effectué directement sur la branche principale, où des branches peuvent être créées pour travailler sur quelque chose de spécifique, tel qu'une nouvelle fonctionnalité, une correction de bogue ou un autre sujet :
 


git branch maModif
git checkout maModif

 
Lorsque vous créez une branche, la nouvelle branche pointe immédiatement sur la même validation que la branche à partir de laquelle elle a été créée, comme illustré à la figure suivante

image 
nouvelle branche au même niveau de commit
 

Effectuer un checkout changera la copie de travail de la branche. Une fois qu'une branche a été extraite (checkout), des commits ou d'autres actions peuvent être effectués dessus.
Il existe un raccourci pour créer une branche et effectuer le checkout en même temps:
git checkout -b mytopic

Lorsque des commits sont faits dans la branche, mais qu’aucun autre n’a été fait sur le maître, Git continuera d’afficher le maître sur la même ligne de temps que celle présentée précédemment dans la Figure 8 . Ce n'est que lorsque le maître et la branche divergent - c'est-à-dire que les branches ont des commits différents – que Git les affichera sous forme de chronologie divergente, comme le montre la figure suivante :

image
Divergence d'historique
  

Comme Subversion, vous pouvez fusionner des branches pour appliquer les modifications d’une branche à une autre. Lorsque vous fusionnez la branche dans le maître, une ou deux choses peuvent arriver à la timeline. Si aucune validation n'a été faite sur le maître depuis la création de la branche, Git transfère par éfaut rapidement le maître au même commit que la branche. Si, toutefois, le maître et la branche ont divergé, alors Git appliquera les modifications apportées dans la branche au maître, ce qui donnera un nouveau commit qui représente la fusion :
 
git checkout master
git merge maModif
 
Comme la branche a été fusionnée dans la branche principale, seul le HEAD de la branche principale sera déplacé vers le nouveau commit, comme illustré à la figure suivante. Un développeur travaillant avec un référentiel local peut créer autant de branches qu'il le souhaite et les fusionner autant de fois qu'il le souhaite pour mener à bien son travail.
   

image
Merge de branches qui avaient divergé

Un développeur et un référentiel distant

Lorsque vous commencez à travailler à partir d'un référentiel distant et qu'il n'y a pas de copie locale existante, la première étape consiste à cloner la copie distante. Si le référentiel distant est hébergé via HTTP, vous pouvez le cloner directement à partir de l'URL:
git clone http://my.server.com/myproject.git
Cela créera une copie locale du référentiel hébergé à cette URL dans un dossier local appelé « myproject », le clone extraira le contenu de la branche maîtresse distante vers le référentiel local. Il va également configurer un Git Remote appelée origin . Origin est une convention de dénomination qui désigne le référentiel distant utilisé pour créer le clone local.

Si un référentiel local existe et ne doit être connecté qu'à un référentiel distant vous pouvez le faire en configurant un Git Remote pointant vers le référentiel distant. Vous pouvez nommer le Remote à peu près comme vous le voulez - il n'est pas nécessaire qu'il soit l'origin - tant que le nom ne comporte pas d'espaces ni de caractères spéciaux (à l'exception de quelques-uns, tels que "-", "_" ou ".") . Vous pouvez configurer le Remote pour qu'il se connecte via n'importe quel protocole pris en charge par Git et vérifier la connexion à ce Remote en appelant la commande Remote show. 

git remote add myserver git@example.com/project.git
git remote show myserver

Après avoir cloné un référentiel, la branche principale du Remote aura subi un pull down et un checkout. Toutefois, lorsque vous configurez un Remote manuellement, aucune branche ne sera aspirée automatiquement, ce qui signifie qu'elles ne seront pas encore disponibles. Pour faire venir des branches et commencer à les utiliser, vous devez d'abord aller chercher les branches distantes:
git fetch myserver

Pour créer une branche locale à partir de la branche distante, spécifiez le nom distant / nom de la branche comme point de départ de la branche locale.
git branch sometopic myserver/sometopic

Il n'est pas nécessaire qu'une branche locale porte le même nom qu'une branche distante. Si une branche locale doit avoir un nom différent, spécifiez le nom en tant que premier paramètre après la commande git branch.
Après le clonage d'un référentiel ou la configuration d'une télécommande et l'extraction de la branche souhaitée, vous pouvez travailler sur le référentiel local. Des validations peuvent être effectuées, des branches peuvent être créées et fusionnées, etc.
Lorsque des commits sont faits et que des branches sont créées il s’agit toujours de commits et de branches locales; Git ne met pas automatiquement à jour le référentiel distant. Une fois que vous êtes prêt à partager les validations dans le référentiel local, vous pouvez les transférer dans le référentiel distant. Par exemple, si vous avez effectué les commits sur  la branche principale. Vous pouvez les pousser avec la commande push, en spécifiant la branche à pousser:
git push myserver master

Cela poussera toutes les modifications apportées à la branche maître locale vers la branche maître distante. Le résultat est que la branche maître distante pointe maintenant vers la même validation que le maître local.

Notez que les ID de validation et les autres détails de validation créés dans un référentiel local restent les mêmes lorsqu'ils sont déplacés vers un référentiel distant. C’est ainsi que Git sait quel est le statut d’une branche distante par rapport à une branche locale et par rapport à d’autres branches.

Développeurs multiples et référentiels distants

Lorsque plusieurs développeurs travaillent sur le même projet, ils doivent partager leurs modifications. Dans Subversion, vous le faites via le référentiel central. Tous les commits que vous avez faits sont disponibles pour les autres développeurs, qu'ils soient sur une branche ou sur le trunk. Cependant, dans Git, les commits ne sont pas toujours immédiatement disponibles pour les autres.
Vous pouvez configurer Git pour que chaque membre d’une équipe ait un Remote pointant sur chaque autre membre de l’équipe, de manière homogène. Dans ce type de configuration les commits sont généralement disponibles lorsque la personne à partir de laquelle vous voulez extraire des commits est disponible via un réseau. Si vous travaillez tous dans le même bureau les commits seront probablement disponibles immédiatement. Toutefois, si l'équipe utilise un référentiel distant partagé les validations ne sont rendues disponibles que lorsqu'elles sont poussées vers le référentiel partagé. Cette configuration offre aux utilisateurs de Git les mêmes avantages qu'un référentiel centralisé, tel que Subversion, tout en offrant tous les avantages d'un système de contrôle de source distribué.

Quelle que soit la topologie du référentiel une équipe qui collabore avec Git suivra les mêmes pratiques qu’un développeur travaillant dans un référentiel local et un autre travaillant avec un référentiel distant. Des clones seront créés, les Remotes seront configurés, les commits et les branches seront poussés vers les référentiels distants. La différence dans le travail en équipe réside dans la gestion des validations de quelqu'un d'autre disponibles dans un référentiel distant et dans la nécessité de les transférer sur votre machine volontairement.
Lorsqu'un développeur a fini de travailler et a fait un push vers un référentiel distant partagé avec les autres développeurs, les modifications sont alors disponibles pour les autres développeurs.

Vous faites un pull des commits d'autres développeurs dans un référentiel distant en utilisant fetch et merge:
git fetch myserver
Une fois que vous avez fait un pull des commits sur votre ordinateur local, vous pouvez les fusionner dans la branche locale.

git merge myserver/topicbranch

Notez que vous effectuez la fusion à partir de la branche locale et que Git fusionne le contenu de la branche distante extraite dans le référentiel local. Aucune modification n'a encore été transmise au référentiel distant. Une fois que les modifications dans le référentiel local sont prêtes, vous pouvez les transmettre au Remote pour que d'autres puissent les extraire, les fusionner et les utiliser.

Il existe un raccourci pour importer des modifications distantes dans une branche locale: git pull . Faire un pull effectuera automatiquement l'extraction et la fusion (Fetch et Merge). Vous pouvez spécifier quelle branche extraire manuellement ou vous pouvez laisser Git déterminer la ou les branches à extraire en fonction de la configuration de votre référentiel :
git checkout topicbranch
git pull myserver/topicbranch

Tous les commits qui existent dans la branche du référentiel distant doivent être présents dans la branche du référentiel local avant que Git autorise le push.
Il y a une difficulté à transmettre des modifications à un référentiel distant. Lorsque des modifications sont disponibles sur le Remote, si elles n'ont pas encore été extraites dans le référentiel local et fusionnées dans la branche locale Git rejettera le transfert. Tous les commits qui existent dans la branche du référentiel distant doivent être présents dans la branche du référentiel local avant que Git autorise le push. Une fois que les commits ont été extraits du référentiel distant et fusionnés dans la branche locale, le push peut avoir lieu.

La nécessité d'extraire en premier toutes les modifications existe car un push qui n'était pas à jour avec les commits du Remote nécessiterait une fusion (merge) du côté distant. Si un conflit se produisait pendant la fusion, il pourrait ne pas être possible de le résoudre. Par conséquent, Git exige que les branches locales soient à jour et qu'elles contiennent tous les validations de la branche distante avant de les envoyer à une branche distante. (Les branches locales sont gérées par des humains pouvant prendre des décision en cas de conflit, un repository distant géré par un serveur sans humain ne le peut pas, c’est assez évident en fait).

Rebase: une autre façon de déplacer les commits entre les branches

La fusion est similaire entre Subversion et Git sous plusieurs angles. Ils apportent tous les deux des modifications d’une branche à l’autre (ou du trunk dans le cas de Subversion). Ils permettent tous deux de travailler indépendamment des changements de l’autre. Ils peuvent tous deux rencontrer des conflits de fusion qui doivent être résolus avec des outils de fusion et peuvent généralement travailler avec les mêmes outils de résolution de conflits de fusion. Git peut même travailler avec TortoiseMerge de TortoiseSVN (même s’il reste préférable d’utiliser des outils conçus pour s’intégrer naturellement avec Git, comme Visual Studio peut en proposer ou d’autres outils externes).
Une rebase permet de déplacer les modifications d'une branche vers la fin d'un ensemble de modifications d'une autre branche, sans effectuer de fusion.
Cependant, il existe des différences significatives dans les options que Git prévoit pour amener des commits d'une branche dans une autre.
Rebase c'est un peu comme une fusion, mais ce n'est pas une fusion
Lorsque vous avez créé une branche à partir du maître, elle peut diverger de la branche principale. Si un développeur utilisant une telle branche doit se tenir au courant des modifications apportées dans la branche principale il peut créer une nouvelle base de sa branche au lieu d'une fusion.
Une base est similaire à une fusion dans la mesure où elle apporte les modifications d'une branche à une autre. Cependant, c’est très différent en ce sens que cela ne fusionne pas les changements d’un seul coup. Au lieu de cela, une base rejouera tous les commits de la branche rebasée vers la Head de la branche cible :
git checkout mytopic
git rebase master

Lorsque la rebase se produit, Git rembobine d'abord la branche sujet jusqu'au point de l'historique du référentiel où les branches se trouvaient sur le même commit. Ensuite, le HEAD de la branche sujet sera déplacé vers le même commit que le HEAD de la branche principale, comme si la branche avait été créée à partir de ce commit. Git réappliquera ensuite tous les commits individuels qui ont été initialement créés sur la branche sujet vers le nouvel emplacement de la branche. Le résultat final est que la branche sujet semble avoir été créée à partir de la validation HEAD de la branche maître au lieu du point d'origine à partir duquel elle a été créée.
Il faut noter que les modifications apportées à la branche sujet ont alors été déplacées depuis une timeline distincte qui est passée du modèle principal à la fin de la timeline du modèle principal. C'est comme si la branche sujet avait été démarrée et traitée après les modifications apportées au maître. Mais ce n’est que “comme si”…

Un Rebase réécrit l'histoire

Les commits dans la branche mytopic originale et rebasée n'auront plus le même ID de validation. En tant que tel Git les traitera comme des commits différents même si le contenu est le même. Cela a quelques implications très importantes qui doivent être examinées avant de procéder à un Rebase.
Lorsque vous effectuez une fusion de base, les validations fusionnées sont laissées telles quelles et une nouvelle validation est effectuée pour capturer la fusion. Pousser et extraire les commits qui ont été fusionnés n'est jamais dangereux, car les ID de validation sont statiques. Ils seront les mêmes sur toutes les machines qui reçoivent ces commits. Les identifiants étant identiques, les modifications effectuées sur une machine peuvent être transférées dans un référentiel d'une autre machine. Cela fait partie des raisons pour lesquelles un système de contrôle de source distribué fonctionne bien. Git a été conçu pour ces situations.

Lorsque vous faites un rebase, l’histoire est réécrite. Les commits qui ont été effectués dans la branche sujet n'ont plus le même parent et les ensembles de modifications dans les commits sont maintenant appliqués à un contenu potentiellement différent de celui d'origine. Cela signifie que Git ne peut pas conserver les ID de validation initialement utilisés. Git réapplique les validations �� un nouveau scénario et crée de nouveaux ID de validations.
Même dans le cas simple où vous utilisez uniquement un rebase pour déplacer les commits d'une branche à la fin d'une autre, cela peut être dangereux. Si vous supprimez une branche d'un référentiel distant puis redéfinissez cette branche contre une branche qui n'existe que sur la machine locale, Git modifiera les identifiants des validations de la branche qui ont été supprimées. Si l'utilisateur Git tente ensuite de repousser la branche qui ont été rebasés sur le Remote, Git générera un message d'erreur indiquant que les branches ne sont pas synchronisées. Cela est dû au fait que l'en-tête HEAD de la branche du référentiel local pointe maintenant vers un ID de validation différent de celui de la branche du référentiel distant. Même si la branche locale et la branche distante partagent le même contenu dans les validations, les identifiants de validation ne sont pas les mêmes et, par conséquent, Git ne sait plus qu'ils étaient autrefois les mêmes commits...

Ce sont ces situations qui peuvent réclamer l’intervention d’un “power user” de Git capable de comprendre les erreurs effectuées et qui saura surtout comme rétablir la situation. Si personne n’a cette compétence tout est planté et on ne peut plus travailler. C’est un danger auquel il faut être préparé en s’assurant qu’au moins un membre de l’équipe est pointu sur Git.

Les conflits de refonte ont un avantage et un inconvénient

Lorsqu'une série de commits sont fusionnés, ils sont essentiellement regroupés dans un grand jeu de modifications, puis appliqués en masse. Il n'y a pas d'autre choix que de prendre en compte tous les changements survenus au même moment. Lorsque la fusion est réussie, ce n'est pas un inconvénient. Cependant, lorsqu'une fusion échoue en raison d'un conflit, l'utilisateur est obligé d'examiner toutes les modifications de toutes les validations du processus de résolution de la fusion.
Une Rebase Git n'est pas à l'abri des conflits et les conflits de Rebase sont traités de la même manière que les conflits de fusion. Cependant, lorsque vous effectuez un Rebase et que des conflits surviennent, un avantage potentiel se présente. Parce que Git applique chaque commit individuel à la branche cible, Git a la possibilité d'appliquer les modifications par incréments beaucoup plus petits, aussi petit que le commit lui-même. Cela permet au conflit d'être aussi petit qu'un commit individuel, ce qui réduit considérablement le contenu que l'utilisateur doit filtrer pour résoudre le conflit.
Malheureusement, un conflit de rebases avec tous les engagements individuels appliqués, un à la fois, peut présenter un désavantage. Si un conflit de rebase se produit, la résolution de ce conflit peut créer une opportunité pour qu'un autre commit qui n'a pas encore été appliqué soit en conflit. Il est possible - et cela se produit - pour un rebase qui résulte en un conflit pour chaque commit re-appliqué.

Rebase est un outil que vous devez maîtriser

Il existe certains scénarios dans lesquels un rebasement est plus logique qu'une fusion, malgré le danger potentiel. Rebase est un outil que tous les utilisateurs de Git devraient avoir dans leur boîte à outils. L'utiliser sans comprendre les conséquences potentielles n'est cependant pas une bonne idée ! Prenez le temps de comprendre comment fonctionne un Rebase, quelles sont les principales options, ainsi que les effets secondaires et les inconvénients potentiels.

Outils Git et intégration

imageL'un des plus grands pouvoirs d'attraction de Subversion est la taille de sa communauté. Subversion existe depuis longtemps et est un système très populaire avec une communauté florissante. Il existe des centaines, voire des milliers d'outils prenant directement en charge Subversion, notamment des outils d'interface graphique très faciles à utiliser et une intégration aux IDE les plus populaires. Un simple home server Synology possède des extensions pour installer un serveur Subversion en quelques clics.
Git, bien que relativement jeune comparé à Subversion, est également soutenu par une communauté florissante qui depuis un moment maintenant s’est imposée un peu partout. Il existe ainsi de nombreux outils permettant d’intégrer Git aux IDE les plus populaires et à d’autres outils, et d’autres sont créés et améliorés régulièrement. Alors que Git continue de gagner en popularité, le nombre d'outils et de points d'intégration continue également de croître.
Voici quelques-uns des outils les plus populaires pour travailler avec Git et l'intégrer dans le travail quotidien d'un développeur de logiciels. Pour une liste plus complète des outils basés sur Git, consultez le wiki de Git à l' adresse https://git.wiki.kernel.org/index.php/Interfaces,_frontends,_and_tools . Il y a des dizaines, centaines peut-être d’outils listés…

Pour faire plus simple vous pouvez utiliser tout naturellement les extensions Git intégrées à Visual Studio, c’est bien fichu et disponible dans l’EDI.

Reste tout de même que savoir se servir d’une console Git pour régler certains problèmes est indispensable dans certains cas…

Conclusion

Nous voici arrivés à la fin de cette série de 3 articles sur le passage de Subversion à Git. J’espère que malgré la sécheresse du sujet (c’est vraiment pas fun !) il vous aura permis de mieux comprendre la philosophie de Git et de mieux vous préparer à l’utiliser car nul doute là dessus, de plus en plus souvent vous allez rencontrer Git plutôt que Subversion… Une page se tourne, les temps changent, les besoins aussi, les outils sont comme nous, ils finissent par vieillir, et malgré leurs qualités un jour ils doivent céder la place. Ce moment est venu pour Subversion. Il ne s’agit pas de se résoudre à une mode, ni de lutter pour l’outil auquel on est habitué et qui est donc le meilleur par définition, non, il s’agit de se prouver d’abord à soi-même si on appartient toujours au présent ou bien si on commence à appartenir au passé

Le passage de Subversion à Git pour beaucoup sera ainsi bien plus un examen de conscience, un rendez-vous avec soi-même, qu’un simple changement technique.

Être de son époque ou ne pas l’Être, Telle est là question, aurait pu dire un Shakespeare informaticien du 21ème siècle !

Si vous suivez Dot.Blog c’est que vous êtes de votre époque et ne vivez pas dans le passé, j’ai donc confiance, au moins pour vous, que vous saurez vous adapter à Git sans combat d’arrière-garde qui ne prouveraient qu’un chose, que vous avez vieilli, pas physiquement seulement, on n’y échappe pas, mais intellectuellement. Et là c’est plus difficile. Je connais des vieux qui n’ont que trente ans. On peut être vieux à tout âge …

Mais attention, vous n’utiliserez pas Git pour faire jeune, cela serait idiot, beaucoup utilisent les nouvelles technologies en espérant afficher ainsi leur jeunesse d’esprit, c’est souvent pathétique… Non vous l’utiliserez parce que c’est tout simplement mieux fait pour vos besoins contemporains, c’est cela que j’appelle vivre avec son temps, ce sont des choix éclairés et non des effets de modes ou se conformer à l’avis des masses.

Vive le présent, vive la jeunesse de l’esprit, Vive les outils modernes, Vive Dot.Blog Smile

Et stay tuned (je promets c’était peut-être un peu chiant comme série d’articles il y aura plus sympa !)

blog comments powered by Disqus