# Apprendre Git --- ## Objectifs de la capsule Connaître les principes de base de Git --- ## Plan - Préambule - Les usages de base - Les usages avancés # Préambule --- ## Git un VCS ? - [Gestion des versions](https://fr.wikipedia.org/wiki/Gestion_de_versions) d'un code - Pour travailler à plusieurs - Gérer les conflits --- ## Git un Dvcs ? ![Centralisé vs Distribué](images/centralised-decentralised-distributed.png) --- ## Quelques alternatives - SVN - Mercurial - Bazaar --- ## Pourquoi Git - Le plus utilisé - Le plus puissant (mais également dangereux) - Utilisé sur les forges --- ## L'installer ```shell $ sudo apt install git ``` --- ## Obtenir de l'aide ```shell $ git help $verb $ git $verb --help $ man git-$verb ``` --- ## Obtenir de l'aide 2 Et sur Internet… - [Des ressources](https://try.github.io/) - [Le graal](https://pcottle.github.io/learnGitBranching/?NODEMO) - [De la pratique](https://gitexercises.fracz.com/) à faire absolument - [La « shitshit »](https://github.github.com/training-kit/downloads/github-git-cheat-sheet.pdf) et [une autre](https://www.git-tower.com/blog/git-cheat-sheet) - [Par la Nulle](https://nissone.com/GitPourLaNulle/GitPourLaNulle.pdf) - [Avec des chats](https://girliemac.com/blog/2017/12/26/git-purr/) - Et évidement la [référence](https://git-scm.com/docs) --- ## En image [![](images/workflow.png)](images/workflow.png) # Les usages de base --- ## git config - Pour éditer la [configuration](https://www.git-scm.com/docs/git-config) - Trois niveaux de configuration : - Système : `--system`, - Utilisateur : `--global`, - Dépôt : `--local`. ```shell $ git config --list # Pour lister les valeurs $ git config $element [$valeur] # Pour afficher/[modifier] la valeur ``` Note: - Chaque niveau de configuration surcharge le précédent - Pensez à utiliser la complétion si disponible. --- ### Identité Nécessaire pour travailler : ```shell $ git config --global user.name "John Doe" $ git config --global user.email "johndoe@example.com" ``` --- ### D'autres conf ```shell $ git config --global core.editor "code --wait" # Attend la fermeture de l'éditeur $ git config --global core.editor "vim" $ git config --global alias.ci "commit" # Les alias de commandes sont pratiques $ git config --global alias.co "checkout" ``` --- ## git clone - Pour [cloner un dépôt](https://www.git-scm.com/docs/git-clone) - Crée une copie locale identique ```shell $ git clone git@gitlab.com:jrmi/n7-fullstack-git.git $ git clone https://gitlab.com/jrmi/n7-fullstack-git.git $ git clone [chemin vers un autre dépôt] ``` --- ### Notion de dépôt local - Contient *toutes* les versions d'un projet - C'est le dossier `.git/` - Image fidèle d'un dépôt distant au moment du clone - Qu'il faudra mettre à jour au besoin --- ### Notion de dépôt distant - Un dépôt local peut être lié à plusieurs dépôts distants - On parle également de **remote** - Chaque *remote* a un nom - Le *remote* par défaut s'appelle "origin" Notes: Plusieurs protocoles sont possibles. --- ## git remote Gérer les [remotes](https://www.git-scm.com/docs/git-log) ```shell $ git remote -v $ git remote add
``` --- ## git log Pour [afficher l'arbre](https://www.git-scm.com/docs/git-log) des commits. ```shell $ git log $ git log --oneline --graph $ git config alias.logg "log --oneline --graph" ``` --- ### Notion de commit - Un commit représente une version de l'ensemble code à un instant donné - C'est également un ensemble de changements par rapport à la version précédente - Chaque commit a un *identifiant unique* qui le désigne - Il est le descendant d'un ou plusieurs commits - Il est associé à un message Notes: - l'ID peut être tronqué pour plus de facilité --- ### Désigner un commit - Par son ID - Par un préfixe de son ID - Par le nom d'une *branche* qui pointe dessus - Par le nom d'un *tag* - Relativement à `HEAD` --- ## git checkout - Pour se [déplacer](https://www.git-scm.com/docs/git-checkout) dans l'arbre des commits - Initialise le répertoire de travail - Sur un commit en particulier, donc une version du code - Déplace `HEAD` - Ou sur une branche - Dans ce cas `HEAD` sera attaché à cette branche ```shell $ git checkout 702ca22 $ git checkout master ``` --- ### Notion de HEAD - C'est le commit courant - Celui auquel est comparé le contenu du répertoire de travail - L'état initial du répertoire de travail avant modifications - `HEAD` se déplace à chaque fois que l'on ajoute un commit - `HEAD` peut être attaché à une branche - C'est le parent du prochain commit --- ## git branch - Pour [créer une branche](https://www.git-scm.com/docs/git-branch) - Celle-ci pointe vers le commit courant (`HEAD`) au moment de l'exécution de la commande - L'étoile désigne la branche attachée à `HEAD` ```shell $ git branch $nom_branche # Créer une branche $ git branch # Lister les branches locales $ git branch -d $nom_branche # Supprimer une branche $ git branch --all # Lister toutes les branches ``` Notes: - Les branches sont gratuites, il faut en faire pleins --- ### Notion de branche - Une **étiquette** / une référence - Désigne **un commit** à un instant donné - Désigne également **l'ensemble des commits** à partir de la divergence - L'étiquette d'une branche locale se **déplace automatiquement** avec `HEAD` si elle est *attachée* - `master`/`main` sont des branches comme les autres mais qui existent **par défaut** --- ## git status - Pour afficher le [statut](https://www.git-scm.com/docs/git-status) des modifications - 3 statuts - Untracked - Not staged (Modified) - To be committed (Staged) ```shell $ git status ``` --- ### Les différents états d'un fichier ![](images/lifecycle.png) --- ## git diff - Pour afficher les [différences](https://www.git-scm.com/docs/git-diff) entre le répertoire de travail et l'index - ou entre l'index et HEAD - ou entre deux commits ```shell $ git diff $ git diff 702ca22 cee44ac # Entre deux commits $ git diff --cached # Pour diff index vs HEAD ``` --- ### Notion de différence ![](images/diff3.png) --- ## git add - Pour [indexer](https://www.git-scm.com/docs/git-push) les fichiers modifiés - Permet de préparer le futur commit - C'est le brouillon ```shell $ git add README.md ``` --- ### Notion d'index ![](images/threeArea.png) --- ## git commit - Permet de transformer les modifications de l'index en un nouveau [commit](https://www.git-scm.com/docs/git-commit) - Ce commit est un enfant du commit courant (`HEAD`) - Enregistre les modifications dans le **dépôt local** uniquement ```shell $ git commit # saisie du message de commit via un éditeur $ git commit -m "Message de commit" ``` --- ### Les autres effets - Déplace `HEAD` vers le nouveau commit - Déplace la branche attachée à `HEAD`, le cas échéant --- ## git push - [Pousse](https://www.git-scm.com/docs/git-push) les nouveaux commits - De la branche en cours - Vers la branche distante liée (upstream) - Du dépôt distant (le remote) ```shell $ git push $remote $nom_branche # Pour pousser vers une branche distante d'un remote $ git push $remote $nom_branche -u # Pour pousser vers la branche distante et la lier à la branche locale $ git push # Quand la branche est déjà liée ``` --- ### Notion de branche liée - Les branches entre un clone et ses `remotes` n'ont aucun lien par défaut - Il est possible de lier une branche locale à une branche d'un remote - Cela permet d'éviter d'avoir à spécifier la branche à chaque fois ```shell $ git branch --set-upstream $remote $nom_branche # Pour spécifier la branche liée ``` Notes: - Les noms des branches ne sont pas forcément les mêmes --- ## git fetch - Permet de [récupérer](https://www.git-scm.com/docs/git-fetch) les nouveaux commits disponibles sur le dépôt distant - Ne touche pas au répertoire de travail - C'est une synchronisation du dépôt local avec le dépôt distant ```shell $ git fetch [$remote] ``` --- ### Notion de branche distante - Les branches distantes sont visibles dans le dépôt local - Ce sont des références (comme les tags) - Elles sont préfixées par le nom du remote - Elles référencent le dernier commit de chaque branche distante lors du dernier fetch --- ## git merge - Permet de [fusionner](https://www.git-scm.com/docs/git-fetch) deux branches - La fusion se fait entre la suite commits menant à `HEAD` - Et la suite de commit menant au commit cible - À partir de la divergence ```shell $ git merge $autre_branche|$commit ``` --- ### Notion de fusion 3 cas se présentent : - Le commit désigné est un enfant direct du commit courant - La branche du commit désigné contient des modifications compatibles avec les modifications de la branche que l'on souhaite merger - Ni l'un, ni l'autre --- ### Notion de Fast Forward Dans le premier cas : - S'il n'y a pas de divergence, on peut faire une _avance rapide_ - Dans ce cas, il n'y a pas de commit de merge - `HEAD` est simplement déplacée sur le bon commit - C'est le cas simple --- ### Notion de fusion simple Dans le second cas: - Un commit de fusion est créé - Ce commit a deux parents : - Le commit référencé par `HEAD` - Le dernier commit de la branche cible - HEAD est déplacée vers ce nouveau commit - Le commit représente alors les modifications des deux branches --- ### Notion de conflit - Un conflit apparaît quand un même fichier est modifié au même endroit : - dans l'un des commit de la branche désignée - et dans l'un des commit de la branche que l'on souhaite fusionner - Ce conflit est révélé lors du `merge` - Il faut alors intervenir manuellement pour régler le conflit --- ### Résoudre un conflit - `git status` pour connaître les fichiers concernés - Les chevrons permettent de localiser les parties en conflit - Une fois les conflits résolus, il faut ajouter les fichiers concernés dans l'index - Et commiter le tout --- ### En image ![](images/merge-conflict.png) --- ## git init - Permet d'initialiser un nouveau dépôt - Crée le dossier `.git` - Aucun remote par défaut - N'ajoute pas de fichiers automatiquement - L'historique est vide ```shell $ git init ``` # Usages avancés --- ## Quelques conseils - Commiter souvent - Tester PUIS commiter - Utiliser les messages de commit - Créer des branches - Utiliser un workflow --- ## Encore des liens - https://www.miximum.fr/blog/enfin-comprendre-git/ - https://rogerdudler.github.io/git-guide/index.fr.html --- ## le patching - Beaucoup de commandes permettent de gérer le patching - En général option `-p` - permet de sélectionner finiment ```shell $ git add -p $ git stash -p $ git reset -p HEAD ``` --- ## .gitignore - Les motif de ce fichiers sont ignorés lors d'un `git status` - Pour éviter les ajouts malencontreux - Fichier à versionner dans le dépôt --- ## Désigner un commit relatif - Pour naviguer plus facilement ```shell HEAD~1 # Le premier parent de HEAD HEAD~2 # ou HEAD~~ le 1er parent du 1er parent de HEAD HEAD^1 # Le premier parent de HEAD HEAD^^ # Le premier parent du premier parent de HEAD HEAD^2 # le second parent quand il y en a plusieurs $commit1..$commit2 # Une liste de commit ``` --- ## Exemple ``` G H I J \ / \ / D E F \ | / \ \ | / | \|/ | B C \ / \ / A A = = A^0 B = A^ = A^1 = A~1 C = A^2 D = A^^ = A^1^1 = A~2 E = B^2 = A^^2 F = B^3 = A^^3 G = A^^^ = A^1^1^1 = A~3 H = D^2 = B^^2 = A^^^2 = A~2^2 I = F^ = B^3^ = A^^3^ J = F^2 = B^3^2 = A^^3^2 ``` --- ## Quelques options ```shell $ git commit --amend -m … $ git branch -d … ``` --- ## git grep - Pour rechercher dans les fichiers indexés - Comme grep mais en plus précis ```shell $ git grep "texte à rechercher" ``` --- ## git pull - Un fetch puis un merge en une seule étape - Permet le rebase ```shell $ git pull [$remote] [$branch] $ git pull --rebase ``` --- ## git stash - Pour [remiser](https://www.git-scm.com/docs/git-stash) les changements courants - TRÈS utile pour changer de branche rapidement ```shell $ git stash $ git stash pop ``` --- ## git reset - Pour ré-initialer une branche vers un commit - Ou enlever un fichier du staging ```shell # Pour déplacer la branche courante $ git reset $commit --mixed # ou --soft ou --hard $ git reset HEAD
``` --- ## git rm - Pour supprimer un fichier du dépôt ```shell $ git rm "chemin de fichier" $ git rm "fichier" --cached ``` --- ## git mv - Pour déplacer un fichier - En conservant l'historique ```shell $ git mv $source $destination ``` --- ## git tag - Pour « nommer » un commit - Comme une branche mais ne se déplace pas ```shell $ git tag -l # Pour lister tous les tags $ git tag "nom tag" # Pour en créer un $ git tag -d "nom tag" # Pour en supprimer un $ git push --tags # Pour pousser les tags ``` --- ## git rebase - Quand on veut un bel historique de commit - Permet de réécrire les commits de la branche courante - Après le commit cible - !!!! ATTENTION DANGER !!!!!! ```shell $ git rebase $commit $ git rebase -i $commit # interactif, plus complet ``` --- ## git cherry-pick - Pour récupérer un (ou plusieurs) commit d'une branche vers une autre ```shell $ git cherry-pick $commit ``` --- ## git revert - Pour appliquer l'inverse des commit désignés - Permet de supprimer des modifications - Mais le garde dans l'historique ```shell $ git revert $commit ``` --- ## git reflog - Quand on a perdu un commit ```shell $ git reflog ``` --- ## git blame - Quand on veut accuser quelqu'un - Affiche l'auteur ligne par ligne ```shell $ git blame $fichier ``` --- ## git show - Consulter un fichier dans une ancienne version - et plein d'autres choses ```shell $ git show $commit:$chemin_vers_fichier ``` --- ## git bisect - Pour rechercher efficacement dans l'historique - Réalise une dichotomie - Pratique pour trouver l'origine d'un bug ```shell $ git bisect start # Pour lancer l'opération $ git bisect bad # Désigne le mauvais commit $ git bisect good [$commit]# Désigne le bon commit $ git bisect skip # Pour sauter un commit ``` Notes: [Dépôt exemple](drive.delicious-insights.com/assets/bisect-demo.zip) https://delicious-insights.com/fr/articles/git-bisect/ --- ## git submodules - Permet de gérer les sous modules - Un sous module est un autre dépôt Git - C'est une dépendance ```shell $ git submodule add
$ git submodule init $ git submodule update ``` --- ## Aller plus loin - [Recréer Git](https://www.leshenko.net/p/ugit/#) --- # Des questions ? Oui sûrement...