Manipuler des dépôts Git

Page de présentation et d'aide sur l'utilisation de Git

Cette page est dédiée à donner quelques pistes concernant l’utilisation de Git, un logiciel de gestion de version, à l’intérieur des projets de 1ère année à l’ENSEIRB.

Les projets se font par équipes, mais les principes décrits dans ces pages doivent être compris par chacun. A la fin de cette feuille, chaque membre de l’équipe doit avoir :

Principes généraux

Lorsque l’on réalise un projet basé sur un ensemble de fichiers source, qu’il s’agisse du code d’un programme en langage C ou du source d’un rapport rédigé en LaTeX, on passe inévitablement par de nombreuses versions successives.

Une version du projet, appelée aussi révision, représente l’ensemble des fichiers appartenant au projet à un moment fixé de son développement, telle une photographie de son état instantané. Une suite de révisions consécutives est appelée une branche. Le projet évolue en suivant un ensemble de branches, à partir de sa révision initiale vers une révision propre à sa mise en production.

Lorsque l’on commence à travailler à plusieurs, chaque membre de l’équipe dispose de sa propre révision, et il n’est pas évident de pouvoir partager ses révisions entre les personnes.


Pour apporter une solution à ces problèmes, on adopte le système suivant :

Après avoir terminé les modifications sur le dépôt local, on peut communiquer/publier ses modifications au dépôt pour créer une nouvelle révision. Cela permettra aux autres participants au projet de récupérer cette révision et de pouvoir travailler avec. Afin de communiquer une modification, git dispose d’un système nommé zone de transit (traduction de staging area) qui permet de choisir finement les modifications que l’on désire publier.

Le diagramme suivant décrit les étapes principales de la vie d’un dépôt (les blocs sont cliquables) :

Créer un dépôt local Récupérer la version la plus récente Gérer un conflit Récupérer un état catastrophique Modifier des fichiers Communiquer une révision Questions / Réponses variées

Vidéo introductive

Sections :

Configurer git

git est un logiciel qui demande peu de configuration, mais nécessaire, voire obligatoire. Il est indispensable de renseigner son nom et son adresse mail. En effet, ces informations permettent de repérer les personnes qui travaillent sur le dépôt.

Pour commencer, nous allons créer un fichier de configuration pour git. Les détails de la configuration peuvent être omis dans un premier temps. Il est possible de générer ce fichier à l’aide de la commande suivante :

ssh ssh.enseirb-matmeca.fr /net/ens/renault/local/bin/init-gitconfig.sh > ~/.gitconfig

ou si vous n’avez pas le même login sur votre machine et à l’école :

ssh <login_enseirb>@ssh.enseirb-matmeca.fr /net/ens/renault/local/bin/init-gitconfig.sh > ~/.gitconfig

Dans un second temps, il sera possible pour les plus aventureux de revenir sur cette étape en lisant la section Configurer son usage de git

Cette commande crée un fichier ~/.gitconfig sur la machine où elle est lancée. Il faudra la relancer sur toutes les machines à partir desquelles vous comptez utiliser git. L’exception, ce sont les machines de l’école, qui partagent le même compte, et donc les mêmes fichiers de configuration.

Créer un dépôt local

  1. Connectez-vous sur la forge de l’ENSEIRB, et rendez-vous sur la page dédiée à votre projet, et suivez plus précisément l’onglet nommé “Dépôt” ou “Repository”.

    Cette page contient une commande de la forme suivante :

    git clone https://<user>@thor.enseirb-matmeca.fr/git/chabadabada

    Il va sans dire qu’il n’existe pas de dépôt nommé chabadabada, et qu’il faut remplacer la partie <user>, ce qui implique que copier/coller directement la commande précédente est un acte inutile.

  2. Visualisez le contenu du dépôt sur l’accès web en suivant le lien https://thor.enseirb-matmeca.fr/web/git/gitweb.cgi?p=chabadabada. Vous devez normalement constater qu’il est vide.

    Un très bon logiciel pour visualiser l’état local de son dépôt est installé sur les machines de l’ENSEIRB avec pour nom gitg (dépôt). Il permet en particulier de visualiser de manière graphique l’ensemble des modifications faites dans le dépôt. Si vous utilisez apt, la commande apt install gitg permet de l’installer.

  3. Placez vous dans un terminal, et déplacez vous dans un répertoire dans lequel vous comptez mettre les sources de vos projets. Ensuite, recopier la commande précédente dans le terminal et l’exécuter. Si tout se passe correctement, le résultat devrait être de l’une des des formes suivantes.

    • Si le dépôt que vous clonez est initialement vide :

      Cloning into 'chabadabada'...
      warning: You appear to have cloned an empty repository.
      
    • Si le dépôt que vous clonez contient déjà des données :

      remote: Counting objects: 72486, done.
      remote: Compressing objects: 100% (36856/36856), done.
      remote: Total 72486 (delta 35803), reused 70908 (delta 34681)
      Receiving objects: 100% (72486/72486), 226.64 MiB | 8.30 MiB/s, done.
      Resolving deltas: 100% (35803/35803), done.
      

    La commande a créé un répertoire de travail appelé chabadabada dans le répertoire courant, contenant la dernière révision de la branche principale. Il est toujours possible de modifier le nom du répertoire de travail après coup, par exemple en le déplaçant.

    Initialement, ce répertoire contient un ensemble de fichiers sources déjà ajoutés au dépôt, ainsi qu’un sous-répertoire nommé .git/ servant à gérer le dépôt lui-même.

  4. Assurez vous que chaque membre de votre équipe ait bien créé un répertoire de travail personnel. C’est important pour la suite.

Composer du code dans le dépôt

  1. Se positionner à l’intérieur de son répertoire de travail (à l’intérieur de celui nommé chabadabada si vous n’avez pas changé son nom).

    Créer un fichier authors.txt vide.

    touch authors.txt
    
  2. Ajouter une ligne à l’intérieur du fichier contenant vos noms, prénoms et adresse mail.

Communiquer une révision

L’opération que nous allons décrire permet de valider un ensemble de modifications dans le code pour créer une nouvelle révision, et est communément appelé un commit en anglais. Dans cet exemple, nous allons supposer que cette modification porte sur un unique fichier authors.txt.

  1. Visualiser l’état des fichiers dans le dépôt avec la commande suivante :

    git status
    

    Si le fichier n’a jamais été communiqué au dépôt, le résultat devrait être de la forme :

    Untracked files:
      (use "git add <file>..." to include in what will be committed)
    
         authors.txt

    Si le fichier était déjà présent dans le dépôt, le résultat devrait être de la forme  :

    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
         modified:   authors.txt
  2. Valider cette modification localement :

    git add authors.txt
    

    Cette commande indique que localement, les modifications du fichier authors.txt ont été ajoutées au répertoire de travail. La commande git status devrait maintenant indiquer :

    Changes to be committed:
      (use "git reset HEAD <file>..." to unstage)
    
       modified:   authors.txt

    En cas d’erreur sur l’ajout de la modification, il est toujours possible de revenir en arrière (comme indiqué par git) avec la commande git reset HEAD authors.txt. De plus, il reste toujours possible, sans faire de reset, de continuer à modifier authors.txt et de faire un autre git add plus tard, si l’on estime qu’il manque encore quelques finitions.

  3. Valider la modification localement, en ajoutant un commentaire :

    git commit
    

    Normalement, cette commande ouvre un éditeur dans lequel écrire le commentaire. Il est possible de configurer cet éditeur en positionnant la variable EDITOR dans son environnement, par exemple export EDITOR=emacs. Le commentaire peut être passé directement en ligne de commande en passant l’option -m à git. Il existe un ensemble de bonnes pratiques pour écrire des messages de commits, par exemple ici.

    Une fois la modification validée, une nouvelle révision a été créée.

  4. Comme il s’agit d’une modification des sources, il faut la communiquer au dépôt distant. Le dépôt distant se nomme par défaut origin, et la branche à communiquer master. Communiquer votre modification des sources avec la commande suivante :

    git push origin master
    
  5. Constater que vos modifications sont bien apparues dans le dépôt, en utilisant l’accès web et le logiciel gitg.

    gitg est un outil local. Les autres membres de l’équipe ne pourront visualiser les modifications sur gitg qu’après avoir récupéré la version la plus récente du dépôt.

    Sur gitg, il devrait être possible de distinguer les étiquettes de la branche locale (master) et de la branche distante (origin/master).

  • La décomposition d’un développement en suite de révisions simple est une opération fondamentale en terme de développement logiciel. Il devient possible de valider plusieurs modifications successivement, en leur assignant des commentaires différents. En conséquence, retrouver des modifications après coup devient bien plus aisé.

  • Les commentaires sont une source d’information importante pour comprendre la manière dont évoluent les sources. Ils doivent être à même d’expliquer précisément les modifications que vous venez de faire. Un commentaire vide est un acte improductif.

Récupérer la version la plus récente du dépôt

Lorsque l’on reprend son travail après une période d’absence ou plus simplement avant de communiquer ses modifications locales à distance, il est impératif de vérifier que l’on est bien cohérent avec les modifications les plus récentes du dépôt.

  1. Avant toute chose, vérifier l’état du dépôt en s’assurant qu’il n’y a pas de modifications locales.

  2. Synchroniser le dépôt local avec le dépôt distant, nommé origin :

    git fetch origin
    
    remote: Counting objects: 3, done.
    remote: Total 3 (delta 0), reused 0 (delta 0)
    Unpacking objects: 100% (3/3), done.
    From /path/to/the/local/repository
    6ec514a..cab0f1b  master     -> origin/master

    La dernière ligne indique qu’il y a eu une modification sur la branche principale (master) dans le dépôt distant (origin/master). Il est possible de visualiser les branches locales et distantes en passant par l’accès web ou par gitg.

  3. Fusionner (en anglais, “to merge”) les modifications distantes dans votre dépôt local :

    git merge origin/master
    
    Updating 6ec514a..cab0f1b
    Fast-forward
     authors.txt | 1 +
     1 file changed, 1 insertion(+)

    Normalement, la fusion s’est déroulée sans erreurs, ce que l’on peut voir grâce au mot clé Fast-forward. Il est néanmoins possible que la fusion ne puisse être réglée simplement, auquel cas le message obtenu devrait être de la forme :

    Auto-merging authors.txt
    CONFLICT (content): Merge conflict in authors.txt
    Automatic merge failed; fix conflicts and then commit the result.

    Dans ce cas, il faut se rendre à la section Gérer un conflit.

L’utilisateur expérimenté de git réalisera les deux opérations précédentes en une seule commande : git pull origin master. Il est néanmoins important de noter qu’il est toujours possible de décomposer cette action, pour visualiser les modifications distances avant de les fusionner avec son code.

Continuer à composer dans le dépôt

  1. Pour simuler un développement à plusieurs sur le dépôt, chaque membre de l’équipe peut ajouter son identité dans le fichier authors.txt.

  2. Une fois cela fait, les autres membres de l’équipe récupèrent la dernière version du dépôt et la fusionnent avec leur version locale.

Questions et réponses variées

Visualiser l’état du dépôt

La commande suivante permet de visualiser l’état du dépôt :

git status

Selon l’état dans lequel se trouve le dépôt, il est possible d’obtenir une somme d’informations comme :

On branch master
•Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)

•Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   README.txtChanges not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   authors.txtUntracked files:
  (use "git add <file>..." to include in what will be committed)

        liste_des_courses.txt

Git est un logiciel assez prévenant, et il est donc possible de voir ici 4 items (mis en évidence par les ‘•’) :

Annuler le dernier commit

Il est assez facilement possible de se retrouver dans une situation où l’on a fait une commande git commit, pour se rendre compte instantanément que ce n’était pas ce que l’on désirait. Plusieurs situations possibles :

Gérer un conflit

Du fait de la présence de multiples dépôts dans lesquels le code peut être modifié, il est possible que la même branche master ait divergé entre deux dépôts (typiquement avec la même branche sur un dépôt distant, comme origin/master). Dans ce cas, une fusion/merge risque d’amener à un conflit. Il convient de ne surtout pas paniquer, cela fait partie des tracas standards du développement logiciel.

Mettons que la dernière fusion/merge ait amené au résultat suivant :

Auto-merging authors.txt
CONFLICT (content): Merge conflict in authors.txt
Automatic merge failed; fix conflicts and then commit the result.

Un conflit peut aussi être visualisé à l’aide de la commande git status, qui devrait indiquer :

On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   authors.txt

no changes added to commit (use "git add" and/or "git commit -a")

Il faut bien noter que l’opération de fusion/merge ne s’est pas terminée (“no changes added to commit”). Git vous met dans un état où il est possible de l’assister à terminer cette fusion correctement.

Pour chaque fichier en conflit, appliquer la procédure suivante :

  1. Ouvrir le fichier dans un éditeur (dans notre exemple authors.txt).

  2. Rechercher dans l’éditeur les blocs de la forme suivante :

    <<<<<<< HEAD
    David, le programmeur extrémiste.
    =======
    David, le programmeur de l'extrême.
    >>>>>>> origin/master
    

    Git a modifié de lui-même le fichier pour inclure, aux endroits qui lui semblaient différents, les deux versions. Il est facile de rechercher ces blocs car les marqueurs <<<<<<<, ======= et >>>>>>> ne sont quasiment jamais utilisés.

    • La zone délimitée par <<<<<<< et ======= représente la version locale du code (branche master).

    • La zone délimitée par ======= et >>>>>>> représente la version distante du code (branche origin/master).

  3. Modifier le code de manière à éliminer tous les marqueurs.

    Il est ainsi facile de choisir une version des deux versions à garder, ou de faire une sorte de mélange des deux si besoin.

  4. A la fin des modifications, ajouter le code au commit (ici le fichier en conflit)

    git add authors.txt
    
  5. Une fois tous les fichiers en conflit gérés, terminer la fusion (un message de commit est généré automatiquement) :

    git commit
    

Récupérer un état catastrophique

Il est toujours possible de se retrouver dans une situation dans laquelle continuer à faire des commandes git est difficile ou met en péril l’état du code source sur lequel on travaille. Il peut s’agir en particulier :

La procédure suivante consiste à reprendre la dernière version du dépôt sur la forge, y copier ses modifications et les committer. Si vous l’appliquez à la lettre, elle assure de ne perdre aucune information pour ce qui est du code. Dans les exemples de commandes, la procédure fait l’hypothèse que le dépôt s’appelle chabadabada.

Si vous en arrivez à cette appliquer cette procédure, c’est forcément après avoir vérifié qu’il n’existait pas une méthode moins grossière pour sauver votre travail de développement.

  1. Se placer dans le répertoire parent du répertoire correspondant à votre dépôt, et le renommer.

    mv chabadabada chabadabada.old
    
  2. Recréer une version du dépôt local.

    git clone https://<user>@thor.enseirb-matmeca.fr/git/chabadabada
  3. Copier les données de l’ancien dépôt dans le dépôt nouvellement créé.

    cp -R chabadabada.old/* chabadabada/
    
  4. Communiquer les modifications dans le dépôt distant.

    cd chabadabada/
    git commit -m -a "Récupération catastrophique"
    git push origin
    

A la fin de la procédure, le dépôt chabadabada contiendra la dernière version du code du dépot initial. Tous les commits intermédiaires auront été perdus. Les fichiers supprimés du dépôt entre temps devront être supprimés à nouveau. Si vous estimez ne plus avoir besoin des vieux commits, vous pouvez après coup supprimer le dépôt chabadabada.old.

Renommer les auteurs des commits

Cette procédure permet de modifier les noms et emails d’utilisateurs de certains commits. Dans le cas où ces commits sont rejetés par la forge, elle permet de récupérer une situation mal engagée. Dans tous les cas, commencez par configurer votre dépôt et n’appliquez cette procédure qu’après avoir fait une copie dudit dépôt.


Commencez par compter combien et quels sont les commits fautifs :

Modifier des commits passés

Cette procédure permet de modifier entièrement un ensemble de commits n’ayant pas encore été poussés sur la forge. Dans le cas où ces commits sont rejetés par la forge. Dans tous les cas, commencez par configurer votre dépôt et n’appliquez cette procédure qu’après avoir fait une copie dudit dépôt.

Commencez par compter combien et quels sont les commits fautifs :

Se connecter à son dépôt

Les utilisateurs de machines personnelles possèdent une configuration propre qui est parfois différente de celle des machines de l’école. En particulier, leur login peut différer. Pour éviter ce genre de problème, lorsqu’on clone un dépôt, on référence usuellement le login dans l’URL de connexion :

git clone https://<user>@thor.enseirb-matmeca.fr/git/chabadabada

Il est aussi possible de positionner cette information après coup, en modifiant le fichier .git/config à l’intérieur de son dépôt :

[remote "origin"]
        url = https://<user>@thor.enseirb-matmeca.fr/git/chabadabada
        fetch = +refs/heads/*:refs/remotes/origin/*

Noter que les informations pour se connecter sur un serveur sont propres au serveur. Ainsi, le mot de passe utilisé pour se connecter à la forge de l’école est le même que celui pour se connecter sur les machines de l’école (et pas son mot de passe personnel par exemple).

Configurer son usage de git

Il est possible de configurer un certain nombre de préférences pour l’utilisation de tous ses dépôts git. Le fichier de configuration est usuellement placé dans son répertoire personnel comme $HOME/.gitconfig. En cas de perte de son fichier, ou pour revenir à une configuration initiale, il suffit d’appliquer :

ssh <login_enseirb>@ssh.enseirb-matmeca.fr /net/ens/renault/local/bin/init-gitconfig.sh > ~/.gitconfig

Le fichier .gitconfig est un fichier texte de la forme suivante :

[http]
        sslVerify = false
[color]
        ui = auto

Il est parfaitement possible de modifier ce fichier dans un éditeur, mais il est aussi possible de le faire à l’aide de la commande suivante :

git config --global color.ui "auto"

Si la commande précédente ne comporte pas le --global, et qu’elle est effectuée depuis un dépôt git, alors la configuration ne concernera que ce dépôt.

Parmi les préférences que l’on peut définir dans ce fichier, il y en a deux qui sont particulièrement importantes :

Sans ces deux préférences, git fera des choix automatiques au moment de communiquer les révisions, et ces choix sont moins que judicieux. N’oubliez donc pas de positionner ces variables correctement.

Tout dépôt GIT utilisé sur la forge de l’ENSEIRB ne peut être utilisé qu’avec une configuration correcte, à savoir un nom d’utilisateur valide et un email propre à l’école. Toute utilisation du dépôt en dehors de ces conditions d’utilisation pourra être rejetée.

Les éléments qui sont testés actuellement sont le nom de la personne réalisant le commit et son adresse mail. Le script init-gitconfig.sh décrit ci-dessus génère un fichier de configuration acceptable.

Ignorer la présence de fichiers

Le fichier .gitignore à la racine du dépôt permet de lister les fichiers que Git va ignorer, à savoir ceux qui n’apparaîtront pas dans la section Untracked files lors des appels à la commande git status.

Un exemple de fichier .gitignore pour des projets en C :

# .gitignore -- List of files ignored by git
*.[oa]
*~

Étiqueter les commits

Il est possible d’étiqueter (tag) des commits particuliers en leur donnant un nom plus lisible que les commits classiques. Ces étiquettes permettent ainsi de mettre en valeur des commits correspondant à des versions intéressantes (par exemple correspondant à la version finale du code). Il existe dans git deux types d’étiquettes :

Typiquement, la commande suivante permet de créer un tag local nommé version-1.0 :

git tag version-1.0

L’étiquette reste locale, tant que l’on ne l’a pas versée dans un dépôt distant. Pour cela, il suffit de faire :

git push origin version-1.0

Une fois un tag disponible dans un dépôt, il devient possible de récupérer la version correspondante simplement avec son nom :

git checkout version-1.0

Enfin, la commande suivante affiche la liste des tags existants :

git tag # Display the list of available tags

Pour des informations complémentaires, consulter la man page de git tag et la page de documentation du livre Pro Git.

Pour aller plus loin …

Il est bien évident que cette page passe sous silence un nombre de détails gigantesques quant à ce logiciel tentaculaire qu’est git, et c’est pourquoi il est possible de trouver d’autres sources de documentations plus génériques et/ou plus avancées en se référant à :

Liste d’outils sympa autour de git