Comment “squasher” efficacement ses commits avec Git

Actualité ekino -

ou mon retour d'expérience après une première Pull Request sur Sonata

Article paru le :
Github
Article paru le :

Lors de ma première Pull Request pour Sonata, j’ai rapidement eu une remarque : “squash your commits”. Ne maitrisant pas le sujet, j’ai fait des recherches, et ai trouvé beaucoup d’informations. Mais il manquait toujours un bout et notamment le cas d’utilisation auquel j’était confronté : le squash après un merge. Du coup, plutôt que de garder cela pour moi je le partage avec vous dans cet article.

Avant tout, qu’est-ce qu’une Pull Request (PR) ? Une PR est une méthode utilisée pour contribuer à un projet en proposant les modifications que le développeur souhaite apporter sur le dépôt principal. C’est le moyen de contribution le plus utilisé avec les outils de gestion de versions décentralisé (tel que Git) et le plus répendu grâce à GitHub et aux projets Open Source. En effet, dans le monde Open Source, les PR sont très fréquemment utilisées lorsqu’un développeur souhaite contribuer à un projet, que ce soit pour l’améliorer et/ou aider dans son développement. Par exemple, certains ajouteront de la documentation, de la traduction, des tests, tandis que d’autres proposeront des optimisations de code ou l’ajouts de fonctionnalités.

L’objectif principal d’une PR est de discuter autour des modifications proposées avec les personnes responsables du dépôt d’origine pour être certain que la qualité de leur travail soit conservée. On soumet ses modifications à l’avis de tous et les responsables du dépôt peuvent accepter ces modifications en les mergeant et fermer la PR.

 

Note – Il est important de bien comprendre que les PR sont des méthodes de travail, il ne s’agit pas d’une fonctionnalité du système de versionning.

 

Un “squash” est un regroupement de plusieurs commits. Le but est de les fusionner en un seul pour avoir un historique Git plus propre. Il est notamment conseillé de le faire dans le cadre des Pull Request pour simplifier la relecture des modifications effectuées.

Concrètement, lorsqu’on travaille sur une PR, on fait souvent beaucoup de commits, que ce soit pour développer la fonctionnalité, ou pour corriger les retours que l’on nous fait, on se retrouve facilement avec une dizaine de commits. L’intérêt principal du squash de commits est de conserver un historique propre malgré les nombreux commits qui ont été faits auparavant. On va ainsi pouvoir les regrouper tous en un seul commit contenant la totalité des modifications. Notre PR sera ainsi plus simple et plus claire.

 

Cas simple

Pour le cas simple, on souhaite squasher nos commits qui se suivent dans l’historique, sur la même branche.
Prenons un exemple concret, voici notre dépôt. git (on visualise le repo à l’aide de tig) :

 

tig du repo actuel

Comme vous pouvez le constater, je suis sur ma branche “master”, sur laquelle j’ai effectué quatre commits pour modifier le même fichier ‘Readme.md’.

 

Tout d’abord, il faut récupérer le sha1 du commit précédant celui dans lequel on va squasher, puis on l’utilise pour lancer la commande :

On entre alors en mode interactif (on utilise l’éditeur de git). Les commits sont affichés du plus ancien au plus récent.

Le premier commit correspond à celui de base, celui que l’on souhaite conserver ; on laisse donc l’instruction “pick” devant. On souhaite squasher tous les commits suivants, on met donc l’instruction “squash” devant. (vous pouvez aussi utiliser l’alias “s” à la place de “squash”)

On obtient alors :

Concrètement, on dit à GIT de se baser sur le premier commit et on lui applique tous les suivants pour n’en faire qu’un seul.

Lorsque l’on valide le squash (on quitte le mode intéractif), Git vas ré-appliquer les commits dans le même ordre qu’ils ont été configuré juste avant. On met alors un message de commit qui concerne le regroupement de nos commits et c’est tout.

 

success git rebase

 

On peut ensuite revérifier toujours à l’aide de la commande tig que nos commits ont bien été squashés. Il ne vous reste plus qu’a pousser vos modifications :

 

Note – Pensez à bien utiliser l’option “–force” car le rebase doit écraser l’ancien historique des commits.
Attention, l’option —force ne doit être utilisée que sur votre propre fork !

 

Cas complexe

Pour le cas complexe, on a mergé une autre branche entre nos commits à squasher.

Prenons un second exemple concret, voici notre dépôt git :

 

repo git cas complexe

Comme vous pouvez le constater, je suis sur ma branche “develop”, sur laquelle j’ai effectué cinq commits. Puis j’ai récupéré master, et enfin j’ai fait un dernier commit.

 

Tout d’abord, il faut déterminer le sha1 du commit à la base de notre branche. Pour cela, on utilise un git merge-base :

 

git merge-base

 

On effectue ensuite notre squash :

Vous remarquerez ici qu’on ne voit pas les commits du merge de master apparaître, grâce au sha1 que l’on a récupéré précédemment ; on travaille uniquement sur les commits de notre branche.

 

Note – Cette méthode d’identification du commit peut fonctionner aussi dans les cas “simples”.

 

Comme vu précédemment, on modifie les instructions devant chaque commit grâce au mode interactif.

De la même manière que pour le cas simple, on indique le commit à ‘pick’ (c’est-à-dire à utiliser comme référence) et ceux à squasher, puis on met le message de commit à celui que l’on conserve.

 

success git rebase

 

On peut ensuite revérifier, à l’aide de la commande tig, que nos commits ont bien été squashés et que le merge de la branche master a bien été conservé. Il ne vous reste plus qu’à pusher vos modifications, en “–force”, comme vu précédemment.

 

Astuce – Si vous souhaitez faire un rebase depuis le tout premier commit de votre projet, vous devez utiliser l’option --root

 

Le squash de commit rend vos PR beaucoup plus propres et plus lisibles pour les autres, il simplifie aussi le revert en cas de problème. N’hésitez pas à squasher vos PR autant que possible lorsque vous travaillez sur vos projets.
Photos par Nat Welch

Sources :

Laisser un commentaire