Maintenant que vous avez vu les difficultés qu'il peut y avoir avec le système de sous-module, voyons une alternative pour résoudre la même problématique. Lorsque Git fusionne, il regarde ce qu'il doit fusionner et choisit alors une stratégie de fusion appropriée. Si vous fusionnez deux branches, Git utilise une stratégie récursive (recursive strategy). Si vous fusionnez plus de deux branches, Git choisit la stratégie de la pieuvre (octopus strategy). Ces stratégies sont choisies automatiquement car la stratégie récursive peut gérer des problèmes complexes de fusions à trois entrées avec par exemple plus d'un ancêtre commun, mais il ne peut gérer que deux branches à fusionner. La fusion de la pieuvre peut gérer plusieurs branches mais elle est plus prudente afin d'éviter les conflits difficiles, elle est donc choisie comme stratégie par défaut si vous essayez de fusionner plus de deux branches.
Cependant, il existe d'autres stratégies que vous pouvez tout aussi bien choisir.
L'une d'elles est la fusion de sous-arborescence que vous pouvez utiliser pour gérer la problématique du sous-projet.
Nous allons donc voir comment gérer l'inclusion de rack
comme dans la section précédente, mais en utilisant cette fois-ci les fusions de sous-arborescence.
La fusion de sous-arborescence suppose que vous ayez deux projets et que l'un s'identifie à un sous-répertoire de l'autre. Lorsque vous spécifiez une fusion de sous-arborescence, Git est assez intelligent pour deviner lequel est un sous-répertoire de l'autre et fusionne en conséquence — c'est assez bluffant.
Premièrement, vous ajoutez l'application Rack à votre projet. Vous ajoutez le projet Rack comme une référence distante dans votre propre projet et le récupérez dans sa propre branche :
$ git remote add rack_remote git@github.com:schacon/rack.git
$ git fetch rack_remote
warning: no common commits
remote: Counting objects: 3184, done.
remote: Compressing objects: 100% (1465/1465), done.
remote: Total 3184 (delta 1952), reused 2770 (delta 1675)
Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done.
Resolving deltas: 100% (1952/1952), done.
From git@github.com:schacon/rack
* [new branch] build -> rack_remote/build
* [new branch] master -> rack_remote/master
* [new branch] rack-0.4 -> rack_remote/rack-0.4
* [new branch] rack-0.9 -> rack_remote/rack-0.9
$ git checkout -b rack_branch rack_remote/master
Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master.
Switched to a new branch "rack_branch"
Vous avez maintenant la racine du projet Rack dans votre branche rack_branch
et votre propre projet dans la branche master
.
Si vous récupérez l'une puis l'autre branche, vous pouvez voir que vous avez différentes racines de projet :
$ ls
AUTHORS KNOWN-ISSUES Rakefile contrib lib
COPYING README bin example test
$ git checkout master
Switched to branch "master"
$ ls
README
Pour tirer le projet Rack dans votre projet master
comme un sous-répertoire, vous pouvez utiliser la commande git read-tree
.
Vous apprendrez davantage sur read-tree
et compagnie dans le chapitre 9, mais pour le moment, sachez qu'il lit la racine d'une de vos branches et l'inscrit dans votre index et votre répertoire de travail.
Vous venez juste de commuter vers votre branche master
et vous tirez la branche rack
vers le sous-répertoire rack
de votre branche master
de votre projet principal :
$ git read-tree --prefix=rack/ -u rack_branch
Au moment de valider, vous verrez tous les fichiers de Rack de ce sous-répertoire, comme si vous les aviez copiés depuis une archive. Ce qui est intéressant, c'est que vous pouvez assez facilement fusionner les changements d'une branche à l'autre. Par conséquence, s'il y a des mises à jour pour le projet Rack, vous pouvez les tirer depuis le dépôt principal en commutant dans cette branche et tirant les modifications :
$ git checkout rack_branch
$ git pull
Puis, vous pouvez fusionner ces changements dans votre branche principale.
Vous pouvez utiliser git merge -s subtree
et cela fonctionnera, mais Git fusionnera également les historiques ensemble, ce que vous ne voulez probablement pas.
Pour tirer les changements et préremplir le message de validation, utilisez les options --squash
et --no-commit
avec l'option de stratégie -s subtree
:
$ git checkout master
$ git merge --squash -s subtree --no-commit rack_branch
Squash commit -- not updating HEAD
Automatic merge went well; stopped before committing as requested
Toutes les modifications de votre projet Rack sont fusionnées et prêtes à être validées localement.
Vous pouvez également faire le contraire, faire des modifications dans le sous-répertoire rack
de votre branche principale et les fusionner plus tard dans votre branche rack_branch
pour les envoyer aux mainteneurs du projet Rack ou les pousser dans le dépôt principal.
Pour voir les différences entre ce que vous avez dans le sous-répertoire rack
et le code de la branche rack_branch
(pour savoir si vous devez les fusionner), vous ne pouvez pas utiliser la commande diff
habituelle.
Vous devez plutôt exécuter git diff-tree
en renseignant la branche avec laquelle vous voulez comparer :
$ git diff-tree -p rack_branch
Ou, pour comparer ce qu'il y a dans votre répertoire rack
avec ce qu'il y avait sur le serveur la dernière fois que vous avez vérifié, vous pouvez exécuter :
$ git diff-tree -p rack_remote/master