Submodules GIT: pourquoi, comment ?

A l’approche d’une conférence pour les CaenCamp, j’ai eu l’occasion de me documenter sur les submodules de GIT. Je me suis heurté à des documentations certes nombreuses, mais très éparpillées, et souvent trop ou pas assez complètes.

Voici donc un exemple concret de l’utilisation des submodules GIT, qui vous aidera j’espère à mieux comprendre et utiliser cet outil.

Pourquoi ?

Les submodules existent depuis GIT 1.5.3, et permettent de cloner un sous-dépôt dans un dépôt principal. Cela vous permet par exemple d’utiliser une librairie tierce dans plusieurs projets, ou de maintenir un module custom simultanément dans plusieurs projets par exemple.
Une fois cloné, ce sous-dépôt est un dépôt GIT à part entière, dans lequel on peut faire des push, des pull, des commits…

Mais le contenu n’est pas stocké dans le dépôt principal. Ce dernier conserve un bookmark vers un commit particulier du sous-dépôt. On peut donc déployer le projet sans que l’utilisateur n’ai besoin de connaitre l’URL du sous-dépôt ou le SHA-1 du commit qui nous intéresse.

Comment ?

Utiliser les submodules n’est pas compliqué, mais demande une certaine pratique pour en comprendre le fonctionnement.

Créer un sous-module

Partons d’un dépôt principal « main », et d’une librairie qui nous servira de sous-module:

On ajoute un fichier dans la librairie et on commite:

On peut maintenant ajouter cette librairie en tant que submodule dans notre dépôt principal:

On retrouve bien le pointeur vers le submodule, GIT stocke un nouveau fichier:

GIT stocke également des informations dans un fichier .gitmodules, avec les entrées de tous les submodules du dépôt (un seul dans notre cas):

On ajoute et on commite ensuite le submodule dans le dépôt principal:

 

Attention: Remarquez qu’on ne met pas de « / » a la fin du submodule lors du git add, car cela demanderait a GIT de mettre dans le stage tous les fichiers non ignorés contenus dans le sous-répertoire. Or, on souhaite conserver le fonctionnement en submodule, et ne tracker que le commit.

GIT pointe maintenant vers le bon commit:

Le commit da57a6d49… correspond au commit de notre dépôt « library »:

Pour mettre à jour le sous-module, si un fichier de « library » a été modifié par exemple, il suffit de se placer dans le sous-dépôt, de faire un pull, gérer les conflits si nécessaire, et commiter les changements dans le dépôt principal.

Cloner un dépôt avec un submodule

Imaginons maintenant que l’on souhaite déployer ce dépôt principal. On clone le dépôt « main » dans un nouveau dépôt « contrib »:

On s’attend a retrouver l’ensemble du contenu du dépôt « main », submodule inclus. Mais…:

Le répertoire est vide… car un submodule doit être initialisé… GIT nous le montre d’ailleurs:

Le « – » précédant le SHA1 indique la présence d’un submodule non initialisé.
Cela se fait en 2 commandes:

que l’on peut synthétiser en une seule ligne:

 

Le contenu a été récupéré, et le statut n’indque plus de « -« , le submodule est donc correctement initialisé.

Par défaut, dans le submodule, GIT se place sur le commit vers lequel pointe le dépôt principal

 

Attention:En cas de changement dans le sous-répertoire my_submodule, il faut toujours penser à commiter les changements dans le dépôt principal. Dans le cas contraire, le dépôt pointe vers un commit différent, et un `git submodule update` effacerait silencieusement vos modifications (silent erase).

Supprimer un submodule

Git n’a pas prévu de `git submodule rm`. Il faut donc nettoyer les fichiers concernés par le submodule, puis effacer les fichiers:

Attention: comme pour `git add`, on n’utilise pas de « / » pour le `git rm`. En effet, rappelez-vous que GIT ne stocke pas le contenu du sous-modules, mais simplement un pointeur vers celui-ci, sous la forme d’un fichier. C’est ce fichier que l’on indique à GIT pour suppression.

Bonus

Git submodule status

Cette commande montre le SHA1 du commit vers lequel pointe le dépôt principal. Le 1er caractère indique son état:

  • « – » : Le submodule n’est pas initialisé (voir plus haut)
  • « + » : Le submodule est initialisé mais le dépôt ne pointe pas vers le dernier commit du submodule
  •  »  » : Le submodule est initialisé et le dépôt pointe vers le dernier commit en date.

Git foreach

Si vous avez plusieurs submodules dans votre dépôt principal, et que vous devez tous les mettre à jour, inutile de passez faire un `git pull`dans chacun des sous-modules un à un. A la place, utilisez `git foreach`, et les pull se feront les uns après les autres.

Forcer le merge lors de l’update

Si vous souhaitez merger automatiquement le commit avec la branche master lors d’un `git submodule update`placez vous dans le répertoire du projet principal et tapez:

Avantages et inconvénients

Les +

  • Les submodules permettent de gérer des dépendances de manière simple, et de déployer un super-projet aux utilisateurs, sans que ceux-ci n’aient besoin de connaître les URL des sous-dépôts ni les SHA1 des commits utilisés.
  • Chaque submodule peut être géré individuellement, et constitue un dépôt GIT à part entière.
  • Ils peuvent être une solution idéale pour gérer un projet perso avec des modules réutilisables.

Les –

  • Ils sont contraignants à maintenir (update + init)
  • On ne peut pas utiliser qu’une partie d’un répertoire dans le submodule
  • Ils sont pensés pour du « one way », c’est-à-dire pour récupérer des mises à jours, du dépôt d’origine vers le dépôt cloné. Si vous avez besoin de contribuer à ces dépôts, tournez vous vers les subtree.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *