Alternatives à Git Submodule : Git Subtree

Nicola Paolucci
Nicola Paolucci
Retour à la liste

The Internet is full of articles on why you should not use Git submodules. I mostly agree, although I am not so harsh in my evaluation. submodules are useful for a few use cases but have several drawbacks.

Are there alternatives? The answer is: yes! There are (at least) two tools that can help track the history of software dependencies in your project while allowing you to keep using git:

In this post I will be looking at git subtree and show why it is an improvement - albeit not perfect - over git submodule.

As a working example I run to my usual use case. How do I easily store and keep up to date the vim plugins used in my dotfiles?

Pourquoi utiliser un subtree et pas un submodule ?

There are several reasons why you might find subtree better to use:

  • Il est facile de gérer un simple workflow.
  • Older version of git are supported (even before v1.5.2).
  • The sub-project's code is available right after the clone of the super project is done.
  • subtree does not require users of your repository to learn anything new, they can ignore the fact that you are using subtree to manage dependencies.
  • subtree does not add new metadata files like submodules doe (i.e. .gitmodule).
  • Le contenu du module peut être modifié sans avoir une copie de dépôt distincte de la dépendance à un autre emplacement.

À mon avis, les inconvénients sont acceptables :

  • You must learn about a new merge strategy (i.e. subtree).
  • Contributing code back upstream for the sub-projects is slightly more complicated.
  • C'est à vous de vous assurer de ne pas mélanger le code du superprojet avec celui du sous-projet.

Comment utiliser git subtree ?

git subtree is available in stock version of git available since May 2012 – 1.7.11+. The version installed by homebrew on OSX already has subtree properly wired but on some platforms you might need to follow the installation instructions.

Let me show you the canonical example of tracking a vim plug-in using git subtree.

La solution rapide sans suivi distant

Si vous souhaitez juste obtenir quelques commandes sur une ligne pour couper et coller, lisez simplement ce paragraphe.

First add the subtree at a specified prefix folder:

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

(The common practice is to not store the entire history of the sub-project in your main repository, but If you want to preserve it just omit the --squash flag.)

La commande ci-dessus génère la sortie suivante :

git fetch 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.
* branch master -} FETCH_HEAD
Added dir '.vim/bundle/tpope-vim-surround'

As you can see this records a merge commit by squashing the whole history of the vim-surround repository into a single one:

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]

If after a while you want to update the code of the plugin from the upstream repository you can just subtree pull:

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

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

Ajout du sous-projet en tant que remote

L'ajout du subtree en tant que remote nous permet d'y faire référence sous sa forme abrégée :

git remote add -f tpope-vim-surround

Nous pouvons ajouter le subtree (comme avant), mais nous pouvons désormais faire référence au remote sous sa forme abrégée :

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

On obtient alors cette commande de mise à jour du sous-projet à une date ultérieure :

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

Revenir à l'upstream

Désormais, nous pouvons librement faire un commit des corrections apportées au sous-projet dans notre répertoire de travail local.

When it's time to contribute back to the upstream project we need to fork the project and add it as another remote:

git remote add durdn-vim-surround ssh://

Now we can use the subtree push command like the following:

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://
02199ea..dcacd4b dcacd4b21fe51c9b5824370b3b224c440b3470cb -} master

After this we're ready and we can open a pull-request to the maintainer of the package.

Sans l'aide de la commande subtree

git subtree is different from the subtree merge strategy. You can still use the merge strategy even if for some reason git subtree is not available. Here is how you would go about it:

Add the dependency as a simple git remote:

git remote add -f tpope-vim-surround

Avant de consigner le contenu de la dépendance dans le dépôt, il est important d'enregistrer un merge pour pouvoir suivre l'arborescence complète du plug-in jusqu'au point suivant :

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

Qui génère :

Automatic merge went well; stopped before committing as requested

Ensuite, nous consignons le contenu du dernier objet arbre du dépôt du plug-in dans notre répertoire de travail prêt à être commité :

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

À présent, nous pouvons faire un commit (il s'agira d'un commit de merge qui conservera l'historique de l'arborescence consigné) :

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

When we want to update the project we can now pull using the subtree merge strategy:

git pull -s subtree tpope-vim-surround master


After having used submodule for a while I appreciate git subtree much more, lots of submodule problems are superseded and solved by subtree. As usual, with all things git, there is a learning curve to make the most of the feature.

Follow me @durdn and the awesome @Bitbucket team for more Git rocking.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant