Git subtree: une alternative à Git submodule

2019-05-15 Git

Internet regorge d’articles sur les raisons pour lesquelles vous ne devriez pas utiliser les sous-modules Git. Je suis en grande partie d’accord, même si je ne suis pas si sévère dans mon évaluation. Comme je l’ai expliqué dans un article précédent, les sous-modules sont utiles pour quelques cas d’utilisation mais ont plusieurs inconvénients.

Existe-t-il d’autres solutions ? La réponse est : oui ! Il y a (au moins) deux outils qui peuvent vous aider à suivre l’historique des dépendances logicielles dans votre projet tout en vous permettant de continuer à utiliser Git :

  • git subtree
  • Google repo

Dans ce billet, je vais regarder git subtree git montrer pourquoi c’est une amélioration - bien qu’imparfaite - sur le sous-module git. A titre d’exemple, j’utilise mon cas d’utilisation habituel : "Comment puis-je facilement stocker et mettre à jour les plugins vim utilisés dans mes dotfiles ?"

1. Qu’est-ce que git subtree, et pourquoi devrais-je l’utiliser ?

Git permet d’imbriquer un référentiel dans un autre sous forme de sous-répertoire. C’est l’une des nombreuses façons dont les projets Git peuvent gérer les dépendances de projet.

Il y a plusieurs raisons pour lesquelles git subtree vaut la peine d’être considéré :

  • La gestion d’un workflow simple est facile.
  • Les anciennes versions de Git sont supportées (même plus anciennes que la v1.5.2).
  • Le code du sous-projet est disponible dès que le clone du super projet est terminé.
  • git subtree n’exige pas des utilisateurs de votre référentiel d’apprendre quoi que ce soit de nouveau. Ils peuvent ignorer le fait que vous utilisez git subtree pour gérer les dépendances.
  • git n’ajoute pas de nouveaux fichiers de métadonnées comme le fait git submodule (i.e.,.gitmodule).
  • Le contenu du module peut être modifié sans avoir une copie séparée du référentiel de la dépendance ailleurs.

Il y a aussi quelques inconvénients, mais à mon avis, ils sont acceptables :

  • Vous devez vous renseigner sur une nouvelle stratégie de fusion (c.-à-d. git subtree).
  • La contribution de code en amont pour les sous-projets est un peu plus compliquée.
  • La responsabilité de ne pas mélanger le code du super-projet et du sous-projet dans les commits vous incombe.

2. Comment utiliser git subtree ?

git subtree est disponible dans Git depuis mai 2012 - v1.7.11 et supérieure. La version installée par homebrew sur OSX a déjà une sous-arborescence correctement câblée, mais sur certaines plates-formes vous devrez peut-être suivre les instructions d’installation.

Laissez-moi vous montrer l’exemple canonique du suivi d’un plug-in vim en utilisant git subtree.

2.1. Le plus rapide et le plus sale : le chemin sans suivi à distance

Si vous voulez juste quelques lignes à copier-coller, lisez ce paragraphe. Tout d’abord, ajoutez git subtree à un dossier de préfixe spécifié :

git subtree add --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git master --squash

(La pratique courante est de ne pas stocker l’historique complet du sous-projet dans votre référentiel principal, mais si vous voulez le préserver, omettez simplement l’option -squash.)

La commande ci-dessus produit cette sortie :

git fetch https://bitbucket.org/vim-plugins-mirror/vim-surround.git master
warning: no common commits
remote: Counting objects: 338, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 338 (delta 101), reused 323 (delta 89)
Receiving objects: 100% (338/338), 71.46 KiB, done.
Resolving deltas: 100% (101/101), done.
From https://bitbucket.org/vim-plugins-mirror/vim-surround.git
* branch master -} FETCH_HEAD
Added dir '.vim/bundle/tpope-vim-surround'

Comme vous pouvez le voir, cela enregistre un commit de fusion en écrasant tout l’historique du dépôt vim-surround en un seul :

1bda0bd [3 minutes ago] (HEAD, stree) Merge commit 'ca1f4da9f0b93346bba9a430c889a95f75dc0a83' as '.vim/bundle/tpope-vim-surround' [Nicola Paolucci]
ca1f4da [3 minutes ago] Squashed '.vim/bundle/tpope-vim-surround/' content from commit 02199ea [Nicola Paolucci]

Si après un certain temps vous voulez mettre à jour le code du plugin à partir du référentiel amont, vous pouvez simplement faire une extraction du git subtree :

git subtree pull --prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git master --squash

C’est très rapide et indolore, mais les commandes sont un peu longues et difficiles à mémoriser. Nous pouvons raccourcir les commandes en ajoutant le sous-projet en tant que remote.

2.2. Ajout du sous-projet en tant que projet distant

L’ajout d’un subtree en tant que sous-projet distant nous permet d’y faire référence sous une forme plus courte :

git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Maintenant nous pouvons ajouter un subtree (comme avant), mais maintenant nous pouvons nous référer au remote sous une forme courte :

git subtree add --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround master --squash

La commande pour mettre à jour le sous-projet à une date ultérieure devient :

git fetch tpope-vim-surround master
git subtree pull --prefix .vim/bundle/tpope-vim-surround tpope-vim-surround master --squash

2.3. Contribuer à la remontée de ces ressources en amont

Nous pouvons librement valider nos corrections pour le sous-projet dans notre répertoire de travail local maintenant. Quand il est temps de contribuer de nouveau au projet amont, nous devons faire un fork du projet et l’ajouter comme un autre projet distant :

git remote add durdn-vim-surround ssh://git@bitbucket.org/durdn/vim-surround.git

Nous pouvons maintenant utiliser la commande push de subtree comme suit :

git subtree push --prefix=.vim/bundle/tpope-vim-surround/ durdn-vim-surround master
git push using: durdn-vim-surround master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 308 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To ssh://git@bitbucket.org/durdn/vim-surround.git
02199ea..dcacd4b dcacd4b21fe51c9b5824370b3b224c440b3470cb -} master

Après cela, nous sommes prêts et nous pouvons ouvrir une pull-request au responsable du paquet.

2.4. Puis-je faire cela sans utiliser la commande git subtree ?

Oui ! Oui vous pouvez. Git subtree est différent de la stratégie de merge de sous-arbres. Vous pouvez toujours utiliser la stratégie de merge même si, pour une raison quelconque, le git subtree n’est pas disponible. Voici comment vous vous y prendriez.

Ajoutez la dépendance comme une simple télécommande git :

git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Avant de lire le contenu de la dépendance dans le référentiel, il est important d’enregistrer une fusion afin de pouvoir suivre l’historique complet de l’arbre du plug-in jusqu’à ce point :

git merge -s ours --no-commit tpope-vim-surround/master

Quels résultats :

Automatic merge went well; stopped before committing as requested

Nous lisons ensuite le contenu du dernier tree-objet dans le référentiel du plugin dans notre répertoire de travail prêt à être livré :

git read-tree --prefix=.vim/bundle/tpope-vim-surround/ -u tpope-vim-surround/master

Maintenant nous pouvons commiter (et ce sera un commit qui préservera l’historique de l’arbre que nous lisons) :

git ci -m"[subtree] adding tpope-vim-surround"
[stree 779b094] [subtree] adding tpope-vim-surround

Lorsque nous voulons mettre à jour le projet, nous pouvons maintenant tirer (pull) en utilisant la stratégie de merge de git subtree :

git pull -s subtree tpope-vim-surround master

3. Git subtree est une excellente alternative

Après avoir utilisé git submodules pendant un certain temps, j’apprécie beaucoup plus git subtree parce qu’il résout beaucoup de problèmes avec git submodule. Comme d’habitude, avec tout ce qui concerne Git, il y a une courbe d’apprentissage pour tirer le meilleur parti de la fonctionnalité.

Suivez-moi sur Twitter @durdn pour plus d’infos sur Git.