Pour ne pas retrouver ses commits de travail dans votre branche de production, il est essentiel de squasher ces commits avant de fusionner votre branche (cf l’image d’illustration! 😉 ). Si vous ne savez pas ce que ce terme signifie ou comment l’appliquer, cet article est fait pour vous! On verra dans un premier temps ce que “squasher des commits” signifie, pourquoi c’est important et enfin on verra comment le faire avec des exemples concrets 🙂
Sommaire
I. Définition
Squasher des commits signifie regrouper plusieurs commits en un seul. Si vous avez 3 commits A – B – C et que vous les regroupez B et C dans le commit A, vous avez “Squashé vos commits”.
II. Quelle utilité?
On va prendre un exemple très simple: imaginons que j’ai une issue à réaliser dont l’objectif est d’ajouter le nom et l’URL du site dans le README d’un projet quelconque.
Voici une situation qui pourrait arriver si je suis très étourdis:
Je me dis: “c’est super simple je vais faire vite” et j’ajoute le nom du site dans le README en oubliant de mettre l’URL.
Je m’en aperçois et je la rajoute (je rajoute donc un commit).
Toujours très étourdis, je fais une faute à l’URL!
Encore une fois je m’en aperçois et je la corrige dans un autre commit.
Enfin, Vinvin relit ma merge Request et me fait remarquer qu’il faudrait un “é” à “Apero”.
Je prends en compte son retour et le modifie dans un commit, ça y est je peux fusionner ma branche dans Master!
Voici les commits générés:
Voici les diffs:
Ici, si je fusionne, je vais me retrouver avec 4 commits supplémentaires (en plus de l’éventuel commit de fusion) dans Master.
Quelle est la plus-value d’avoir les commits “Ajout accent ..”,”Ajout URL..” etc.. dans l’historique? Cela va “polluer” l’historique en ajoutant des commits inutiles.
Voici l’historique des commits de ma branche Master après fusion en l’état:
Un seul commit appelé “Ajout du nom du site et de son URL dans le README” serait suffisant!
Est-ce que c’est si grave que ça ?
Il y a d’abord l’impact évident: lorsque je regarde ma branche principale (master ou main par exemple), si personne ne squash ses commits, on se retrouve avec énormément de commits peu parlants. Il est donc difficile de savoir ce qui a été effectué, et ce qui aurait pu causer d’éventuelles régressions par exemple.
Ensuite, je vais vous parler très concrètement du désavantage que j’y vois de mon point de vu de développeur: quand je veux savoir pourquoi une ligne de code a été rajoutée, il m’arrive de vouloir voir le contenu du commit dans lequel cette ligne a été introduite pour comprendre son rôle et pourquoi elle est présente.
Lorsque la ligne de code qui me pose question a été introduite dans un énorme commit peu parlant, je vais mettre du temps pour comprendre pourquoi elle a été introduite et dans quel contexte.
Au contraire, s’il y a plein de commits (ce qui arrive souvent lorsqu’on ne squash pas ses commits), je vais devoir regarder plein de commits du type “fix typo”,”prise en compte retour MR 1”, “prise en compte retour MR 2” et je vais devoir inspecter le contenu de tous ces commits pour comprendre ce contexte.
Si on reprend l’exemple évoqué précédemment on a:
Cette perte de temps pourrait être évitée si les commits étaient clairs, c’est-à-dire qu’ils forment un groupe de sens de modification avec un message clair et c’est exactement ce que permet de faire le squash de commits.
III. Comment Squasher ses commits?
Mise en garde: avant de squasher, assurez vous bien que personne d’autre ne travaille sur votre branche, sinon il y aura des divergences d’historique qui vont être embêtantes!
Très concrètement, une personne qui a pull la branche avant le push d’un squash ne pourra pas pull ni push simplement son travail s’il continue de développer sur cette branche.
C’est pour cette raison que je vous conseille de le faire juste avant de merge votre branche 🙂
Je vous conseille de lire la façon de faire manuellement même si vous ne souhaitez pas l’utiliser à l’avenir car elle vous permettra de comprendre comment un Squash fonctionne 😉
1. Squasher manuellement en ligne de commande
On reprend l’exemple précédent, on a les commits suivants (sortie de la commande git log):
Ici, on va regrouper les 4 commits qu’on a généré dans un seul commit: le premier. On renommera par la même occasion ce commit pour qu’il soit plus parlant.
Pour cela, on va faire un rebase interactif à partir du commit qui précède le commit qu’on veut garder. Ici c’est le commit initial avec le hash 40c7b53b0c2046d0d79c605050f5b12cc4cda4ef.
On va donc exécuter la commande suivante:
git rebase -i 40c7b53b0c2046d0d79c605050f5b12cc4cda4ef
Un éditeur s’ouvre alors:
Le commit que je veux garder est le premier (le d45cf43) je laisse donc pick.
Je ne veux pas garder les autres, je veux qu’ils soient fusionnés dans le commit que je veux garder. Je vais donc remplacer “pick” par “squash” (à noter qu’on pourrait mettre simplement “s”).
J’enregistre ensuite mes modifications, un autre fichier s’ouvre:
Si on enregistrait à ce stade, on aurait plus qu’un seul commit qui aurait pour message la concaténation de tous les messages des commits regroupés.
Ici, on va simplement les supprimer et écrire un message plus parlant.
On sauvegarde et c’est fini 🙂
Si on fait un git log, on voit bien un seul commit:
Il faut ensuite faire un push -f car on souhaite réécrire l’historique de la branche distante.
Après acceptation de la merge request on a:
Ici, on a un historique clair: un seul commit parlant est généré (en plus du commit de fusion).
Cochez simplement l’option “Squash commits” dans votre Merge Request
Par défaut Gitlab Squash tous les commits de la branche en “Resolve “nom de l’issue associée””. Ce qui donne dans notre cas:
Je n’ai pas fait d’exemple sous Github mais le fonctionnement devrait être très proche.
Ici, on n’a pas le choix du nommage de notre commit et on a forcément un commit par issue ce qui n’est pas forcément top. En effet, parfois on aimerait avoir plusieurs commits par issue pour plus de clarté.
En revanche, si les issues sont assez petites ça peut faire l’affaire. Le gros point fort ici est sa facilité d’utilisation.
Bonus
Lors de mes premiers squash j’appréhendais énormément de le faire (notamment à cause du git push -f qui ne pardonne pas en cas d’erreur). Si c’est votre cas aussi, n’hésitez pas à créer une branche locale temporaire à partir de votre branche locale à squash! Cela vous permettra d’avoir un backup en cas de problème et vous n’aurez qu’à supprimer cette branche une fois la merge request acceptée 😉
Ce que je faisais les premières fois:
git checkout ma_branche_a_squash
git branch ma_branch_backup
// je fais mon squash
// la MR est validée
git branch -d ma_branch_backup
Conclusion
Il est indispensable à mes yeux de garder un historique Git propre, c’est pourquoi j’espère qu’à travers cet article je vous aurais convaincu de Squasher vos commits et que vous avez à présent toutes les connaissances nécessaires pour le faire 🙂
Super article, merci pour ces infos très utiles 😉
Petite question : je n’ai pas fais ces manips et ma branche main est pleine de petits commits inutiles du genre. Est-il possible de réunir tous les commits sur la main branch dans le même esprit afin d’avoir un seul commit donc plus clair ?
Merci d’avance 🙂
Oui, tu peux tout à fait faire cette manipulation sur ta branche main à condition qu’elle ne soit pas protégée. En effet, pour réécrire l’historique il va falloir faire un push -f vers la branche distante et cette opération est (on l’espère) désactivée par défaut sur la branche principale de ton projet.
Donc si tu veux squasher des commits sur ta branche principale je te propose de faire l’opération suivante pour être sécure:
– vérifie qu’il n’y ait plus de branches de travail qui ne seraient pas fusionnées dans main (sinon ça va faire des chocapics à la merge request 😉 )
– place toi sur ta branche main et crée une branche temporaire au cas où ça se passe mal(git checkout main && git pull && git branch tmp_branch)
– enlève la protection de la branche via l’interface graphique de github, gitlab ou autre
– squash tes commits
– git push -f pour réécrire l’historique de ta branche distante
– réactive la protection de ta branche main
– si tout est ok à ce stade la, tu peux détruire ta branche temporaire et tu auras une branche main avec un historique impec’ 😉
On va voir comment avoir en quelques minutes des assertions qui vont vérifier les endpoints de notre API avec des scénarios de ce genre:
Feature: Create a new account
As a visitor,
I can create an account to access the game
Scenario: A visitor creates an account
When I fill the login form with
| email | password |
| [email protected] | Jh0...
Depuis .NET 9, le le support d’OpenAPI est directement inclus dans .NET et ne passe plus par les librairies Swagger par défaut (plus d’info sur ce choix ici si jamais ça vous intéresse).
De façons simplifiée, la librairie Swashbuckle.AspNetCore.Sw...
Description du problème
Par défaut, il n’est pas autorisé de faire des requêtes entre une application qui est dans un domaine A vers une autre qui serait dans un domaine B (pour des raisons de sécurité, il y a plus de détails dans les sources).
S...
On va créer pas-à-pas un raccourci qui nous permettra de générer un IBAN et de l’insérer à l’endroit où se trouve notre curseur. Si vous n’avez pas fait le setup pour développer un plugin Jetbrains, je vous invite à lire notre article sur le sujet.
...
Super article, merci pour ces infos très utiles 😉
Petite question : je n’ai pas fais ces manips et ma branche main est pleine de petits commits inutiles du genre. Est-il possible de réunir tous les commits sur la main branch dans le même esprit afin d’avoir un seul commit donc plus clair ?
Merci d’avance 🙂
Merci pour ton retour Alex62 🙂
Oui, tu peux tout à fait faire cette manipulation sur ta branche main à condition qu’elle ne soit pas protégée. En effet, pour réécrire l’historique il va falloir faire un push -f vers la branche distante et cette opération est (on l’espère) désactivée par défaut sur la branche principale de ton projet.
Donc si tu veux squasher des commits sur ta branche principale je te propose de faire l’opération suivante pour être sécure:
– vérifie qu’il n’y ait plus de branches de travail qui ne seraient pas fusionnées dans main (sinon ça va faire des chocapics à la merge request 😉 )
– place toi sur ta branche main et crée une branche temporaire au cas où ça se passe mal(git checkout main && git pull && git branch tmp_branch)
– enlève la protection de la branche via l’interface graphique de github, gitlab ou autre
– squash tes commits
– git push -f pour réécrire l’historique de ta branche distante
– réactive la protection de ta branche main
– si tout est ok à ce stade la, tu peux détruire ta branche temporaire et tu auras une branche main avec un historique impec’ 😉
Après plus de 15 ans de dév, je découvre cette commande Git… Merci Maxime, c’est très clair !
Avec plaisir, merci pour ton commentaire Jean-Luc ! 🙂
Merci !
franchement, détaillé comme il faut. vous expliquez bien. merci énormément
Bien détaillé. Merci énormément !
Exactement ce que je cherchais, clairement expliqué. Merci.
bravo merci