Cours

Git / GitHub

Présentation

Présentation personnelle

Présentation du cours

Règles générales concernant :

  • les retards
  • l'attention en cours
  • les supports de cours
  • la notation
    • interrogation écrite
    • TP à faire en groupe

Pourquoi ce cours ?

Pourquoi ce cours ?

  • Cours indispensable pour les développeurs
     
  • Néanmoins très intéressant pour les autres (si si vous verrez)

Le versioning

Pourquoi faire du versioning ?

Pourquoi faire du versioning

Prenons l'exemple d'un projet de développement

Comment travaillez-vous en équipe, voire même seul pour :

  • Valider des versions stables dans votre avancée ?
  • Travailler sur différentes fonctionnalités en parallèle ?
  • Gérer un code de production et un code de dev ?
  • Voir l'état de votre site web "à l'époque de la version X" ?
  • Revenir sur une décision que vous aviez prise ?
  • Fusionner le travail que vous avez fait avec celui de votre collègue ?

Le versioning

Le "versioning" est un terme regroupant différentes fonctionnalités :

  • Historique des modifications apportées à un projet
     
  • Possibilité de se déplacer dans la timeline (se placer à un point précis de l’historique, revenir en arrière, etc.)
     
  • Aide au travail en équipe grâce à un système de branches (aide à la résolution de conflits)

Il existe de nombreux gestionnaires de versions (VCS)

  • Git
  • Mercurial
  • Subversion
  • ...

Alors pourquoi Git ?

Pourquoi Git ?

Un système distribué

Un système distribué

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é

système non distribué

Système non distribué

  • Beaucoup trop dépendant du serveur distant
  • Lent et nécessite internet, dans le sens où il faut toujours communiquer avec le serveur

Système distribué

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

L'anecdote pas utile

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 !

Dernières infos avant de commencer

  • 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/

L'installation

L'installation

Windows

L'installation

Windows

  • Aller sur git-scm.com
  • Télécharger l'installeur
  • Lancer l'installeur

Pour toutes les étapes non mentionnées ci-après, faire juste "suivant"

L'installation

Windows

Placer le où vous voulez

L'installation

Windows

On décoche ce qui nous sert à rien

L'installation

Windows

  • Utilisation de git bash seulement
     
  • Utilisation de git via l'invite de commande windows
     
  • Installations de quelques commandes linux en plus

Choisissez comme bon vous semble !

L'installation

Windows

Laissez la première

L'installation

Windows

Une fois tout installé, faites un coup de :

Dans votre invite de commande

git help

Si la doc de Git apparaît, tout est OK !

L'installation

Windows

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

L'installation

Linux

L'installation

Linux

sudo apt-get install git
git --help

L'installation

Mac

L'installation

Mac

  • Aller sur git-scm.com
  • Téléchargez le DMG
  • Installez le

Si le PKG ne peut pas être installé car ne provenant pas d'un développeur etc.. clic droit + ouvrir

git help

La configuration

La configuration de git

Git 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

La configuration de git

git config --global user.name "Bob"
git config --global user.email bob@burger.com

La configuration de git

git config --global --list

Pour afficher l'ensemble des variables globales renseignées :

La configuration de git

Notons 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 configuration de git

La commande pour voir l'ensemble de votre configuration git est la suivante :

git config --list

Une aide détaillée

Il 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 config

Initialiser un dépôt

git init

Initialiser un dépôt

git init

git 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

 Staging

/

l'indexation

git status, git add, git reset <file>, README.md, .gitignore

La première commande que nous allons apprendre est la commande suivante :

git status

Cette commande permet de connaître l'état actuel des fichiers dans le projet 

Lançons la commande dans un dépôt vide

git status

La commande nous indique 3 choses :

  • Nous sommes actuellement sur la branche master (nous y reviendrons dans le chapitre sur les branches)
  • Il n'y a pour le moment aucun commit (nous y reviendrons très vite)
  • Il n'y a actuellement rien à commiter dans notre dépôt

Créons un fichier README.md dans le dépôt :

touch README.md
git status

Ici, 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 add

git add peut prendre comme paramètre :

  • le chemin du fichier à ajouter
  • le chemin d'un dossier à ajouter (les sous-fichiers seront ajoutés aussi)
  • un pattern du genre "*.html" (tous les fichiers avec l'extension html
  • --all ou . : tous les fichiers

Ainsi, nous pouvons faire :

git add README.md
git status

ATTENTION, 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 status

Cela 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 status

Le 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>

README.md

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

.gitignore

Il arrive souvent que certains fichiers du projet ne doivent pas être versionnés pour plusieurs raisons :

  • ce sont des fichiers à ne surtout pas divulguer (rappelons que l'objectif de git est aussi d'avoir un dépôt distant)
  • ce sont des fichiers temporaires dans votre dépôt mais absolument pas nécessaires au projet
  • ce sont des fichiers de configuration de votre IDE (parfois versionnés, parfois non)

.gitignore

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

.gitignore

créer fichier .gitignore
*.temp
toto/*

Ne serons plus pris en compte :

  • Les fichiers avec l'extension ".temp"
  • La totalité du dossier "toto"

Attention à également indexer le fichier .gitignore dans votre projet !

Nous avons donc appris dans ce chapitre :

  • la commande git status permet de connaître l'état du dépôt à tout moment et est capitale
     
  • les fichiers doivent être indexés ("staggés" en franglais) pour être pris en compte à la prochaine version
     
  • la mise en Staging se fait via git add <fichier, ...>
     
  • on retire un fichier ou dossier du Stage grâce à git reset <fichier ou dossier>
     
  • le fichier .gitignore nous permet de renseigner les chemins de fichiers à ne jamais versionner

Pour finir, schématisons toute cette histoire de Stage :

Enregistrer des modifications

git commit (-m, -a)

Qu'est-ce qu'un commit ?

Qu'est-ce qu'un commit ?

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é

Qu'est-ce qu'un commit ?

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

Créer un commit

La commande magique n'est autre que ... :

git commit

Votre terminal vous demandera ensuite (via votre éditeur par défaut) de renseigner un message de commit

Créer un 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 status

Rien à commit, le projet est clean, tout va bien !

Jouons avec le Stage

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.html

Jouons avec le Stage

git status

Modifier votre fichier "index.html", ajoutez-le au Stage et commitez

Jouons avec le Stage

git add index.html
git status
git commit -m "Add index.html"

Jouons avec le Stage

git status

Nous voyons donc que le commit n'a bien validé QUE les fichiers précédemment indexés

Jouons avec le Stage

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

Jouons avec le Stage

Ajoutons maintenant d'autres fichiers, header.html et footer.html

git status

Jouons avec le Stage

git add --all

Traquons tous ces fichiers :

Modifions ensuite footer.html et voyons l'état du dépôt :

Jouons avec le Stage

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"

Visualiser l'historique

git log (--oneline, -p), git diff (--cached)

git log

La commande de base pour visualiser l'historique du dépôt est :

git log

git log permet de lister l'ensemble des commit de la branche en cours

Nous y trouvons :

  • leur ID (sha-1)
  • leur auteur
  • leur date
  • leur message

Par exemple, git log sur notre dépôt actuel donne :

git log

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

git log

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 --oneline

git log --oneline

Il 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.html

Cette commande existe aussi en version raccourcie

git log --oneline -p index.html

git log -p

Ré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

git log -p

git diff

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 diff

git diff

Mê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

git diff

Attention : git diff ne fonctionne que pour les fichiers non indexés !

En effet, si vous faites :

git add index.html
git diff

Vous ne verrez... rien !

En effet, à partir du moment ou un fichier est indexé, il n'apparaît plus dans le git diff

git diff

Pour visualiser les  différences entre le Stage et le dernier commit, la commande est :

git diff --cached

Finissez ce chapitre en commitant les modifications sur le fichier index.html

git commit -m "Update index.html"

Visualiser l'historique

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

Se déplacer dans l'historique

(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 ? 

  • Voir le code avant la dernière mise à jour qui a fait tout planter ?
     
  • Jeter un coup d'oeil au projet avant que votre collègue fasse sa "fameuse" refacto qui a tout casser ?
     
  • Ou simplement tout effacer et repartir sur de meilleures base à un moment propre du projet ?

 

git checkout

git checkout

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

git checkout ae8dbf57b9735c777f5465ef2b860e436eb9bd80

Ce SHA-1 correspond en fait à mon commit précédent la modification de mon fichier index.html !

Voici ce que nous dit git :

git checkout

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 :

git checkout

Faisons un petit coup de :

git log --oneline

Hummmm.. 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 !

git checkout

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 !

git checkout

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

git checkout

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 master

Et voilà ! Nous étions sur le commit et nous avons switcher sur master !

git checkout

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

git checkout

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.html

En 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 !)

git checkout

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"

git checkout

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 ? 

git checkout

Il existe une commande permettant de réinitialiser un fichier à l'état qu'il avait au dernier commit !

git checkout -- fileName

Attention cependant ! En effectuant cette commande vous revenez bien à l'état du dernier commit, mais vous perdez absolument tous les changements !

Modifier l'historique

Un grand pouvoir implique de grandes responsabilités

git revert, git reset (--soft, --mixed, --hard), git commit --amend

ATTENTION

ATTENTION

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

git revert

git revert

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

git revert

Voici les commits de notre dépôt

git revert

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

git revert

Tentons de le revert

git revert 440156b

Il vous sera demandé un message de commit, laissez celui par défaut et validez

Voici le résultat !

git revert

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

git revert 0e72951

Tout 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 !

git reset

git reset

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

git reset

Premier exemple dans les changements courants

Modifions quelques fichiers et faisons les commandes : 

git add --all
git status

git reset

Testons maintenant le git reset

git reset
git status

git reset

Nous 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

git reset

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 --hard
git status

git reset

Nous 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)

git reset sur un commit

git reset sur un commit

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

git reset sur un commit

Ok, imaginons maintenant que je veuille revenir à l'état du commit 0f55c5f

git reset 0f55c5f
git log --oneline

Nous avons bien remonté le temps jusqu'au commit 0f55c5f, effaçant les commits suivants

git reset sur un commit

Cependant, comme nous étions en mode --mixed, nous avons gardé les modifications

git status

git reset sur un commit

Nous pouvons donc les commit

git commit -a -m "Make better index.html message"

git reset sur un commit

Testons maintenant le flag --hard en revenant au commit ae8dbf5 

git reset --hard ae8dbf5

Nous sommes bien revenus au niveau du commit, en ayant supprimé toutes les modifications depuis

modifier le dernier commit

modifier le dernier commit

Il peut arriver, pour diverses raisons, que vous ayez besoin de modifier un commit que vous venez de faire

  • Peut-être l'avez-vous mal nommé ?
     
  • Peut-être avez vous oublier des changements qui y étaient associés ?

La commande git commit --amend est là pour ça !

git commit --amend

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 

git commit --amend

Modifiez votre index.html pour y ajouter le "!"

git add --all
git commit --amend

Votre é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

Les branches

git branch (name, -d name), git checkout, git merge (FF, no FF), git rebase (-i)

Les branches

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

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é !

Les branches

git branch

Pour visionner l'ensemble de vos branches, la commande est :

Créons maintenant une nouvelle branche "testing" avec la commande :

git branch testing
git branch

Les branches

Nous 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 testing

Nous venons de nous déplacer vers testing !

Les branches

Faisons maintenant un commit basique, en modifiant par exemple le index.html

Voilà une représentation de ce que nous venons de faire :

Les branches

 

GitKraken nous montre le même résultat

  • HEAD pointe bien sur la branche testing
  • master est bien restée sur le commit précédent

Les branches

 

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 !

Les branches

 

(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)

Les branches

 

Lorsque vous créez une branche, pour éviter d'avoir toujours à faire ces deux même commandes :

Infos pratiques

git branch maBranche
git checkout maBranche

Vous pouvez directement faire :

git checkout -b maBranche

Cette commande va créer la branche et directement vous mettre dessus !

Les branches

 

BON, ok, c'est cool ..

Mais ça sert à quoi dans la vrai vie ???

Les branches

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

Les branches

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

Les branches

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 !

Les branches

Cas pratique

Qu'allons nous faire ?

Les branches

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 !

Les branches

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/redesign

Les branches

Cas pratique

Infos pratiques

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 !

Les branches

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 :

Les branches

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

Les branches

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;
}

Les branches

Cas pratique

Hop, on valide ce petit changement par un commit "Make navigation tabs blue"

git commit -a -m "Make navigation tabs blue"

Les branches

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 !

Les branches

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.

Les branches

Cas pratique

Nous allons donc laisser de côté pour le moment notre travail sur la refonte, et revenir sur master

git checkout master

Regarder 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 !

Les branches

Cas pratique

git branch hotfix/remove_buttons

Nous 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

Les branches

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

Les branches

Cas pratique

Testez que tout fonctionne bien, et si c'est le cas, committez votre modification !

git commit -a -m "Remove social network buttons"

Les branches

Cas pratique

Léger rappel sur la situation actuelle de notre dépôt :

  • master est bien restée sur le commit initial
  • feature/redesign suis sa route en parallèle
  • hotfix/remove_buttons est à 1 commit d'avance sur master

Les branches

Cas pratique

Il est temps de rapatrier votre fix sur la prod !

La notion de Merge

Le cas simple : Fast Forward

Les branches

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

Les branches

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 master

Il ne reste plus ensuite qu'à appliquer la commande git merge en précisant la branche à merger

git merge hotfix/remove_buttons

Les branches

Cas pratique

git merge hotfix/remove_buttons

Les branches

Cas pratique

Que s'est-il passé ?

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

Les branches

Cas pratique

GitKraken nous indique d'ailleurs bien ce simple changement

Désormais, master ET hotfix pointent vers le même commit

Les branches

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_buttons

Les branches

Cas 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_buttons

Les branches

Cas pratique

Désormais, nous pouvons reprendre notre travail que nous avions mis de côté sur la branche feature  !

git checkout feature/redesign

Il ne nous reste plus qu'à mettre les titres de sections en rouge !

Les branches

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;
}

Les branches

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 !

Les branches

Cas pratique

Voici l'état de notre dépôt actuellement !

Les branches

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/redesign

Les branches

Cas 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

Les branches

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

Les branches

Cas pratique

git status

Les branches

Cas 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 :

  • Les régler puis faire un commit de résolution
     
  • abandonner la résolution de conflits avec la commande git merge --abort

Les branches

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

Les branches

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

Les branches

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 "===="

Les branches

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 "===="

Les branches

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 !

Les branches

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 !

Les branches

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/redesign

Les branches

Cas pratique

Et voilà !

Les branches

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 !

Les branches

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 maBranche

Que le Fast-forward soit possible ou non, git ne l'utilisera alors pas et forcera l'utilisation d'un commit de merge

Les branches

Cas pratique

Pour finir sur le merge

Les branches

Cas pratique

Pour finir sur le merge

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

Les branches

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 test

Les branches

Cas 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é !

Les branches

Cas pratique

git branch -d test

Les branches

(Le cas rebase)

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

Les branches

(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

Les branches

(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

Les branches

(Le cas rebase)

Les branches

(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 !

Les branches

(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

Les branches

(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

Les branches

(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"

Les branches

(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"

Les branches

(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

Les branches

(Le cas rebase)

Les branches

(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

Les branches

(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

Les branches

(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

Les branches

(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 !

Les branches

(Le cas rebase)

Git nous explique dans ce message ce qu'il nous est possible de faire :

 

  • Les résoudre, indexer les fichiers et continuer le rebase avec un git rebase --continue
  • Annuler et revenir à l'état avant le rebase avec un git rebase --abort

Corrigeons donc nos conflits !

Les branches

(Le cas rebase)

git status

Le problème semble venir d'index.html

Les branches

(Le cas rebase)

Prenons par exemple les changements provenant de la branche toto et donc du commit daa452a

Les branches

(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

Les branches

(Le cas rebase)

Les branches

(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 toto

Et 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

Les branches

(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

Les branches

 

Félicitations !

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 :)

Le remote : GitHub

GitHub, git remote (-v, add),  git branch -r, git push, git pull (--rebase), git fetch

Le remote : Github

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

Mais comment travailler en équipe à distance ?

Le remote : Github

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.

Le remote : Github

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 : Github

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 !

Et ce fameux "GitHub" dans tout ça ?

Le remote : Github

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  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 !

Le remote : Github

Attention, il existe de nombreux autres outils du même genre !

  • GitLab
  • BitBucket
  • ...

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 :)

 

Le remote : Github

  • En cliquant en haut à droite, vous pouvez aller dans "your profil"
  • Aller dans l'onglet "repositories"
  • Cliquer sur le bouton "New"

Nous allons créer notre premier dépôt sur GitHub !

Le remote : Github

Le remote : Github

  • Vous devez naturellement donner un nom à votre dépôt
  • Vous pouvez ensuite accessoirement lui donner une description
  • Si cela vous intéresse, vous pouvez le mettre en privé, auquel cas seul vous pourrez le consulter
    Un dépôt public peut en effet être consulter par tout le monde.

    Attention cependant, seul vous et les personnes que vous aurez autoriser pourront le modifier

Le remote : 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

git remote

Le remote : Github

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 :

  1. Un nom
  2. Un chemin pour y accéder (dans le cas d'un serveur distant, son URL)

Le remote : Github

Ajouter un dépôt distant à un git local est très simple, il suffit de faire la commande :

git remote add nomDepot urlDepot

Attention, 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"

Le remote : Github

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.git

Le remote : Github

La commande git remote -v permet de lister tous nos remotes :

git remote -v

Nous 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

Le remote : Github

Il est possible de lister l'ensemble des branches présentes sur le dépôt distant grâce à :

git branch -r

La 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

Le remote : Github

Pour ce faire, voici la commande :

git push origin master

Comprendre "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 !

Le remote : Github

git branch -r
git branch test
git push origin test
git branch -r

Le remote : Github

Voilà 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

Le remote : Github

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_bob

Le remote : Github

Bob 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

Le remote : Github

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 master

Le remote : Github

Le 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

Le remote : Github

Voyons maintenant, en retournant sur mon premier dépôt, comment je peux récupérer les nouveautés apportées par Bob !

Le remote : Github

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 master

Le remote : Github

Et 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 !

Le remote : Github

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

Le remote : Github

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

Le remote : Github

// 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 master

Le remote : Github

Une 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 !

Le remote : Github

Ainsi, comme nous l'avons vu :

git pull origin master

Vous 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

Le remote : Github

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 master

Le remote : Github

Il 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

Le remote : Github

git fetch

Le remote : Github

Lorsque vous faites un git pull, git va :

 

  • Tirer en local les changements d'origin
  • Faire un merge entre votre branche et celle d'origin

 

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

Le remote : Github

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/

Le remote : Github

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 !

Parlons sécurité

Parlons sécurité

Votre dépôt git est un espace très critique niveau sécurité pour vous ou votre entreprise

Il s'agit de l'endroit où vous allez héberger l'ensemble du code source de votre ou vos applications, programmes, etc.

Parlons sécurité

  • pour une application (site web, app mobile, etc.), il s'agit tout simplement de son code source, de sa valeur ajoutée
  • au delà du code source, il y a également des données potentiellement sensibles qui ne doivent pas tomber entre toutes les mains

Il y a donc constamment des risques de vol de données ou de code source

Parlons sécurité

Mais le vol n'est pas le seul problème potentiel

De nos jours, la plupart des systèmes des historiques git des entreprises sont branchés à des intégrations continues qui ont diverses raison d'être, comme la mise en production du code automatique après un changement particulier par exemple

Imaginez alors ce que pourrait faire quelqu'un de mal intentionné qui pourrait, en quelques secondes, mettre du code malveillant sur votre site en production

Parlons sécurité

Enfin, il est fort probable que vous utilisiez dans votre code des clés d'API de certains services comme Google Map, AWS, etc.

Il est très imprudent de versionner ces clés !

Il existe des robots qui fouillent les dépôt Github à leur recherche et les récupère (vous aurez ensuite une surprise lors de votre prochaine facture)

Parlons sécurité

Nous allons donc voir comment sécuriser au mieux son dépôt Github

Supprimer définitivement un fichier 

Supprimer un fichier

Imaginons que vous ayez besoin de retirer toute trace d'un fichier dans votre historique 

Ce que nous allons apprendre est dangereux, car nous allons réécrire les SHA-1 de tous les commits impactés par ce changement

Supprimer un fichier

Partez toujours du principe qu'une information poussée sur GitHub est considérée comme  compromise

Si vous avez poussé un mot de passe : changez-le !

Si c'était une clé SSH : générez-en une nouvelle

Supprimer un fichier

Nous allons utiliser une commande permettant de filtrer la totalité des commits de notre historique et d'en retirer le fichier en question

Attention, nous allons changer le contenu des commits et donc leur SHA-1 !

Supprimer un fichier

Attention, si des Pull Request étaient en cours, le changement des SHA-1 de votre côté risquent de tout casser au moment du Merge

Il faut donc penser à fermer ou merger les Pull Request avant

Supprimer un fichier

Attention, si votre dépôt a été forké... vous ne pouvez rien y faire, la personne a toujours les commits en question !

Je crois qu'il est toujours possible de contacter GitHub et de voir ce qu'il est possible de faire.. mais autant vous dire qu'il faut TOUJOURS partir du principe que votre info a fuité

Supprimer un fichier

filter-branch

Attention, vos modifications "stashées" seront perdues après cette commande ! Le stash est expliqué dans les Bonus pour ceux que ça intéresse

Supprimer un fichier

Pour expliquer un peu tout ça, mettons en place un petit dépôt git et un dépôt GitHub associé

Créer un déoôt
Créer un fichier mdp.txt avec les mots de passes
Commit
Créer un autre fichier genre index.html
Commit
Créer une branche feature/random
Ajouter une info dans l'index
Commit
Créer un depôt Github
Poussez votre branche principale
Poussez votre branche feature/random

On est bon

Supprimer un fichier

Faites bien attention aux SHA-1 des commits

Maintenant, nous nous rendons compte que le mdp.txt ne devait pas être versionné.. nous allons le supprimer de l'historique !

git filter-branch --force --index-filter 
"git rm --cached --ignore-unmatch mdp.txt" 
--prune-empty --tag-name-filter cat -- --all

Supprimer un fichier

"Et voilà"

Nous venons de supprimer les références au fichier en question, ainsi que les commits devenus vides à cause de cette suppression !

Nous venons également de changer tous les SHA-1 du coup !

Supprimer un fichier

Pensez maintenant à mettre votre fichier dans le .gitignore !

Pour finir, nous allons pousser ça sur GitHub

git push origin --force --all

--force : permet d'écraser l'historique distant par l'historique local

--all : le fait sur toutes les branches d'un coup

Supprimer un fichier

Enfin, voici les 3 commandes à jouer pour effacer les traces dans le Reflog :

git for-each-ref --format="delete %(refname)" refs/original | git update-ref --stdin
git reflog expire --expire=now --all
git gc --prune=now

Supprimer un fichier

Attention, si certains de vos collègues ont d'autres branches de leur côté, dites leur de REBASE vos branche et non pas de les MERGE

Un commit de merge pourrait potentiellement ramener des références que l'on souhaitait effacer

Supprimer un fichier

Ce que nous venons de faire est intéressant mais dangereux et non annulable

De plus, il existe d'autres étapes à ajouter lorsque vous avec des Tags ou des Pull Request

Voici le lien pour ceux qui veulent aller plus loin :

Supprimer un fichier

Nous l'avons vu, supprimer un fichier est compliqué

Ca peut ne pas fonctionner, ou tout du moins entraîner des effets de bords non souhaités

Partez donc toujours du principe que toute information poussée est compromise et qu'il est impératif de surtout faire attention à ce que vous poussez !

Parlons authentification

Parlons authentification

Tout d'abord, voyons comment mettre en place une authentification par SSH avec notre compte GitHub

Vous authentifier auprès de GitHub via SSH est plus sécurisé car :

  • vous n'aurez plus jamais à rentrer vos identifiants
  • vous pourrez très facilement révoquer une clé qui vous semble compromise

Parlons authentification

Pour ajouter une clé SSH, rien de plus simple :

ssh-keygen -t ed25519 -C "your_email@example.com"

Lorsqu'on vous demande un fichier, ne rentrez rien pour choisir celui par défaut

Indiquez ensuite la passphrase de votre choix

Parlons authentification

Vous devriez ensuite pourvoir voir votre nouvelle clé :

ls -la ~/.ssh

SSH est basé sur un système de clé publique, clé privé

clé publique

clé privée

Savez-vous comment ça fonctionne ?

Parlons authentification

Il s'agit ensuite d'aller placer votre clé publique dans vos settings GitHub

Cela se passe dans :

Profile > Settings > SSH and GPG keys

Parlons authentification

A partir de maintenant, il faut que vous utilisiez l'url SSH de vos dépôts pour vous y connecter !

git remote set-url origin url_ssh

Signons nos commits

Signons nos commits

Nous allons apprendre ici quelque chose de très important, que très peu de développeurs ont en tête

Nous allons voir que ce que nous connaissons actuellement de Git et  des commits peut présenter une légère faille :)

Signons nos commits

Petite démonstration en Live !

L'un de vous doit me donner l'url de l'un de ses dépôts, et je m'occupe ensuite de vous faire une petite démonstration de mes talents de "hacker" pas piquée des hannetons

Signons nos commits

J'écris ici ce que j'ai fais en live, pour que vous ayez de quoi réviser !

Vous vous souvenez de la config globale que nous avions modifiée au tout début de notre aventure avec git ?

Nous y avions placé notre name et notre email

Signons nos commits

Ces informations servent à Git pour savoir qui a fait les changements lors d'un commit

Sauf que... il est très facile de se faire passer pour quelqu'un d'autre !

Prenons par exemple Jake Wharton ! (Développeur très très connu de la sphère Android)

Signons nos commits

Je veux me faire passer pour lui sur l'un de mes dépôts

Et pour ça... ce n'est pas très compliqué à vrai dire

Tout d'abord, je vais fouiner dans ses dépôts publics et en prendre un au hasard

Prenons Retrofit par exemple, un des plus connus !

git clone https://github.com/square/retrofit hacked_retrofit

Signons nos commits

Ensuite, j'affiche les 3 derniers commits par exemple :

git log -n 3

Signons nos commits

Comme vous pouvez le voir, nous avons accès à ses informations d'auteur Git

name: Jake Wharton

email: github@jakewharton.com

Rien de surprenant, ça représente en effet bien l'auteur du commit

Signons nos commits

Mais... qu'est-ce qui m'empêche de mettre les même informations dans ma config ?

Signons nos commits

Et si je fais maintenant un commit :

touch jake_qui_leak_des_mdp.txt
git add jake_qui_leak_des_mdp.txt
git commit -m "coucou c'est jake !"
git log -n 1

Que remarquez-vous ? 

Il semble que Jake soit bien l'auteur du commit !

Signons nos commits

Pire encore, faites donc un push !

Evidemment, si vous cliquez sur l'avatar de Jake... vous tombez bien sur son compte GitHub

Signons nos commits

OK.... prenons quelques instant pour mesurer à quel point ce qu'on vient de faire est dangereux

Vous le voyez, il est super facile de falsifier son identité sur Git et de se faire passer pour quelqu'un d'autre

Là, si vous venez sur mon dépôt, il est à priori impossible de savoir que j'ai falsifié l'identité de Jake Wharton

Signons nos commits

Attention, pour autant il y a des limites

Vous ne pouvez pas pour autant pousser des modifications sur un dépôt sur lequel vous n'avez pas les droits GitHub

Si je voulais pousser quelque chose sur Retrofit, GitHub me demanderait les credentials de Jake... et là s'arrêterait ma virée de hacker professionnel !

Signons nos commits

Quels sont les cas d'usage malicieux d'une telle pratique ?

Ils sont très nombreux !

  • écrire une pull request pour un projet open source en se faisant passer pour quelqu'un de connu
     
  • introduire du code malicieux dans le dépôt de sa boîte tout en se faisant passer pour un collègue
     
  • etc.

Signons nos commits

Ok, alors comment son image, et protéger sa boîte de telle pratiques ?

Si vous jetez un coup d'oeil aux derniers commits de notre ami Jake sur le dépôts de Retrofit, vous allez tomber sur ce beau petit cadre VERIFIED

Signons nos commits

Eh oui... en fait Jake s'est quand même un peu protégé !

Il a en fait simplement mis en place un système de signature de commits

Signer ses commits lui permet de s'assurer de 2 choses :

  • que le code qu'il a push et celui arrivé sur Github sont bien identiques
  • que l'auteur est approuvé et que c'est bien lui qui en est la source

Signons nos commits

Alors vous l'avez vu, signer ses commits n'empêche personne d'usurper son identité !

En fait cela permet seulement aux gens de n'accepter QUE les commits signés et donc vérifiés de Jake, aucun autre

Signons nos commits

Bon, c'est bien joli, mais comment on les signe ces commits ?

Signons nos commits

Nous allons pour cela utiliser une signature cryptographique grâce à l'outil GPG

Avant de parler de GPG, un bref rappel sur les signatures cryptographiques

Il existe 2 types d'algorithmes cryptographiques :

  • les symétriques
  • les asymétriques

Signons nos commits

Dans le cadre d'un algorithme symétrique :

  • vous chiffrez votre message avec un mot de passe
  • vous donnez ce mot de passe à votre pote
  • lorsque vous vous envoyez des messages, c'est ce mot de passe commun (symétrique) qui vous permet de chiffrer / déchiffrer le message

Signons nos commits

Dans le cadre d'un algorithme asymétrique :

Ici, on se base sur le principe de :

  • clé publique
  • clé privée

Voyez votre clé publique comme un cadenas et votre clé privée comme la clé de ce cadenas

Signons nos commits

Lorsque que votre pote veut vous envoyer un message, il doit :

  • chiffrer le message avec votre clé publique (mettre votre cadenas dessus en gros)
  • vous l'envoyer
  • vous seul pouvez lire le message puisque vous seul avez la clé du cadenas en question !

Signons nos commits

Il est donc capital, dans ce système, de garder précieusement votre clé privée... privée !

Votre clé publique, elle, on s'en fout ! C'est un bête cadenas, de toute manière personne n'en a la clé

Si vous vouliez répondre à votre pote, il vous suffirait d'avoir sa clé publique à lui (son cadenas), de chiffrer votre message avec puis de lui envoyer, etc.

Signons nos commits

Quel système que nous avons vu dernièrement utilise exactement ce principe de cryptographie asymétrique ?

Signons nos commits

Et nos commits dans tout ça ?

Nous allons donc, comme je le disais, utiliser l'outil GPG

GPG (GNU Privacy Guard) est une implémentation très célèbre et open source du OpenPGP qui est un standard mondial sur la cryptographie et pleins d'autres joyeusetés

Signons nos commits

GPG est accessible sur tous les OS, open source, utilise le principe de cryptographie asymétrique, et fais pleins pleins d'autres choses incroyable dont nous ne parlerons jamais mais que je vous invite à regarder pour les plus curieux

Signons nos commits

Je vous laisse donc télécharger GPG sur votre ordinateur

Pour ceux qui ont brew... c'est pas très compliqué :

brew install gnupg

Signons nos commits

Nous allons pouvoir ensuite générer une clé !

Pour cela, rien de plus simple :

gpg --full-generate-key
  1. on choisi la première option RSA and RSA
  2. pour la taille : 4096
  3. période de validité, perso je mets 0, à vous de voir
  4. nom & email
  5. enfin, un mot de passe sécurisé !

Signons nos commits

Pour visualiser vos clés GPG :

gpg --list-secret-keys --keyid-format LONG [email]

Le [email] est optionnel et permet juste de filtrer en fonction de l'email si plusieurs clés GPG

Signons nos commits

Voici ce que ça donne chez moi :

La partie intéressante est le : 649AB0C1D11EF0A43980936F03B9376654058A39

C'est ce qui va permettre à Git de savoir quelle clé utiliser pour signer

Signons nos commits

Vous n'avez ensuite plus qu'à mettre les informations qui vont bien dans votre config, soit en ligne de commande, soit directement dans le fichier ~/.gitconfig

git config --global gpg.program gpg
git config --global user.signingkey [ID_DE_VOTRE_CLE]
git config --global commit.gpgsign true

Signons nos commits

On touche au bout !

Il est possible, surtout sur MacOS que vous ayez besoin de faire une dernière manipulation qui permettra à GPG de vous proposer de rentrer votre mot de passe sur votre terminal :

Dans vos variables d'environnements, préciser ceci :

export GPG_TTY=$(tty)

Signons nos commits

Allez-y, faites un commit !

Rentrez votre passphrase et HOP, vous venez de signer votre premier commit !

git log --show-signature

Cette commande vous permet d'afficher également les signatures lors des logs des commits

Signons nos commits

Enfin, nous allons lier notre clé GPG avec notre compte GitHub !

Pour cela, on récupère la clé publique de notre GPG :

gpg --armor --export [ID_DE_VOTRE_CLE]

Signons nos commits

On récupère tout depuis

-----BEGIN PGP PUBLIC KEY BLOCK-----

jusqu'à 

-----END PGP PUBLIC KEY BLOCK-----

(compris)

Signons nos commits

Et on va coller ça dans nos settings GitHub !

Signons nos commits

Allez-y ensuite, faites donc un push !

Vous voilà certifiés par GitHub !

Signons nos commits

Il est ensuite possible d'obliger la signature des  commits sur des branches spécifiques de votre dépôt GitHub

Pour cela, aller dans :

mon_depot > settings > branches > Add Rule > ajouter la règle correspondante

Les Hooks

Les Hooks

Les Hooks sont des scripts qu'il est possible d'activer et qui seront lancés à des moments clés du "cycle de vie" de Git

L'objectif d'un Hook est d'automatiser une tâche lors des récurrences d'une tâche en particulier

Les Hooks

N'importe quel dépôt git a des Hooks créés par défauts

ls -la .git/hooks/

Les Hooks

Les étoiles indiques que se sont des exécutables

Par défaut, les hooks sont désactivés, il suffit de retirer le .sample pour qu'ils soient reconnus par Git et donc activés

Leurs noms sont plutôt clairs, mais vous voyez qu'il existe des Hooks pour pleins d'occasions spécifiques

Les Hooks

Au hasard :

  • pre-commit

Déclenché au moment d'un commit, avant que celui-ci soit réellement pris en compte

  • commit-msg

Déclenché au moment d'un commit, une fois que son message a été récuperé, mais toujours pas validé

  • Etc.

Les Hooks

Il est donc possible d'écrire des petits scripts qui seront joués par ces Hooks

En soit, il n'y a pas de règles concernant le langage des scripts en question... le tout étant d'avoir le langage sur votre machine

Ceux par défaut sont écris en shell, mais nous allons nous utiliser du Ruby, un peu plus simple pour faire du script basique

Nous n'allons de toute manière rien faire d'incroyable niveau code, donc on s'en fiche un peu

Les Hooks

Mais d'ailleurs...

On va faire quoi en fait ?

Arrivés à ce moment du cours, vous devriez avoir compris que, comme toujours en sécurité, il vaut mieux prévenir que guérir !

La majorité des problèmes de sécurités sont provoqués parce que nous sommes humains, et qu'une erreur d'innatention peut avoir de grosses répercussions 

Les Hooks

Ok, et les Hooks dans tout ça ?

Nous allons nous mettre à la place d'une entreprise qui souhaite faire respecter certaines règles à ses développeurs en terme de sécurité sur Git

Pour être sûrs que tous les développeurs respectent les même règles, nous allons automatiser le respect de ces règles, grâce aux Hooks !

Les Hooks

Par exemple, nous allons tenter de vérifier, lors de chaque commit, que le développeur ne commit aucun console.log()

Laisser ses logs dans son code est en effet souvent une mauvaise pratique car ces logs pourraient être visibles en production et comporter des données sensibles sur le logiciel 

Les Hooks

C'est un exemple comme un autre, le tout est de comprendre comment faire, vous pourrez ensuite l'adapter à bien d'autres concepts !

Les Hooks

Commençons par installer Ruby sur votre OS

Normalement, tout est expliqué sur le site et l'installation devrait très bien se passer sur les 3 OS principaux

Les Hooks

OK, on peut commencer !

Remplacez le contenu de votre fichier .git/hooks/pre-commit par ça :

#!/usr/bin/env ruby

puts "SALUT !"

Les Hooks

Renommez ensuite le nom du fichier en supprimant le .sample

pre-commit.sample devient donc pre-commit

Tentez ensuite un commit, n'importe quoi, et voilà :

Les Hooks

Nous arrivons donc à communiquer avec le développeur

Le commit sera toujours accepté sauf si le programme se termine avec un code de sortie différent de 0

Par exemple : 

#!/usr/bin/env ruby

puts "SALUT, tu peux plus commit, cordialement."
exit 1

Les Hooks

Tentez de faire un commit... voilà.

Ok, donc on sait :

  • déclencher un script lors du commit
  • refuser le commit

Il ne nous reste plus qu'à écrire notre algorithme !

Les Hooks

Comment faire pour récupérer les fichiers qui ont été modifiés dans le commit en cours ?

Pour faire cela, nous allons utiliser la commande git diff

Vous vous en souvenez ? En l'utilisant avec le --cached ainsi qu'un nouveau flag --name-only, on peut récupérer les noms des fichiers modifiés entre le stage et la dernière version

Les Hooks

Modifiez un ou plusieurs fichiers, ajoutez les au Stage puis lancez :

git diff --cached --name-only

Nous récupérons donc la liste des noms de fichiers prêts à être commit !

Les Hooks

Une fois qu'on a cette lite, on va pouvoir s'en servir pour vérifier le contenu de chaque fichier en Ruby

Si un fichier comporte les mots interdits console.log() alors on refuse le commit... et c'est tout !

Allez-y, tentez donc de faire ce petit algo :)

(Cheat Sheet Ruby)

Les Hooks

(c'est pas bien de regarder la réponse sans même avoir essayé, je vous vois)

Voici un exemple de solution :

#!/usr/bin/env ruby

logRegex = /console.log(.)/

filenames = `git diff --cached --name-only`.split("\n")

logFound = false

filenames.each do |fileName|
	file = File.open(fileName)
	content = file.read
	if logRegex.match(content)
		logFound = true
		puts "[POLICY] console.log() found in file : #{fileName}"
	end 
end

if logFound
	puts "[POLICY] Commit refused, please remove all your logs"
	exit 1
end

Les Hooks

Allez-y, tentez maintenant de faire des commits avec des console.log(), vous ne pourrez plus !

L'erreur est humaine, et les erreurs d'innatentions sont légions chez les développeurs, même les plus expérimentés !

Grâce aux Hooks, vous automatisez autant de gardes-fous que vous le souhaitez !

Les Hooks

Il nous reste cependant un souci à régler

Vous avez vu, depuis le début, on modifie le fichier pre-commit mais celui-ci ne semble jamais pris en compte par Git, nous ne pouvons pas le versionner

Sauriez-vous dire pourquoi ?

Les Hooks

Parce que le dossier /hooks est un dossier à l'intérieur de votre propre dossier .git/ !

Et on l'a dit au début du cours : le .git/ est un dossier caché qui est le "moteur" de votre dépôt local

Il est donc tout à fait normal que Git ne s'y intéresse pas, il s'agit de votre "moteur" à vous, il n'est pas fait pour être versionné

Les Hooks

Sauf que ça, c'est problématique

En tant que responsable de la sécurité du logiciel (rien que ça), vous aimeriez que tous les développeurs partagent les même règles de sécurité

Chaque développeur pourrait copier/coller le code, etc.

Mais maintenant que vous êtes expert en Git, vous savez que le mieux est que ces Hooks soient eux-même versionnés et partagés via GitHub par exemple !

Les Hooks

Voilà comment on va faire :

Il est possible de modifier le chemin vers le dossier des Hooks

De cette manière, nous allons pouvoir sortir nos Hooks du dossier .git/ et les mettre dans un dossier à la racine du projet, que nous pourrons versionner comme n'importe quel dossier

Les Hooks

Pour préciser un nouveau chemin à Git concernant les Hooks, ça se passe dans la config, et voici la commande :

git config core.hooksPath <monDossier>

Nous allons donc créer un dossier .githooks/ à la racine du projet, et lancer ensuite :

git config core.hooksPath .githooks

Les Hooks

Ensuite, il n'y a plus qu'à déplacer nos hooks (seuls ceux qu'on utilise vraiment) dans le nouveau dossier !

mv .git/hooks/pre-commit .githooks/

On les rend éxecutables :

chmod u+x .githooks/pre-commit

Les Hooks

Et voilà !

Votre hook est toujours pris en compte et il est cette fois présent dans un dossier que vous pouvez versionner !

Mais.... il nous reste un dernier (promis) problème 

Tentez de faire un commit, rien d'anormal ?

Les Hooks

Notre fichier pre-commit, contenant lui-même un console.log() dans sa regex nous empêche dons de faire un commit... et c'est quand même un peu con 

Pour corriger ce souci, nous allons simplement ajouter un filtre dans notre commande git diff :

git diff --cached --name-only ':(exclude).githooks/pre-commit'

Les Hooks

OK ! Là, on est enfin bons !

  • le Hook n'est pas compris dans l'analyse
  • nous analysons tous les fichiers commités
  • si on trouve un console.log(), on interdit le commit
  • le hook est partagé dans un dossier versionné

Félicitations, vous venez de réaliser un Hook qui peut éviter de nombreuses erreurs d'innatentions !

Les Hooks

Suis-je obliger d'être développeur pour créer des Hooks ?

Non.

Vous l'imaginez, il y a de nombreux développeurs qui ont eu besoin de créer de nombreux hooks

Il existe donc pleins d'exemples de Hooks déjà faits sur internet pour plein d'utilité différentes, il n'y a qu'à se servir !

Les Hooks

Les limites des Hooks

Les Hooks peuvent tout à fait être ignorés par les développeurs

Pour ce faire, ils peuvent simplement changer leur code, ou encore lancer un commit en mode --no-verify par exemple

Les Hooks

C'est pour cette raison que les Hooks ne doivent être qu'un complément de sécurité

Ils servent à rendre la vie plus facile, à éviter les erreurs d'inattentions, etc. Mais n'empêcherons jamais un développeur mal intentionné de les ignorer

Les Hooks

Sachez qu'il est également possible de mettre en place des Hooks côté serveur, et donc beaucoup plus sécurisés car non accessible aux développeurs à priori

Nous n'en parlerons pas dans ce cours mais, pour les plus curieux, vous pouvez voir un tuto sur le site de Git :

Les Hooks

Envie de vous entraîner ?

Voici un scénario qui arrive souvent dans la vraie vie

Dans la plupart des projets IT, les développeurs se basent sur des systèmes de tickets représentants les tâches qu'ils doivent faire 

Ce système permet à la gestion de projet de savoir où ils en sont, le temps passé sur une tâche, qui bosse dessus, etc.

Les Hooks

Le développeur doit donc mettre à jour les tickets selon l'avancée de la tâche (pas commencée, en cours, etc.)

Il est assez commun que les entreprises demandent aux développeurs de renseigner la référence du ticket concerné lors d'un commit

Ca permet de retrouver facilement le ticket concerné par les changements en question mais aussi potentiellement d'automatiser des services !

Les Hooks

On peut imaginer plein de système d'automatisation :

  • le ticket est automatiquement fermé/ouvert/etc
  • le chef de projet reçoit une notification
  • etc.

Côté serveur (GitHub), il s'agit de mettre en place un système d'intégration continue, et nous ne verrons pas ça ensemble

Les Hooks

En revanche, pour vous entraîner, vous pouvez essayer d'écrire un Hook qui, déjà en local, forcera l'utilisateur à renseigner un message de commit avec une référence

Par exemple :

  • [ref: 42] Add a.txt         serait valide
  • Add a.txt [ref: 42]         serait valide
  • Add a.txt                        ne serait pas valide

Les Hooks

Pour vous aider, le hook à modifier ici semble être le hook commit-msg

Celui-ci reçoit comme argument le chemin du fichier contenant le nom du commit renseigné

Il suffit donc de récupérer l'argument, lire le fichier (et donc le nom du commit), et de faire votre algorithme !

// récupère le 1er argument, le nom du fichier
// contenant le message de commit
commitFile = ARGV[0]

BONUS

Le remisage avec git stash

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 ?

  • Vous souhaitez changer de branche mais ne pas perdre le travail en cours (sans pour autant devoir le commit)
  • Vous devez faire un petit commit alors même que vous travailler déjà sur quelque chose

git stash

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

git stash

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)

git stash

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 ?

  • Commiter tout, avec les modifications instables ? (NON)
  • Effacer toutes vos modifications pour faire la modif et tout reprendre après ? (du temps de perdu)

git stash

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 stash

Tous 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 !

git stash

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 list

On 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é

git stash

Nous allons maintenant pouvoir reprendre où nous en étions notre travail !

git stash apply

Cette 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 drop

git stash

Imaginons 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 list

Attention, pour les commandes à venir, le véritable identifiant est le label "stash@{numero}" de votre stash, son nom n'étant pas unique

git stash

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}

git stash

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

git stash

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

git stash

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)

BONUS

git rebase pour réorganiser ses commits

git rebase

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é

git rebase

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 !

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 69a90e7

git rebase

On 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

git rebase

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

git rebase

Nous souhaitons ici réordonner les différents commits

git rebase -i 1cc93b4

Raccourcis 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 !

git rebase

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 1cc93b4

git rebase

Sauvegardez - 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)

git rebase

Effacez tout sauf le bon message, et sauvegardez - quittez ... Et voilà !

Git 3ème année SI

By Ecalle Thomas

Git 3ème année SI

Cours sur les bases de GIT orienté Sécurité Informatique

  • 1,691