Git / GitHub
Règles générales concernant :
Prenons l'exemple d'un projet de développement
Comment travaillez-vous en équipe, voire même seul pour :
Le "versioning" est un terme regroupant différentes fonctionnalités :
Aide au travail en équipe grâce à un système de branches (aide à la résolution de conflits)
Il existe de nombreuses raisons qui poussent la plupart des développeurs et entreprises à préférer Git à ses différents concurrents
La première reste toutefois le fait qu'il s'agisse d'un système distribué
Avec un système distribué, chaque utilisateur possède une copie locale du dépôt distant
Cela assure donc que chaque membre du projet pourra travailler de son coté, sans internet, tout en ayant la capacité de partager son code à tout le monde quand il le souhaite
En cas de panne du serveur, tout le monde a une copie locale : la sécurité est assurée
A l'origine, Linus Torvald avait imaginé git pour travailler... sur le noyau linux !
Avant Git, les contributeurs lui envoyait leurs travaux.. via des patchs de codes par mail !
Développement commencé par Linus Torvald pour le noyau Linux en 2005 et suivi par toute la communauté
INDISPENSABLE pour n’importe quel développeur… et informaticien en règle générale !
Documentation officielle (très bien faite et accessible en français) : https://git-scm.com/
Pour toutes les étapes non mentionnées ci-après, faire juste "suivant"
Placer le où vous voulez
On décoche ce qui nous sert à rien
Choisissez comme bon vous semble !
Laissez la première
Une fois tout installé, faites un coup de :
Dans votre invite de commande
git helpSi la doc de Git apparaît, tout est OK !
Il est possible que le PATH n'ait pas été mis à jour
Si tel est le cas, renseignez dans votre PATH le chemin vers le dossier bin de l'utilitaire Git que vous avez installé
Le plus souvent à cet emplacement :
C:\Program Files\git\bin
sudo apt-get install gitgit --helpSi le PKG ne peut pas être installé car ne provenant pas d'un développeur etc.. clic droit + ouvrir
git helpGit possède un outil de configuration global
Parmi de nombreuses variables, il y a notamment le "nom" et l' "email" à remplir afin de s'authentifier pour la suite des opérations
git config --global user.name "Bob"
git config --global user.email bob@burger.comgit config --global --listPour afficher l'ensemble des variables globales renseignées :
Il est d'ailleurs possible de modifier directement le fichier de config se trouvant à
~/.gitconfigNotons que le flag --global permet de configurer une seule fois ces variables pour tous nos futurs dépôts git
Si souhaité, il est tout à fait possible de ne préciser des variables que pour un dépôt spécifique en retirant simplement ce flag
La commande pour voir l'ensemble de votre configuration git est la suivante :
git config --listIl est possible d'avoir accès à une documentation détaillée de chaque commande si vous avez un trou à un moment : très utile !
git help <verbe>
git <verbe> --help
man git-<verbe>
git help configgit init
git initgit init est une commande qui va automatiquement créer un dossier caché .git
Ce dossier est CAPITAL car il regroupe l'ensemble des fichiers inhérents au fonctionnement de git et à l'historique de votre dépôt
La commande git init doit être effectuée à la racine du projet que vous souhaitez versionner
git status, git add, git reset <file>, README.md, .gitignore
La première commande que nous allons apprendre est la commande suivante :
git statusCette commande permet de connaître l'état actuel des fichiers dans le projet
Lançons la commande dans un dépôt vide
git statusLa commande nous indique 3 choses :
Créons un fichier README.md dans le dépôt :
touch README.md
git statusIci, le résultat de git status est quelque peu différent
Nous voyons que git a bien reconnu notre nouveau fichier mais il le considère comme "untracked", soit "non suivi"
Afin de pouvoir sauvegarder l'état courant des fichiers d'un projet, il faut donc les traquer (les suivre) et les indexer, c'est à dire les mettre dans le Staging
Il s'agit ni plus ni moins de dire "Ce fichier là, je le veux dans mon prochain commit" : c'est comme un panier d'achat... mais pour les sauvegardes !
Pour traquer un fichier et l'ajouter au Stage, il faut utiliser la commande :
git addgit add peut prendre comme paramètre :
Ainsi, nous pouvons faire :
git add README.md
git statusATTENTION, git add ne fait qu'indexer (mettre dans le Stage) la version du fichier avant la commande
Pour mieux comprendre, modifier maintenant le fichier README.md, puis :
git statusCela peut paraître étrange que le fichier soit à la fois indexé et non indexé mais c'est en fait tout à fait normal.
En effet, la commande git add n'indexe qu'une certaine version du fichier (celle juste avant la commande)
Ainsi, si nous "commitions" maintenant, nous ne sauvegarderions que la version précédente du fichier, sans les changements.
Il nous faudra donc indexer de nouveau le fichier README pour que les changements soient pris en compte
Ainsi :
git add README.md
git statusLe fichier est maintenant prêt à être "commité" et donc sa version sauvegardée !
Mais que faire si nous avons indexé un fichier que nous ne souhaitons pas sauvegarder pour le moment ?
Il faut pour cela utiliser la commande :
git reset <nomDuFichier ou dossier>Un petit aparté pour parler du fichier README.md que nous venons de créer
Il est une très bonne pratique de toujours en placer un à la racine de votre projet
Il s'agit en fait d'un fichier en Markdown dont l'objectif est de spécifier ce que fait votre projet, la manière dont il fonctionne, etc.
C'est une convention partagée par tous les développeurs, ne pas en avoir serait vraiment dommage
Il arrive souvent que certains fichiers du projet ne doivent pas être versionnés pour plusieurs raisons :
Cela arrive si souvent qu'il existe une méthode très facile pour les déclarer
Il faut créer un fichier .gitignore à la racine de votre projet
Il suffit ensuite de déclarer dans ce fichier l'ensemble des chemins/patterns des fichiers ou dossiers que vous ne souhaitez jamais versionner
créer fichier .gitignore
*.temp
toto/*Ne serons plus pris en compte :
Attention à également indexer le fichier .gitignore dans votre projet !
Nous avons donc appris dans ce chapitre :
Pour finir, schématisons toute cette histoire de Stage :
git commit (-m, -a)
Dans l'optique de versioner notre code, nous voudrions pouvoir figer une version de notre projet à un moment donné
C'est exactement à ça que sert un commit
Ce n'est ni plus ni moins qu'une image de votre projet à un moment donné
Il faut voir votre Stage comme l'ensemble des fichiers dont vous souhaitez valider la version actuelle et donc la marquer d'un commit
Attention, un commit ne peut donc prendre en compte que les fichiers que vous avez préalablement indexé (mis dans le Stage) avec la commande git add
La commande magique n'est autre que ... :
git commitVotre terminal vous demandera ensuite (via votre éditeur par défaut) de renseigner un message de commit
Un message de commit n'est pas obligé d'être unique
Il est toutefois plus que souhaitable de donner un nom clair et représentatif des changements effectués par ce commit
Si votre commit consiste au changement d'une couleur de titre dans votre projet et que, par exemple, vous nommez votre commit "babar", ça n'a aucun sens et le versionning perd beaucoup de son utilité
Appelons donc notre commit "Add .gitignore and readme.md"
Le commit devrait ensuite être validé et nous devrions voir quelque chose comme :
Git nous récapitule donc l'ensemble des modifications apportées par le commit et nous donne également son identifiant SHA-1 : 440156b
Utiliser l'éditeur par défaut peut être embêtant et surtout représenter une étape en plus inutile
Il est tout à fait possible de renseigner le message du commit directement dans la commande grâce au flag -m
git commit -m "Add .gitignore and README.md"Après tout ça, votre projet doit être tout propre et sans aucun changement !
Vérifiez ça avec un petit git status
git statusRien à commit, le projet est clean, tout va bien !
Nous l'avons dit, le commit ne valide que les modifications des fichiers indexés
Par exemple, créons maintenant 2 fichiers index.html et test.html
touch index.html
touch test.htmlgit statusModifier votre fichier "index.html", ajoutez-le au Stage et commitez
git add index.html
git statusgit commit -m "Add index.html"git statusNous voyons donc que le commit n'a bien validé QUE les fichiers précédemment indexés
Cette particularité est extrêmement appréciable car elle permet de maîtriser avec une grande précision ce que l'on commit et donc de faire un travail propre
Toutefois, il est possible (et souvent fait) d'indexer les fichiers en même temps que de faire le commit, d'une pierre deux coups et ceci grâce au flag -a de la commande commit
Ajoutons maintenant d'autres fichiers, header.html et footer.html
git statusgit add --allTraquons tous ces fichiers :
Modifions ensuite footer.html et voyons l'état du dépôt :
git commit -a -m "Start of an awesome site"Plutôt que de continuellement devoir re-add les fichiers dans le Stage, le flag -a prend ici tout son sens
Grâce à cette commande, vous venez donc de réaliser un comit prenant en compte l'ensemble des fichiers traqués, même ceux pas dans le Stage à ce moment
Il existe même un raccourcis :
git commit -am "Start of an awesome site"git log (--oneline, -p), git diff (--cached)
La commande de base pour visualiser l'historique du dépôt est :
git loggit log permet de lister l'ensemble des commit de la branche en cours
Nous y trouvons :
Par exemple, git log sur notre dépôt actuel donne :
Nous voyons donc bien tous les commits que nous avions effectué ensemble !
Vous aurez peut-être remarqué le (HEAD -> master)
Nous l'expliquerons plus en détail quand nous parlerons des branches, mais disons que HEAD correspond à votre emplacement dans l'historique et qu'il pointe actuellement vers master, tout est donc normal
Lorsque nous devons visualiser un grand nombre de commits, la simple commande git log est parfois trop verbeuse
Un petit raccourcis sympa à utiliser peut donc être le flag --oneline qui permet de tout aplatir sur une seule ligne par commit
git log --onelineIl est également possible de lister l'ensemble des commits qui ont apporté des modifications à un fichier en particulier grâce au flag -p suivi du nom du fichier
Par exemple, si nous voulons analyser tous les commits qui ont modifié le fichier index.html depuis le début du projet :
git log -p index.htmlCette commande existe aussi en version raccourcie
git log --oneline -p index.htmlRésultat de la commande :
La commande nous donne d'ailleurs pas mal d'informations sur les changements en question : super pratique pour déceler l'origine de changements ou de bugs
Cette commande assez intéressante permet de voir l'ensemble des différences entre le projet courant et le dernier commit
Exemple : Effaçons tout le fichier index.html pour écrire "Git c'est vraiment trop bien"
Puis, lançons la commande :
git diffMême si ce n'est pas le format le plus lisible du monde, nous reconnaissons tout de même la suppression d'une ligne et l'ajout d'une autre ainsi que leur détail
Attention : git diff ne fonctionne que pour les fichiers non indexés !
En effet, si vous faites :
git add index.html
git diffVous ne verrez... rien !
En effet, à partir du moment ou un fichier est indexé, il n'apparaît plus dans le git diff
Pour visualiser les différences entre le Stage et le dernier commit, la commande est :
git diff --cachedFinissez ce chapitre en commitant les modifications sur le fichier index.html
git commit -m "Update index.html"BONUS : outil graphique
GitKraken
Outil graphique : GitKraken
Plus un projet git devient imposant, plus il devient compliqué de tout visualiser et gérer via sa console
C'est pourquoi il existe de nombreux outils graphiques permettant de grandement se simplifier la tâche
Il en existe des tonnes, sûrement plein que je ne connais d'ailleurs pas, mais les plus "connus" sont : GitKraken et SourceTree
A savoir également que certains IDE intègrent en interne une gestion de git parfois très élaborée comme pour les très bons IDE JetBrains
Outil graphique : GitKraken
Pour la suite de ce cours, j'utiliserai donc parfois l'outil GitKraken comme outil de visualisation de notre dépôt
Il n'est absolument pas nécessaire que vous le téléchargiez de votre côté, c'est juste un "bonus" pour y voir plus clair dans votre dépôt, mais vous pouvez tout faire sans
Pour information, les deux outils sont très intéressants et mon choix ne veut absolument pas dire que gitKraken est mieux que SourceTree
Ce choix étant essentiellement basé sur la présence d'un poulpe lors de l'ouverture du logiciel
Outil graphique : GitKraken
Côté visualisation, rien de plus simple !
Une fois téléchargé puis ouvert, cliquez sur "open a repo"
open a repository pour aller chercher votre repo git
Sélectionner le dossier comprenant votre dossier .git
Observons ensemble l'outil :
C'est bon ! Vous devriez maintenant voir votre super dépôt !
Outil graphique : GitKraken
Sur la gauche de l'écran nous pouvons voir l'ensemble des branches présente sur votre dépôt local mais aussi sur votre dépôt distant
Nous y reviendrons lors de prochains chapitres
Notez que la coche verte indique sur vous êtes actuellement sur la branche master
Outil graphique : GitKraken
Le premier bloc présente l'ensemble des fichiers non indexés du dépôt courant
En cliquant dessus, vous pouvez les add et donc les placer dans le second bloc : l'ensemble des fichiers indexés du dépôt
Enfin, lorsque vous êtes prêt à commit, vous pouvez entrer votre message de commit dans le "summary" puis commit vos changements
Outil graphique : GitKraken
Voici la vue qui nous intéresse le plus !
Il s'agit de la liste de l'ensemble des commits du projet, dans l'ordre chronologique
Voyez cette vue comme un git log bien plus joli !
En cliquant sur chaque commit, vous pouvez voir à droite de l'écran les fichiers qu'il a modifié ainsi que les changements concernés pour chaque fichier !
Outil graphique : GitKraken
Vous l'aurez compris, cet outil est assez incroyable et nous fait gagner un temps précieux
Cependant, l'utiliser dans toute sa puissance doit impérativement passer par une grande connaissance des bases de git en ligne de commande
Ainsi, nous n'utiliserons de cet outil QUE son écran au centre, soit la visualisation des commits
Libre à vous d'étudier le reste de l'outil, mais nous n'en auront pas le temps dans ce cours et nous concentrerons sur les bases en ligne de commande
(sans le modifier)
git checkout (hash, master, hash + fileName, master + fileName, --)
L'une des grandes forces de git est la capacité que l'on a à se déplacer dans la timeline du projet
Que diriez-vous de pouvoir revenir n'importe quand dans le projet ?
git checkout est une commande qui a la particularité de vous placer sur le commit de votre choix
(Elle permet également de changer de branche, mais c'est pour un peu plus tard, patience)
Vous pourrez ainsi voir la totalité des fichiers dans l'état dans lequel ils étaient au moment du commit !
Pour cela, il s'agit de récupérer le SHA-1 du commit en question, puis de lancer la commande :
git checkout ae8dbf57b9735c777f5465ef2b860e436eb9bd80Ce SHA-1 correspond en fait à mon commit précédent la modification de mon fichier index.html !
Voici ce que nous dit git :
Pour ceux qui comprenne à peu près l'anglais, je vous invite à lire ce qui est marqué, c'est très bien expliqué !
Pour résumer, ce message nous indique que nous sommes désormais en mode "detached HEAD"
Comme je l'avais dit précédemment, ce fameux HEAD est en fait .. vous ! et vous ne pointez plus actuellement vers master mais sur l'un des commit de master, dans le passé ! D'où le :
Faisons un petit coup de :
git log --onelineHummmm.. l'un de nos commits semble avoir disparu, celui intitulé "Update index.html"
C'est normal ! Nous venons de faire un bond dans le passé, grâce à git checkout !
Nous ne voyons pas le commit en question, car il n'existait pas à l'époque, c'est tout !
Si on regarde le dépôt grâce à notre outil graphique GitKraken, on s'aperçoit bien du mécanisme
Le HEAD pointe bien vers l'avant dernier commit de la branche master !
VOUS ETES SPECTATEUR
Attention, on a sorti les gros titres en rouge
Je vous laisse relire le message que git nous proposait après le checkout, mais il dit en gros que nous ne sommes que spectateur sur ce commit et que nous ne devrions rien changer car aucun commit ne pourra être réalisé
En vérité, c'est un chouia plus compliqué que ça, et vous pouvez tout de même notamment tirer une branche dans un état détaché, mais cela relève d'un cours plus avancé que celui-ci et nous ne le verrons pas
Bon, alors si je suis juste spectateur, comment je fais pour revenir dans le présent ?
Si vous souhaitez revenir à l'état présent, vous pouvez à tout moment faire un checkout sur votre branche actuelle :
git checkout masterEt voilà ! Nous étions sur le commit et nous avons switcher sur master !
Petite précision, il existe une autre manière d'arriver au même résultat, sans avoir besoin du SHA-1 du commit
git checkout HEAD^Chaque ^ signifiant un bond en arrière d'un commit !
Nous ne faisons que bouger le pointeur de commit en commit
Il est aussi possible de récupérer une ancienne version d'un fichier grâce à git checkout hash + fileName
Pour cela, il suffit de faire la même commande, avec le SHA-1 de la version spécifique + le nom du fichier en question
git checkout ae8dbf5 index.htmlEn faisant cela, vous êtes au même endroit dans votre historique mais vous avez ramené un fichier du passé !
git checkout master index.html(si vous voulez annuler et reprendre l'état courant du fichier !)
Comme nous sommes toujours dans le présent de notre timeline, nous pouvons tout à fait commit notre fichier et donc mettre à jour le projet en revenant à l'ancienne version de ce fichier
git commit -m "Put old version of index.html"Voici une dernière commande intéressante concernant checkout et le retour dans le passé
Que faire si l'on ne souhaite pas conserver les modifications que nous sommes en train d'apporter à un fichier ?
Il existe une commande permettant de réinitialiser un fichier à l'état qu'il avait au dernier commit !
git checkout -- fileNameAttention cependant ! En effectuant cette commande vous revenez bien à l'état du dernier commit, mais vous perdez absolument tous les changements !
Un grand pouvoir implique de grandes responsabilités
git revert, git reset (--soft, --mixed, --hard), git commit --amend
La plupart des commandes que nous allons apprendre dans ce chapitre sont aussi utiles que dangereuses
La modification d'un historique git ne doit jamais être pris à la légère car il s'agit peut-être du projet le plus important de votre entreprise dont vous modifiez la timeline
Heureusement, la plupart du temps, rien n'est jamais vraiment perdu et les erreurs peuvent être rattrapées
Cependant, ce n'est pas toujours le cas et vous pouvez perdre des données importantes si vous vous y prenez mal, alors prudence
Cette commande permet de défaire un commit précédent et d'annuler les changements que celui-ci a mis en place
Cette commande est un peu spéciale car elle ne brise pas vraiment l'historique en "annulant le commit" mais en créant un nouveau commit qui annulera les changements du précédent
Rien de mieux qu'un petit exemple
Voici les commits de notre dépôt
Si l'on observe attentivement le premier commit (git log -p 440156b), on s'aperçoit qu'il ne fait que créer le README et le .gitignore
Tentons de le revert
git revert 440156bIl vous sera demandé un message de commit, laissez celui par défaut et validez
Voici le résultat !
Nous avons bien un nouveau commit qui vient défaire le précédent commit !
Seulement ... les fichiers étaient en fait bien utiles et ce revert n'était peut-être pas une si bonne idée
Aucun souci, car un revert étant lui-même un commit.. nous pouvons revert un revert !
git revert 0e72951Tout est revenu à la normale !
C'est l'astuce avec le revert, il s'agit d'une manière assez sécurisée de revenir sur une décision, car un revert peut lui-même être revert !
Vous vous souvenez quand je vous disais de faire attention en modifiant votre historique ?
git reset est justement l'une des commandes à manier avec une grande précaution !
Cette commande, très puissante, permet de revenir à l'état d'un commit mais va littéralement modifier votre historique
Premier exemple dans les changements courants
Modifions quelques fichiers et faisons les commandes :
git add --all
git statusTestons maintenant le git reset
git reset
git statusNous voyons donc que la commande git reset vient de remettre la totalité du Stage dans l'état dans lequel il était au dernier commit
Il a donc totalement dés-indexé la totalité des fichiers qui l'étaient
Cependant, nous avons pu garder nos modifications, si l'on regarde dans nos fichiers, et ça c'est plutôt rassurant
Sans le savoir, nous venons d'utiliser la commande git reset --mixed (le flag par défaut) qui garde l'ensemble des modifications en mémoire après le reset mais ne les indexe pas
Seulement.. git reset vient également avec son flag --hard que je vous propose de tester sans plus attendre sur notre dépôt
git reset --hardgit statusNous venons d'écraser tous les changements effectués depuis le dernier commit et de revenir à l'état de celui-ci... sans rien garder en mémoire
Or ces changements n'ayant jamais été commité, il nous est impossible de les retrouver via git .. vous comprenez donc qu'il faut utiliser le flag --hard avec une grande prudence
Un 3ème flag existe, le --soft qui permet de revenir en arrière tout en gardant les changements dans l'index (à la différence du --mixed)
La grande force de git reset est qu'il peut s'appliquer directement sur un commit, un peu à la manière d'un git checkout pour revenir dans le passé
La grande différence, et pas des moindres, est que tous les commits précédents seront effacés !
Revenons sur notre dépôt
Ok, imaginons maintenant que je veuille revenir à l'état du commit 0f55c5f
git reset 0f55c5f
git log --onelineNous avons bien remonté le temps jusqu'au commit 0f55c5f, effaçant les commits suivants
Cependant, comme nous étions en mode --mixed, nous avons gardé les modifications
git statusNous pouvons donc les commit
git commit -a -m "Make better index.html message"Testons maintenant le flag --hard en revenant au commit ae8dbf5
git reset --hard ae8dbf5Nous sommes bien revenus au niveau du commit, en ayant supprimé toutes les modifications depuis
Il peut arriver, pour diverses raisons, que vous ayez besoin de modifier un commit que vous venez de faire
La commande git commit --amend est là pour ça !
Prenez, par exemple, votre fichier index.html et remplacez tout pour y écrire "Salut tout le monde"
Commitez ensuite ces changements avec un message comme "Add welcoming message"
Vous avez oublié un "!" à la fin de votre phrase dans le index.html... comment faire ?
Créer un nouveau commit juste pour ajouter le "!" semble un peu ridicule, surtout que le message du premier commit était bien, autant les regrouper
Modifiez votre index.html pour y ajouter le "!"
git add --all
git commit --amendVotre éditeur par défaut va ensuite vous demander de renommer le message de votre commit précédent, si vous le souhaitez
Valider le changement.. et voilà ! Vous n'avez toujours qu'un seul commit, mais avec le "!" en plus
git branch (name, -d name), git checkout, git merge (FF, no FF), git rebase (-i)
Les branches sont l'une des caractéristiques essentielles de git
Imaginez que vous soyez en train de travailler sur une nouvelle fonctionnalité
Arrivé à la moitié, votre client vous demande de corriger d'urgence un bug en production
Que faire ? Si vous avez tout coder sur master depuis le départ, vous allez pousser la correction du bug + la moitié de votre fonctionnalité ? Donc une fonctionnalité non finie ?
Les branches sont justement là pour ça !
Il faut imaginer une branche comme une timeline parallèle à votre branche principale
Vous pouvez ainsi vous déplacer de branche en branche
Développer votre fonctionnalité sur l'une d'elle tout en pouvant revenir sur votre branche principale afin d'y effectuer des corrections urgentes, etc.
Lorsque votre fonctionnalité sera finie, vous n'aurez plus qu'à la fusionner à la branche principale.. et le tour est joué !
git branchPour visionner l'ensemble de vos branches, la commande est :
Créons maintenant une nouvelle branche "testing" avec la commande :
git branch testinggit branchNous venons de créer notre première branche !
Le petit "*" à côté de master signifie que HEAD pointe actuellement dessus
Ainsi, tout nouveau commit sera effectué sur master
Pour changer de branche, il suffit d'utiliser git checkout
git checkout testingFaisons maintenant un commit basique, en modifiant par exemple le index.html
Voilà une représentation de ce que nous venons de faire :
GitKraken nous montre le même résultat
Je vous invite d'ailleurs à switcher (checkout) sur master et testing et de faire un coup de git log
Vous remarquerez que testing est bien en avance et que master n'en a aucune idée, chaque branche a sa propre timeline, basée sur sa branche d'origine !
Attention, lorsque vous switchez d'une branche à l'autre, il faut que votre branche courante soit vide de tout changement, sinon git refusera
Pour ce faire, commitez votre travail sur la branche avant de checkout, et c'est bon !
(Il existe une technique appelée remisage qui permet d'éviter de commit, en "mettant de côté" notre code, mais nous n'en parlerons qu'en bonus)
Lorsque vous créez une branche, pour éviter d'avoir toujours à faire ces deux même commandes :
git branch maBranche
git checkout maBrancheVous pouvez directement faire :
git checkout -b maBrancheCette commande va créer la branche et directement vous mettre dessus !
Cas pratique
Nous allons réaliser un petit cas pratique pour expliquer tout ça et pour voir comment fusionner les branches etc.
Ce chapitre est très important car nous allons aborder les problématiques de Merge, Rebase et de résolutions de conflits, souvent incomprises des débutants
Cas pratique
Dans ce cas pratique, nous allons nous baser sur un code "basique" trouvé sur le cours de HTML/CSS d'openclassroom
Vous trouverez les fichiers de code sur votre espace étudiant
Téléchargez les, placez les où vous voulez et initialisez un projet git à la racine
Cas pratique
Vous devriez donc avoir une structure de base comme suit :
Vous pouvez ouvrir l'index.html dans votre navigateur et voir le magnifique site qui va nous servir d'exemple !
Cas pratique
Cas pratique
Une incroyable refonte graphique a été demandée par le client et est prévue pour dans quelques jours !
Il s'agit de :
- Virer la section "à propos de l'auteur"
- Mettre les onglets en bleu
- Mettre les titres des sections en rouge
La qualité du code n'a aucune importance ! Ce ne sont que des exemples un peu idiots pour nous concentrer sur git !
Cas pratique
Commençons par figer la version actuelle sur master pour avoir un bon point de départ
Faites donc un premier commit "Initial commit" avec l'ensemble des fichiers du projet
Une fois que c'est fait, nous pouvons créer une nouvelle branche, pour nous attaquer à la refonte !
Appelons la feature/redesign
git branch feature/redesignCas pratique
Il est très bien vu de nommer toutes ses branches de fonctionnalité feature/leNom ainsi que toutes les branches de fix de bug hotfix/leNom
Cela nous vient d'une convention appelée git flow qui décrit d'excellentes pratiques à appliquer dans sa gestion de git
Nous n'aurons cependant pas le temps d'en parler davantage mais je vous invite grandement à vous renseigner dessus !
Cas pratique
Première étape de la refonte, virer la section "A propos de l'auteur" !
Niveau code, il suffit de retirer ce bout de code du fichier index.html :
Cas pratique
Valider donc ces changements par un petit commit "Remove author section"
git commit -a -m "Remove author section"Notre nouvelle branche est bien en avance sur master avec ce commit
Cas pratique
Deuxième étape de notre refonte, mettre les onglets en bleu !
Niveau code, il suffit de remplacer, dans le fichier style.css, ce code :
nav a
{
font-size: 1.3em;
color: #181818;
padding-bottom: 3px;
text-decoration: none;
}Par :
nav a
{
font-size: 1.3em;
color: blue;
padding-bottom: 3px;
text-decoration: none;
}Cas pratique
Hop, on valide ce petit changement par un commit "Make navigation tabs blue"
git commit -a -m "Make navigation tabs blue"Cas pratique
ALERTE
Avant de réaliser la 3ème étape de la refonte, votre client vous appel, il y a un problème en prod !
En effet, les boutons des réseaux sociaux de l'auteur n'ont jamais été demandé et il veut impérativement que vous les retiriez !
Cas pratique
ALERTE
Au cours de la refonte, vous avez retirer la section auteur, mais il s'agit d'une refonte qui ne doit apparaître que dans quelques jours pour le client, il faut donc pour le moment revenir à l'état initial de la prod et simplement retirer les boutons.
Cas pratique
Nous allons donc laisser de côté pour le moment notre travail sur la refonte, et revenir sur master
git checkout masterRegarder votre site .. rien qu'avec cette commande, il vient de reprendre exactement l'état qu'il avait au début, l'état de la prod !
Maintenant qu'on a compris que les urgences peuvent subvenir n'importe quand, on va être prudent et créer une branche pour notre correction de bug !
Cas pratique
git branch hotfix/remove_buttonsNous pouvons maintenant tranquillement corriger notre bug !
Mais pourquoi avoir créer une nouvelle branche et ne pas avoir corriger directement sur master ?
Imaginons que la correction de ce bug prenne du temps et qu'un autre bug, encore plus critique, arrive en même temps... ça devient compliquer à gérer
Créer une branche par tâche permet de bien séparer le travail et de ne jamais avoir ce genre de souci
Cas pratique
Pour la correction du bug, rien de plus simple, il vous suffit de retirer les dernières balises <p> dans la section auteur
Cas pratique
Testez que tout fonctionne bien, et si c'est le cas, committez votre modification !
git commit -a -m "Remove social network buttons"Cas pratique
Léger rappel sur la situation actuelle de notre dépôt :
Cas pratique
Il est temps de rapatrier votre fix sur la prod !
La notion de Merge
Le cas simple : Fast Forward
Cas pratique
Voici un moment important dans notre cours sur les branches : le fait de "merger" une branche sur une autre
Merger signifie le fait de fusionner une branche à une autre. Autrement dit, rapatrier le code d'une branche sur une autre afin de n'en faire qu'une
Ici, nous allons rapatrier le code de hotfix/remove_buttons sur master afin de "pousser en prod" le correctif
Cas pratique
Lorsque l'on veut faire un merge, il faut toujours se placer sur la branche qui va accueillir l'autre
Plaçons-nous donc sur master
git checkout masterIl ne reste plus ensuite qu'à appliquer la commande git merge en précisant la branche à merger
git merge hotfix/remove_buttonsCas pratique
git merge hotfix/remove_buttonsCas pratique
Etant donné que le commit sur la branche hotfix était directement un descendant du commit sur master, git n'a fait que déplacer le pointeur HEAD d'un cran sur le commit de hotfix
Il s'agit du cas de merge le plus simple qui a pour nom : l'avance rapide ou Fast Forward en anglais
Cas pratique
GitKraken nous indique d'ailleurs bien ce simple changement
Désormais, master ET hotfix pointent vers le même commit
Cas pratique
Nous pouvons dors et déjà supprimer la branche hotfix
Il est important de toujours avoir un système de branche propre pour mieux s'y retrouver
Si une branche n'est plus utile, on la supprime
Pour supprimer, il suffit de lancer la commande :
git branch -d hotfix/remove_buttonsCas pratique
Notons que -d ne fonctionnera pas si la branche ciblée contient des modifications encore non mergée
Il faudra alors utiliser le flag -D si vous souhaité tout de même forcer sa suppression
git branch -D hotfix/remove_buttonsCas pratique
Désormais, nous pouvons reprendre notre travail que nous avions mis de côté sur la branche feature !
git checkout feature/redesignIl ne nous reste plus qu'à mettre les titres de sections en rouge !
Cas pratique
Niveau code, il suffit de remplacer, dans le fichier style.css, ce code :
section h1, footer h1, nav a
{
font-family: Dayrom, serif;
font-weight: normal;
text-transform: uppercase;
}Par :
section h1, footer h1, nav a
{
font-family: Dayrom, serif;
font-weight: normal;
text-transform: uppercase;
}
section h1
{
color: red;
}Cas pratique
Y'a plus qu'à commit !
git commit -a -m "Make section titles red"Et voilà ! Notre superbe refonte graphique est finie, et en plus on s'est payé le luxe de réparer un bug en prod durant la refonte !
Cas pratique
Voici l'état de notre dépôt actuellement !
Cas pratique
Nous remarquons que la branche master a avancé pendant la refonte
C'est un cas très courant et qui soulève une question : comment git va faire pour merger les deux branches ??
Faisons le test, mettons nous sur master et mergons !
git checkout master
git merge feature/redesignCas pratique
Tout ne s'est pas passé comme prévu !
Automatic merge failed nous indique que git n'a pas été capable de gérer la fusion tout seul
En effet, normalement git arrive à gérer les fusions automatiquement, ce qui est très très appréciable
Cas pratique
Il arrive pourtant parfois qu'un même bout de code ait été modifié sur les deux branches, ce qui donne lieu à ce qu'on appelle un conflit
Git ne peut alors choisir à votre place quel code prendre en compte, c'est donc à vous de prendre la décision !
Heureusement, git est sympa et vous indique les conflits existants grâce à la commande git status que l'on connaît bien
Cas pratique
git statusCas pratique
Cette commande nous donne toutes les infos utiles !
Elle nous indique déjà qu'il n'y a que 2 manières de sortir de conflits :
Cas pratique
git status nous indique également que git a parfaitement réussi à fusionner le style.css sur master et celui sur feature, et que cleui-ci est dans le Stage
Enfin, la commande nous indique tous les "Unmerged paths", soit les fichiers causant des conflits : ici uniquement le index.html
Cas pratique
Allons donc dans le fichier index.html
Git a l'air de nous avoir mis pleins de caractères bizarres et compliqués, mais ce n'est pas si difficile à comprendre en fait
Cas pratique
Lors d'un conflit de merge, git va indiquer dans le fichier les changements apportés par les deux branches et vous serez responsable de ne garder que ce qui vous intéresse
Les changements sont séparés par les symboles "===="
Cas pratique
Ici, il faut comprendre que les changements apportés par feature/redesign sont en dessous des "====" (il n'y a en l'occurence rien car feature/design a supprimé cette partie du code)
Les changements apportés par master (HEAD) , eux, sont au dessus des "===="
Cas pratique
Il ne reste donc plus qu'à faire votre choix !
Ici, comme nous voulons rapatrier le code de la refonte graphique, nous allons uniquement laisser le code provenant de feature/redesign !
Soit... rien ! Donc vous pouvez tout supprimer :)
Il arrive parfois d'avoir envie de piocher un peu dans les deux, et c'est tout à fait faisable !
Il faut simplement penser à enlever tous les chevrons écris par git et bien laisser le code que vous souhaiter valider !
Cas pratique
Cette étape consistant à aller chercher, grâce à git status les différents conflits et les réparer peut paraître un peu compliquée au début mais ça vient vite
Vous verrez vite qu'il est primordial de savoir gérer des conflits lors de travail en équipe car ils sont inévitables !
En effet, en équipe, tout le monde travaille de manière parallèle sur des branches séparées, et les merges génèrent souvent des conflits !
Mais vous l'avez vu, rien d'insurmontable !
Cas pratique
Une fois que vous avez pioché le code qu'il vous faut, vous n'avez plus qu'à valider vos changements avec un commit après avoir indexé votre fichier!
git add index.html
git commit -m "Merge redesign into master"Maintenant que nous n'avons plus besoin de la branche feature/redesign, on peut la supprimer
git branch -d feature/redesignCas pratique
Et voilà !
Cas pratique
Notons qu'en cas de merge non fast-forward, git garde en mémoire le fait que le code provenait d'une autre branche comme le schéma l'indique
C'est assez intéressant car cela permet de vraiment comprendre tout l'historique du projet !
Cas pratique
Il est également possible de forcer l'utilisation d'un commit de merge si l'on souhaite garder un historique complet et empêcher le fast-forward
Pour ce faire, il suffit de rajouter le flag --no-ff à la commande de merge
git merge --no-ff maBrancheQue le Fast-forward soit possible ou non, git ne l'utilisera alors pas et forcera l'utilisation d'un commit de merge
Cas pratique
Cas pratique
Il nous reste un tout dernier effort pour comprendre le merge
Nous avons vu qu'un merge fast-forward se contente d'avancer le pointeur de la branche d'acceuil
Nous avons vu que lors d'un merge avec conflit, nous devons les réparer puis créer un commit de merge
Cas pratique
Que se passe-t-il lors d'un merge non fast-forward (où la branche d'accueil a avancé parallèlement) mais sans conflits ?
Pour faire le test, créez une branche test, modifiez-y le fichier style.css, commit, puis revenez sur master et faites un changement sur index.html puis commit
git checkout -b test
# Modifiez le fichier css
git commit -a -m "Update css"
git checkout master
# Modifiez le fichier html
git commit -a -m "Update html"
git merge testCas pratique
Git a parfaitement réussi à fusionner les deux branches car il n'y avait aucun conflit !
Il vous demande alors simplement de rentrer votre message de commit (ou garder celui-ci) puis votre merge sera effectué !
Cas pratique
git branch -d test(Le cas rebase)
Il existe une manière d'aplatir 2 branches et de réaliser un merge fast-forward même si, à priori, les deux branches ont avancé parallèlement grâce au principe du rebase
Le rebase est une technique dangereuse dans le sens ou vous allez manipuler et modifier l'historique
Toutefois elle a pour avantage de pouvoir rendre l'historique un peu plus joli et digeste
(Le cas rebase)
En effet, le merge a pour "défaut" de garder l'historique des branches et de rajouter un commit de merge en plus
Du coup, pour voir les modifications apportées par les différentes branches, il faut suivre l'évolutions de ces branches car les commits y sont restés et ce n'est parfois pas pratique
L'idée derrière le rebase sera donc de rejouer la totalité des commits présents sur la branche pour les mettre sur master et donc aplatir complètement l'historique
(Le cas rebase)
Alors comment faire ?
Tout d'abord, créons un petit cas avec une nouvelle branche toto
Faites-y un commit ajoutant un fichier toto.txt
Revenez sur master et faites un commit ajoutant un fichier tata.txt
Retournez sur la branche toto
git rebase master
git checkout master
git merge toto(Le cas rebase)
(Le cas rebase)
Grâce à cette commande, vous venez de rejouer tous les commits de la branche master sur la branche toto !
Ensuite il a suffit de revenir sur master et de faire un simple merge, qui sera alors fast-forward car toto a été mis à jour aves les commits de master !
(Le cas rebase)
Depuis master, supprimer toto et vous verrez que votre historique git a l'air de fonctionner comme s'il n'y avait jamais eu de branche toto !
Cette technique, en supprimant le commit de merge et en rejouant les commits sur la branche ciblée permet un historique plus lisible et joli mais empêche en contrepartie de comprendre l'historique des branches du projet
(Le cas rebase)
Un peu comme pour le merge, nous avons vu ici le cas d'un Rebase sans conflits
Nous allons revenir à l'état précédent tous ces changements pour voir un cas un peu plus complexe mais instructif
Faites donc un coup de reset --hard sur le commit précédent ces changements, b1518cd7860864382ff8dc3ab2add5ca14e6cb42 pour moi
(Le cas rebase)
Ensuite, nous allons créer de nouveau une branche toto et faire les changements suivants dessus
touch toto.txt
git add --all
git commit -am "Add toto.txt"
// Modifiez ensuite le index.html en
// changeant le premier titre h2
git commit -am "Change title toto"(Le cas rebase)
Revenez sur master, modifier le premier titre h2 comme bon vous semble et commitez
git checkout master
// Modifiez le titre h2
git commit -am "Update title"(Le cas rebase)
Revenez sur toto, faites un rebase mais cette fois-ci en ajoutant un petit flag -i (pour "interactive")
git rebase -i master(Le cas rebase)
(Le cas rebase)
N'ayez pas peur !
La commande rebase est très puissante et nous donne l'occasion de réorganiser nos commits ! En effet, nous allons appliquer nos commits sur master, mais avant tout, on peut y faire un peu de ménage
Si vous ne changez rien dans l'écran qui vous est proposé, les 2 commits seront pris en compte
Notez que les commits vous sont présentés dans le sens inverse du log : Le plus récent est ici en bas de page
(Le cas rebase)
Mettons que nous souhaitions fusionner ces commits pour n'en faire qu'un seule (représentatif de tout le travail effectué sur la branche toto)
Il suffit pour cela de remplacer le pick devant le second commit par un squash (ou s uniquement)
Nous indiquons alors à git que nous souhaitons fusionner le seconds commit dans le premier ! Ceci est possible pour un nombre X de commits
Nous pourrions également renommer le premier commit pour un historique plus lisible avec le flag reword
(Le cas rebase)
Ce qui nous donne :
Sauvegardez et quittez
Attention, petite coquille ici : ne changez pas le nom du premier commit, ou alors placez un reword ou un r à la place du commit pour préciser que vous voulez le renommer
(Le cas rebase)
Nous avons des conflits !
Eh oui, merge et rebase ont de toute manière le même souci lorsqu'une même portion de code est modifiée sur les deux branches, c'est à nous de dire quel code prendre !
(Le cas rebase)
Git nous explique dans ce message ce qu'il nous est possible de faire :
Corrigeons donc nos conflits !
(Le cas rebase)
git statusLe problème semble venir d'index.html
(Le cas rebase)
Prenons par exemple les changements provenant de la branche toto et donc du commit daa452a
(Le cas rebase)
git add index.html
git rebase --continue
// Choisissez le message de commit qui vous va Nous avons donc normalement maintenant la branche toto qui a bien récupérer les modifications de master et les a incorporé à son historique
Attention, rappelez-vous que nous avions souhaiter modifier le premier message de commit lors du squash. S'il y a des conflits, il faut redonner ici le message que vous souhaitiez donner
(Le cas rebase)
(Le cas rebase)
Ainsi, nous allons pouvoir retourner sur master et faire un merge fast-forward pour un historique propre
git checkout master
git merge totoEt voilà ! Nous pouvons dorénavant supprimer la branche toto et l'historique, grâce à ce petit merge fast-forward ne fera plus apparaître la branche toto
(Le cas rebase)
Pour résumer, le rebase permet donc de rejouer les commits de la branche ciblée sur la branche courante afin de ré-écrire l'historique
Une fois l'historique propre, la brache cible peut donc merger votre branche sans souci et en faisant donc un fast-forward qui rend invisible la branche dans l'historique de git
Si vous avez tenu jusque là et bien compris les différentes notions, c'est vraiment top !
Les notions de merge sont toujours un peu compliquées au début, mais relisez ce cas pratique 2 ou 3 fois et tout devrait être clair :)
GitHub, git remote (-v, add), git branch -r, git push, git pull (--rebase), git fetch
Si vous êtes arrivé à ce chapitre en ayant bien compris les précédents, c'est que vous êtes déjà bien à l'aise avec git en local
Je précise git en local car souvenez-vous du début du cours, nous parlions de git comme d'un système distribué
Jusqu'à présent, vous avez appris tout un tas de choses sur git vous permettant de très bien organiser votre travail de manière locale, sur votre ordinateur
Nous l'avons vu, un dépôt git n'est rien d'autre qu'un dossier dans lequel nous avons fait un git init et qui contient donc, au fur et à mesure, tout l'historique de notre projet
Ce que nous appelons un remote n'est en fait rien d'autre qu'un dossier git à distance !
Sauf que ce dossier en question peut être n'importe où ! Il peut s'agit d'un autre dossier sur votre ordinateur, sur une clé USB, sur l'un de vos serveurs à distance, etc.
L'intérêt d'un tel procédé est que votre dépôt local et votre dépôt distant pourront communiquer entre eux pour se partager l'historique !
Ainsi, vous pourrez mettre facilement la totalité de votre code source sur un dépôt à distance
Cette particularité est incroyable car, tout en vous permettant de travailler avec l'historique complet sur votre ordinateur, elle vous permet de tout mettre en lieu sûr à distance (en cas de perte de données de votre ordinateur) mais également de partager votre code avec vos collègues ou au monde entier !
Le remote correspond donc à ce fameux dossier à distance !
Il est d'ailleurs tout à fait possible d'avoir plusieurs remote pour un même projet, même si c'est plus rare
Mis à part quelques commandes vous permettant d'interagir avec le dépôt distant, vous allez vite comprendre que, ce remote n'étant qu'un dossier git comme un autre, ce que vous avez appris jusqu'à présent correspond à 90% de ce qu'il faut savoir sur git !
Nous l'avons dit, un dépôt distant peut être n'importe où !
Le plus pratique, pour pouvoir travailler en équipe, est d'avoir ce dépôt sur un serveur distant et que chacun y ait accès de chez lui
Seulement gérer un serveur, sa sécurité, etc.. n'est pas toujours simple et sympa
C'est là que Github intervient !
Github permet d'héberger gratuitement vos dépôts distants !
L'outil vient en plus avec une interface graphique et des outils incroyablement utiles et pertinents !
Attention, il existe de nombreux autres outils du même genre !
Chacun a ses avantages et ses défauts mais ils sont tous identiques sur le principe
Nous prendrons pour ce cours GitHub car il s'agit du plus utilisé à ce jour dans le monde et .. voilà
Je vous invite donc tous à aller sur www.github.com et de vous y créer un compte
Essayez d'utiliser la même adresse mail que celle renseignée dans les configs de votre git local !
Sinon, changez simplement celle de git local, ce n'est pas grave :)
Nous allons créer notre premier dépôt sur GitHub !
Une fois que c'est fini, vous pouvez cliquer sur le bouton "Create repository" !
Vous arriverez ensuite sur l'écran principal de votre dépôt et vous pouvez remarquer que GitHub vous mâche déjà le travail pour la suite des opérations
Mais comme il y a des commandes que vous ne connaissez pas, nous allons d'abord les étudier ensemble avant de continuer
Reprenons le dépôt local où nous l'avions laissé au chapitre précédent
Un dépôt distant n'est constitué, pour git, que de 2 informations importantes :
Ajouter un dépôt distant à un git local est très simple, il suffit de faire la commande :
git remote add nomDepot urlDepotAttention, le nom du dépôt ne doit pas forcément correspondre au nom que vous avez mis sur GitHub. Il ne s'agit que de son "identifiant" pour le git local (au cas ou vous auriez plusieurs dépôts distants)
La norme veut que le principal dépôt distant d'un projet git se nomme "origin"
Créons donc notre dépôt distant !
Pour trouver l'URL associée à votre dépôt GitHub :
git remote add origin https://github.com/ThomasEcalle/cours_git_2019.gitLa commande git remote -v permet de lister tous nos remotes :
git remote -vNous avons donc bien notre remote origin qui a été mis en place et qui pointe vers notre dépôt GitHub !
L'url de fetch est l'url qui va nous permettre de récupérer localement tous les changements fais sur le dépôt distant
Il est possible de lister l'ensemble des branches présentes sur le dépôt distant grâce à :
git branch -rLa commande n'affiche rien pour le moment car nous n'avons tout simplement rien sur le dépôt distant
Nous allons donc commencer par "pousser" nos changements sur le remote
Pour ce faire, voici la commande :
git push origin masterComprendre "nous poussons l'état actuel du projet (donc la dernière version de la branche master) sur le branche master du dépôt distant origin"
Une fois que c'est fait, mettez à jour votre dépôt github !
Côté dépôt local, je peux voir les changements sur le remote et je peux m'amuser à pousser de nouvelles choses !
git branch -rgit branch test
git push origin test
git branch -rVoilà donc comment pousser du contenu sur un dépôt distant !
Maintenant, nous allons voir comment faire pour récupérer du contenu et donc rentrer un peu plus dans le travail en équipe
Tout d'abord créons un dossier parallèle qui représentera le dépôt local de Bob, un développeur de l'équipe
Bien sûr, dans la vraie vie, Bob a son dépôt sur son ordi, mais ça revient exactement au même, deux dépôts git sont complètements indépendants
cd ..
Il y a une commande très simple pour commencer un dépôt git à partir d'un déjà existant : git clone
La commande a simplement besoin de l'URL du dépôt et, éventuellement, du nom du dossier dans lequel le mettre si différend du nom du dépôt distant, et tout est bon
git clone https://github.com/ThomasEcalle/cours_git_2019 repo_de_bobBob a maintenant accès en local à la totalité du code présent sur le remote !!
Bob peut donc faire son travail à lui, puis le pousser, etc.
Evidement, pour respecter tout ce qu'on a vu précédemment, Bob devrait faire une petite branche, faire son travail, et ne la merger à master qu'une fois que tout est bon
Pour ce cours sur le remote, disons que Bob va simplement modifier le index.html et pousser ses modifications sur origin master
Modifions le premier titre <h2> du index.html en y mettant
<h2>Voici le nouveau titre</h2>
git commit -a -m "Update Home title"C'est bon, Bob a fait tout ce qu'il avait à faire, il peut pousser sur le remote !
git push origin masterLe dernier commit de Bob a bien été pris en compte !
Attention, ici GitHub ne fait évidemment pas la différence entre "Bob" et "moi" puisque les configs de mon git local sont généralisées et indiquent l'adresse email correspondant à ce compte Github
C'est donc tout à fait normal, et dans la vrai vie chaque développeur a ses propres configs pour signer différemment ses commits
Voyons maintenant, en retournant sur mon premier dépôt, comment je peux récupérer les nouveautés apportées par Bob !
La commande pour récupérer les informations d'une branche en remote est très simple
S'il fallait "pousser" pour envoyer les informations, il faut "tirer" pour les récupérer !
git pull origin masterEt voilà !
Nous venons de récupérer le commit que Bob a fait et tout va bien dans le meilleur des mondes !
Si vous observez bien le message lors du pull, il indique un "Fast-forward"
Cela devrait vous rappeler le cours sur les branches et les problèmes potentiels de merge !
Il faut comprendre que la branche master à distance n'est qu'une branche comme une autre ! Le pull que vous venez d'effectuer n'est finalement qu'une fusion de 2 branches comme nous en avions l'haitude !
En effet, ici, nous avons attendu sagement que Bob fasse son commit avant de pull. Mais dans la vrai vie, il se peut que nous avancions en même temps
Comme toute fusion, il y a des cas comme celui-ci ou la branche cible est juste en avance par rapport à l'actuelle et qui se résume donc en un simple fast-forward
Mais ce n'est pas toujours le cas
Faisons un test !
Revenez sur le dépôt de Bob, créez un fichier test.txt puis commitez et poussez le
Revenez sur le dépôt principal, faites de même mais en appelant le fichier test2.txt et sans avoir pull avant
// Allez sur le dépôt de Bob
touch test.txt
git add --all
git commit -m "Add test.txt"
git push origin master
// Allez sur le dépôt initial
touch test2.txt
git add --all
git commit -m "Add test2.txt"
git push origin masterUne erreur ? Que s'est-il passé ?
Le message de git est assez explicite mais voici la version française et résumée :
Vous souhaitez apporter des changements à la branche master de votre dépôt distant alors déjà que celle-ci est en avance sur votre branche
Il nous faudra donc toujours impérativement tirer les dernières modifications du remote avant de pouvoir le mettre à jour !
Ainsi, comme nous l'avons vu :
git pull origin masterVous devez ensuite préciser un nom de commit de merge !
Rappelez vous le cours sur les branches, git n'a pas été capable de faire un Fast-forward mais a tout de même réussi à gérer le merge.
Cependant , il vous demande tout de même de donner un nom à votre commit de merge
Je garde personnellement celui proposé et valide mon commit
Finalement, les problèmes de merge sont exactement les même que pour les branches !
Ce pull aurait également pu présenter des conflits, mais je vous laisse relire le cours sur les branches pour la gestion des conflits, finalement il s'agit exactement du même processus
Notons que si git utilise le merge par défaut lors d'un pull, il est tout à fait possible de préciser que vous souhaitez gérer la fusion via un rebase :
git pull --rebase origin masterIl est assez fréquent que les gens utilsent davantage le rebase lorsqu'ils tirent les modifications d'une branche en remote
Cela a en effet comme avantage de ne pas se retrouver avec X commits de merge un peu polluants dans l'historique général
Lorsque vous faites un git pull, git va :
Ce qu'il faut comprendre, c'est que les branches remote (origin) sont des branches comme les vôtre
Il est donc possible de simplement les récupérer en local et les voir
Pour récupérer en local les branches distantes, vous pouvez faire un coup de git fetch
Vous aurez ensuite accès, en local, aux branches distantes
Pour les différencier, celles-ci sont préfixées par origin/
Vous pouvez ensuite vous déplacer tranquillement sur les branches via des checkout et ainsi visionner sur votre local les changements actuels sur le remote
Faites le test en changeant quelque chose uniquement depuis le remote, soit en passant par un autre dépôt, soit en changeant directement sur Github (démo à l'oral)
Faites ensuite un git fetch, puis voyez que les changement sont arrivés !
Le remisage avec git stash
Le remisage est une pratique qui va permettre de stocker nos modifications dans le but de les ré-appliquer plus tard
Dans quel cas est-ce utile ?
Pour l'exemple, nous repartirons d'un nouveau projet git avec comme commit initial la mise en place du petit site que nous avons utiliser pour le chapitre sur les branches
Téléchargez donc les sources et mettez les dans un dossier dans lequel vous initialiserez ensuite le projet git et ferez votre premier commit
Une fois que c'est fait, imaginons que nous souhaitions modifier quelques trucs dans l'index.html en changeant par exemple le titre et d'autres infos (on s'en fiche un peu pour l'exemple)
Mais surtout, ne commitez pas, vous êtes en plein dans votre tâche)
Imaginez maintenant qu'on vous demande d'urgence de modifier le titre de h2 du site et de commit immédiatement la modification
Le problème est que si vous êtes dans un état instable de votre code, le commit est une mauvaise pratique (chaque commit doit représenter un état stable du code)
Or, vous devez commit la modification du titre.. Alorts que faire ?
C'est là que git stash intervient !
Voyez le comme un petit panier dans lequel vous allez stocker vos changements (trackés) dans l'idée de les appliquer de nouveau ensuite
Allez, y faites donc la commande :
git stashTous les changements que vous n'avez pas commit ont été mis de côté et vous pouvez faire votre commit avec votre modification de titre !
Le stockage des changements agit sous forme de pile et nous pouvons voir l'ensemble des stashs du dépôt avec la commande
git stash listOn voit que c'est le stash 0 et que ses changements sont basés à partir du commit aa01bc4 sur master
Modifiez le titre et commitez comme demandé
Nous allons maintenant pouvoir reprendre où nous en étions notre travail !
git stash applyCette commande permet de rejouer le dernier stash sur le commit courant, nos modifications sont donc revenues !
Une fois que c'est bon, vous pouvez supprimer le dernier stash avec
git stash dropImaginons que nous créons plusieurs stash, il est possible de leur donner des noms plus compréhensible avec :
git stash save Work on header
git stash listAttention, pour les commandes à venir, le véritable identifiant est le label "stash@{numero}" de votre stash, son nom n'étant pas unique
Pour voir les changements compris dans un stash de la liste (exemple ici avec le premier, le seul) :
git stash show -p stash@{0}Pour appliquer un stash spécifique
git stash apply stash@{0}Pour supprimer un stash spécifique
git stash drop stash@{0}Pour apply puis drop en même temps, il existe la commande pop qui fonctionne de la même manière (en indiquant un stash en particulier ou en prenant automatiquement le dernier)
git stash pop
// ou encore
git stash pop stash@{0}Cette commande applique le dernier stash et le supprime automatiquement de la liste
Attention avec le pop, si vous faites un stash pour changer de branche tranquillement, le fait de l'appliquer ensuite peut potentiellement créer des conflits
Il est toujours rassurant d'avoir le stash encore en réserve tant qu'on a pas complètement corriger tous les conflits !
Je conseillerai donc plutôt de rester sur apply puis drop
Bien sûr, le stash est beaucoup utilisé pour les changements de branche lorsque git vous interdit de checkout à cause d'un Stage non vide
git stash vous permet alors de mettre de côté les changements, switcher sur votre branche puis appliquer ces changements (ou non, mais en tout cas les changements sont bien stockés de manière à part des branches)
git rebase pour réorganiser ses commits
Nous avons vu que la commande git rebase permet de modifier l'historique afin de l'aplatir lors de fusion de branches
Il est également possible de faire un Rebase sur un commit de notre branche courante
Nous pourrons alors modifier à notre guise les noms, ordre et fusion des commits suivants celui visé
Imaginons que nos commits ressemblent à ça :
Les deux derniers commits pourraient être fusionner pour une meilleure lecture car le dernier est juste un oubli
Nous allons utiliser le git rebase !
Il faut toujours se baser sur le commit précédent le dernier que nous voulons modifier
Ici, nous souhaitons modifier jusqu'au commit 6035ae1, nous allons donc faire le rebase sur le commit 69a90e7
git rebase -i 69a90e7On remarque toujours que l'ordre des commits a été inversé ! Le commit le plus récent est en bas de liste
Comme pour les branches, si nous voulons fusionner les deux commits, nous n'avons qu'à écrire squash (ou s) à la place du pick sur la seconde ligne
Cela indique à git que nous souhaitons fusionner le second commit avec le premier
Si vous sauvegardez et quitter l'éditeur, la modification sera prise en compte !
git rebase nous permet ainsi de modifier énormément de choses dans notre historique !
Prenons un autre exemple
Nous souhaitons ici réordonner les différents commits
git rebase -i 1cc93b4Raccourcis VIM pour déplacer le commit du fichier 3 au dessus de celui du fichier 4 : dd k P
Sauvegardez et quittez, tout est bien en ordre !
Enfin, ces 4 commits sont un peu inutiles, fusionnons-les en un commit avec un message plus expressif
(Nous utiliserons reword (ou r) plutôt que pick pour éditer le message d'un commit)
git rebase -i 1cc93b4Sauvegardez - quittez
Remettez le bon message (comme nous modifions tous les commits du rebase, même le premier, il demande de valider cela par un nouveau commit qu'il va créer)
Effacez tout sauf le bon message, et sauvegardez - quittez ... Et voilà !