{{tag>git dev}} ====== Git ====== Git est un outil de suivi de version ou **gestionnaire de révisions**. Contrairement à certains outils centralisés du même type comme svn, Git est distribué, ce qui le rend très réactif, plus didactique car aucun serveur n'est nécessaire pour apprendre à travailler avec Git. Un gestionnaire de révision permet de garder en mémoire: * Les modifications apportées sur chaque fichier ; * Pourquoi elles ont eu lieu ; * et par qui ! Cette tâche de gestion des révisions est appelée **versioning** en anglais. Git est capable de suivre les modifications apportées à tous types de fichiers (textes ou binaires) que l'on désigne par sources du projet. Une **révision** est une série de modifications apportée sur les sources du projet. Git note les modifications associées à l’ensemble des fichiers qu’il suit avant de les ajouter, sous forme de révision, à son historique. L'historique Git pourra ensuite être parcouru et travaillé autour de ces étapes (révisions). Pour qu'un outil de gestion de révision puisse être utilisé à son plein potentiel (et pas comme un simple outil de sauvegarde), l'utilisateur doit prendre soin de créer des **révisions atomiques**. Il est essentiel que chaque révision corresponde à une modification logique et structurée du logiciel pour que certains outils intégrés à Git puisse être utilisés efficacement. Le rôle de Git est de suivre l'évolution du code source d'un projet. Il conserve la trace de l'ensemble des révisions formant **l'historique**. L' interaction entre l’utilisateur et Git se concentre essentiellement sur la manipulation de cet historique, qu’il s’agisse d’y ajouter du contenu (nouvelle révision), d’en modifier l’agencement des révisions (rebase) ou de changer le contenu d’une révision (fixup). Avec Git on peut réécrire l'histoire pour lui donner un sens logique et l'ordonner. On peut également créer des réalités parallèles (branches) évoluant de façon indépendante. **commit**: Sélectionner (désigner ou grouper) une série de modifications apportées sur un ou plusieurs fichiers afin de créer une révision au sein de l'historique Git. Le commit contient des métadonnées (ID, auteur, date, commentaire, etc) permettant à l'utilisateur de documenter la révision. De nombreuses commandes Git requièrent l'ID du commit souvent désigné par "sha". L'**espace de travail** est l'arborescence sur le système de fichier contenant les sources manipulables par l'utilisateur. C'est en comparant l'état des sources au sein de l'espace de travail avec une révision que Git peut indiquer à l'utilisateur les éventuels changement à intégrer à l'historique du projet. L'espace de travail peut être créé/recréer par extraction d'une révision précise de l'historique. **checkout**: Extraction d'une révision de l'historique vers l'espace de travail. Grâce au checkout, on peut remplacer les changements apportés dans le répertoire de travail existant et revenir dans l'état antérieur correspondant à cette révision. Un très bon [[http://ndpsoftware.com/git-cheatsheet.html|support interactif]] décrit les différentes zones et les actions des principales commandes. L'historique d'un projet n'est pas forcément linéaire. Il peut contenir des **branches**. Un projet peut donc avoir un historique présentant des branches parallèles et c'est même une des principales fonctionnalités d'un tel outil. Une **branche** permet de gérer de façon isolée une série de changements. Les applications les plus communes concernent les **branches de maintenances** et les **branches de développement** apportant de nouvelles fonctionnalités. Branche de maintenance: Créée à partir d'une ancienne révision, elle contient un ensemble de correctifs. La branche restera parallèle et ne rejoindra jamais la branche principale. Ici on cherche à publier de nouvelles versions corrigées d'un produit existant. Il est parfois pertinent qu'un changement appliqué sur une branche puisse être fait également sur une autre. Dans le cas par exemple d'un défaut affectant plusieurs versions du logiciel. Git possède un ensemble d'outils facilitant grandement ce travail désigné sous le terme **backport**. Branche développement: Rejoins tôt ou tard la branche d'où elle provient. Une fois la fonctionnalité implémentée, l'ensemble des révisions rejoint la branche parente. Lors de cette opération deux possibilités: * **Rebase**: L'ensemble des modifications de la branche de développement sont appliquées directement sur l'état actuel de la branche cible. Dans ce cas, l'historique ne conservera pas de trace de l' existence de branche de développement. Le **rebase** permet de conserver un historique linéaire et séquentiel. * **Merge** ou fusion: Dans ce cas l'historique conserve l' existence de la branche, des points de divergence et de fusion. Ces opérations rebase ou merge sont automatisée mais peuvent avoir besoin d'intervention en cas de conflit. Le nombre de révisions pouvant rapidement devenir important même pour de modestes projets, un système d'étiquetage (**tag**) permet d'associer un numéro de version, un nom à une révision précise. ===== Installer ===== $ sudo apt-get install git ==== Configuration globale ==== Les informations définies dans la configuration globale sont stockées dans le fichier ''~/.gitconfig''. Ce sont les valeur par défaut qui s'appliqueront sur l'ensemble des dépôts gérés par l'utilisateur. Pour chaque dépôt il est possible de redéfinir ces valeurs. Pour lister les paramètres globaux: $ git config --global -l C'est particulièrement utile pour définir des alias de commandes git qui seront ainsi disponibles sur tous les dépôts. ==== Créer un dépot ==== Par clonage d'un dépot existant, ou par initialisation. Créer d'abord le dossier puis initialiser le depot dans le dossier. $ mkdir test-git $ cd test-git/ $ git init Initialized empty Git repository in /tmp/test-git/.git/ ===== Workflow ===== Dans l'idéal, La branche principale (master) comporte les états stables du projet. Pour ajouter des fonctionnalités, créer une branche, travailler dans la branche et lorsque la fonctionnalité est implémentée et le code stable, fusionner sur la branche master. Git n'est pas un outil de sauvegarde, les commits journaliers par exemple ne permettront pas de tirer pleinement parti du potentiel de Git. Les commits doivent être atomiques et correspondre à des fonctions logiques unitaires et structurées du projet logiciel. Un commit doit se limiter par exemple à un correctif précis et identifié ou à l'ajout d'une seule fonctionnalité. L'ajout de révisions atomiques permettront l'utilisation d'outils avancés tel que bisec ou cherry-pick ==== Créer une branche ===== Pour afficher la branche courante: $ git branch experimental * master Ici deux branches existent: ''master'' et ''experiemental'', nous nous trouvons sur la branche master. Changer de branche $ git checkout experimental Switched to branch 'experimental' La branche courante est à présent ''experimental''. Les prochains commits se feront sur cette branche. La commade **checkout** et le commutateur **-b** permettent la création et le changement de branche en une seule commande. $ git checkout -b bug Switched to a new branch 'bug' Après invocation de cette commande, la branche bug est créée et l'utilisateur est placé sous la branche bug. Il peut maintenant apporter les modifications nécessaires sur les fichiers sources. ==== Etat des modifications ==== La commande **git status** donne un récapitulatif de l'état courant, elle liste notamment: * Les fichiers présents dans l'index, prêts à être commités (archivés dans le dépôt). * Les fichiers existants dans le dépôt, ayant subis des modifications dans l'espace de travail mais non présents dans l'index (liste not updated). * Les fichiers existants dans l'espace de travail, n'ayant jamais été commités et donc qi ne sont pas encore suivis par le système de gestion de versions (liste untracked). $ git status # On branch experimental # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: licence.txt # # Changed but not updated: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # # modified: doc/index.htm # # Untracked files: # (use "git add ..." to include in what will be committed) # # src/hello.c Dans l'exemple ci-dessus ''**git status**'' nous informe que nous sommes sur la branche ''experimental'', que le fichier ./licence.txt est dans l'index, il fera donc parti du prochain commit. Le fichier doc/index.htm présent dans notre espace de travail a été modifié, il diffère de celui présent dans le dépot. Un fichier src/hello.c est présent dans l'espace de travail mais n'a jamais été commité. === Afficher les modifications apportées sur un fichier === ''**git diff**'' permet de visualiser les modifications apportées sur un fichier en comparant le fichier de l'espace de travail avec la version disponible dans le dépot: $ git diff path/to/file.c Ceci permet de comparer le fichier présent dans l'espace de travail et celui présent dans le dépot git. ==== Ajouter des fichiers dans l'index ==== L'**index** est une zone d'assemblage (staging area). Seuls les fichiers présents dans l'index seront envoyés sur le dépot lors du commit. Pour ajouter un fichier dans l'index: $ git add path/to/file.c Pour placer dans l'index tous les fichiers de l'espace de travail modifiés mais déjà suivis par git: git add --update Cette commande ajoute seulement à l'index les fichiers déja commités mais ayant subit des modification depuis le dernier commit. Ils sont ainsi prêts à être historisés à nouveau. ==== Ignorer des fichiers ==== Certains fichiers ou dossiers n'ont pas besion d'être suivis, il peuvent être systèmatiquement ignorés par la commande **add**(par exemple les logs). Pour cela créer à la racine du projet un fichier **./.gitignore** contenant la liste des fichiers et dossiers à ignorer. ==== Commit des modifications ==== L'opération de commit placera les fichiers de l'index (staging area) dans le dépot. Les fichiers en cours de modification dans l'espace de travail ne seront pas automatiquemnt placés dans le dépot même s'ils ont déjà été commités auparavant! Il faut bien penser à placer tous les fichiers que l'on souhaite envoyer sur le dépot dans la staging area à l'aide de la commande ''**git add**''. $ git commit La commande précédente affichera l'éditeur permettant de saisir le message d'accompagnement du commit. Une fois validé, les fichiers sont historisés, une nouvelle révision est enregistrée dans le dépot. Le modificateur ''**-a**'' permet de placer automatiquement les fichiers modifiés de l'espace de travail dans l'index et de lancer le commit. Attention cependant les nouveaux fichiers (untraked) ne seront pas ajoutés. $ git commit -a ==== Fusionner la branche ==== Une fois un état stable atteint et le commit réalisé, les modifications peuvent être rapportées sur la branche principale (master): $ git checkout master $ git merge bug Ici on se place sur la brache ''master'' et on fusionne les modifications apportées dans la branche ''bug'' sur la branche ''master''. Lors de la fusion des conflits peuvent se révélés. Dans ce cas le processus de fusion automatique est arrêté. La commande **git status** permet de lister les fichiers en conflit, il faut: * Editer les fichiers pour régler les conflits, * Ajouter les fichiers dans la zone d'assemblage (l'index), * Terminer la fusion avec la commande **git commit**. ==== Suppression d'une branche temporaire ==== Une fois la branche fusionnée sur la brache ''master'', si aucune autre modification ne doit avoir lieu, elle peut être supprimée: $ git branch -d bug Deleted branch bug (was a0431c3). Ici la branche ''bug'' ayant été fusionnée sur ''master'' et les corrections du bug apportées, elle n'a plus de raison d'être, elle est donc supprimée. ==== Afficher l'arborescence ==== git log --graph --oneline --all