« Committez tôt, committez souvent » est un mantra populaire en développement logiciel quand on utilise Git. Cela garantit que chaque modification est bien documentée, améliore la collaboration et facilite le suivi de l’évolution du projet. Cependant, cela peut également conduire à un trop grand nombre de commits.
C’est ici que l’importance de réunir les commits entre en jeu. Réunir les commits consiste à combiner plusieurs entrées de commit dans un seul et unique commit cohérent.
Par exemple, supposons que nous travaillions sur une fonctionnalité implémentant un formulaire de connexion, et que nous créons les quatre commits suivants :
Une fois la fonctionnalité complète, pour le projet global, ces commits sont trop détaillés. Nous n’avons pas besoin de savoir dans l’avenir que nous avons rencontré une erreur qui a été corrigée pendant le développement. Afin d’assurer une historique propre sur le branche principale, nous fusionnons ces commits en un seul commit :
Comment fusionner des commits dans Git : le rebase interactif
La méthode la plus commune pour fusionner des commits est l’utilisation d’un rebase interactif. Nous l’initions en utilisant la commande :
git rebase -i HEAD~<number_of_commits>
Remplacez <number_of_commits>
par le nombre de commits que vous voulez fusionner.
Dans notre cas, nous avons quatre commits, donc la commande est :
git rebase -i HEAD~4
Exécution de cette commande ouvrira un éditeur de ligne de commande interactif :
La section supérieure affiche les commits, tandis que la section inférieure contient des commentaires sur la manière de fusionner les commits.
Nous voyons quatre commits. Pour chacun, nous devons décider quelle commande exécuter. Nous nous préoccupons de la commande pick
(p
) et de la commande squash
(s
). Pour fusionner ces quatre commits en un seul commit, nous pouvons sélectionner le premier et fusionner les trois restants.
Nous appliquons les commandes en modifiant le texte précédant chaque commit, en changeant spécifiquement pick
en s
ou squash
pour les deuxième, troisième et quatrième commits. Pour faire ces modifications, nous devons entrer le mode “INSERT” dans l’éditeur de texte en ligne de commandes en appuyant sur la touche i
du clavier :
Après avoir appuyé sur i
, le texte -- INSERT --
apparaîtra en bas, indiquant que nous avons entré le mode d’insertion. Maintenant, nous pouvons déplacer le curseur avec les touches fléchées, supprimer des caractères et tapez comme dans un éditeur de texte standard :
Une fois que nous serons satisfait des modifications, nous devons quitter le mode d’insertion en appuyant sur la touche Esc
du clavier. La prochaine étape consiste à sauvegarder nos modifications et à quitter l’éditeur. Pour ce faire, nous devons d’abord appuyer sur la :
pour signifier à l’éditeur que nous avons l’intention d’exécuter une commande :
Au bas de l’éditeur, nous voyons maintenant une virgule :
nous invitant à insérer une commande. Pour sauvegarder les modifications, nous utilisons la commande w
, qui signifie « écrire ». Pour fermer l’éditeur, utilisez q
, qui signifie « quitter ». Ces commandes peuvent être combinées et tapées ensemble wq
:
Pour exécuter la commande, appuyez sur la Enter
. Cette action ferme l’éditeur actuel et ouvre un nouvel éditeur, vous permettant d’entrer le message de commit pour le nouveau commit fusionné. L’éditeur affichera un message par défaut composé des messages des quatre commits que vous fusionnez :
Je recommande de modifier le message pour refléter avec précision les changements mis en œuvre par ces commits combinés — après tout, l’objectif de la fusion est de maintenir une histoire propre et facilement lisible.
Pour interagir avec l’éditeur et modifier le message, nous appuyons sur i
à nouveau pour entrer en mode édition et modifier le message à notre goût.
Dans ce cas, nous remplaçons le message de commit par « Implémenter le formulaire de connexion. » Pour quitter le mode édition, nous appuyons sur Esc
. Ensuite, nous enregistrons les modifications en appuyant sur :
, en entrant le commande wq
, et en appuyant sur Enter
.
Comment afficher l’historique des commits
En général, récupérer l’historique complet des commits peut être difficile. Pour afficher l’historique des commits, nous pouvons utiliser la commande git log
. Dans l’exemple mentionné, avant de réaliser la fusion, l’exécution de la commande git log
afficherait :
Pour naviguer dans la liste des commits, utilisez les touches fléchées haut et bas. Pour quitter, appuyez sur q
.
Nous pouvons utiliser git log
pour confirmer le succès du squash. L’exécution de cette commande après le squash affichera un seul commit avec le nouveau message :
Envoi de commit groupé
La commande ci-dessus agira sur le dépôt local. Pour mettre à jour le dépôt distant, nous devons envoyer nos modifications. Cependant, comme nous avons modifié l’historique des commits, nous devons effectuer un push forcé en utilisant l’option --force
:
git push --force origin feature/login-form
Un push forcé écrasera l’historique des commits sur la branche distante et pourrait perturber les autres membres de l’équipe travaillant sur cette branche. Il est recommandé de communiquer avec l’équipe avant de procéder ainsi.
Une méthode plus sécurisée pour effectuer un push forcé, qui réduit le risque de perturber les collaborateurs, consiste à utiliser à la place l’option --force-with-lease
:
git push --force-with-lease origin feature/login-form
Cette option nous garantit que nous ne faisons un push forcé que si le branche distante n’a pas été mise à jour depuis notre dernière extraction ou pull.
Compresser des commits spécifiques
Imaginons que nous avons cinq commits :
Supposons que nous voulons conserver les commits 1, 2 et 5 et compresser les commits 3 et 4.
Lors de l’utilisation du rebase interactif, les commits marqués pour être compressés seront combinés avec le commit immédiatement précédent. Dans ce cas, cela signifie que nous voulons compresser Commit4
de sorte qu’il fusionne avec Commit3
.
Pour ce faire, nous devons initier un rebase interactif qui inclut ces deux commits. Dans ce cas, trois commits suffisent, donc nous utilisons la commande :
git rebase -i HEAD~3
Ensuite, nous définissons Commit4
comme s
afin qu’il soit fusionné avec Commit3
:
Après l’exécution de cette commande et l’affichage des commits, nous constatons que les commits 3 et 4 ont été fusionnés ensemble tandis que les autres restent inchangés.
Fusionner à partir d’un commit spécifique
Dans la commande git rebase -i HEAD~3
, la partie HEAD
est un raccourci pour le commit le plus récent. La syntaxe ~3
est utilisée pour spécifier un ancêtre de commit. Par exemple, HEAD~1
renvoie au parent du commit HEAD
.
Dans un rebase interactif, les commits pris en compte comprennent tous les commits ancêtres menant jusqu’au commit spécifié dans la commande. Notez que le commit spécifié n’est pas inclus :
A la place d’utiliser HEAD, nous pouvons spécifier directement un hash de commit. Par exemple, Commit2
possède un hash de dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
, donc la commande :
git rebase -i dbf3cc118d6d7c08ef9c4a326b26dbb1e3fe9ddf
démarrera un rebase en considérant tous les commits effectués après Commit2
. Par conséquent, si nous voulons démarrer un rebase à un commit spécifique et inclure ce commit, nous pouvons utiliser la commande :
git rebase -i <commit-hash>~1
Résolution des conflits lors du fusionnement de commits
Lorsque nous fusionnons des commits, nous combinons les modifications de plusieurs commits dans un seul commit, ce qui peut entraîner des conflits si les modifications se chevauchent ou divergent significativement. Voici quelques scénarios courants où des conflits peuvent surgir :
- Changements en chevauchement: Si deux ou plusieurs commits à fusionner ont modifié les mêmes lignes dans un fichier ou des lignes fortement liées, Git peut ne pas être en mesure de réconcilier automatiquement ces modifications.
- État de modification différent: Si un commit ajoute une certaine partie du code et un autre commit modifie ou supprime ce même morceau de code, la fusion de ces commits peut entraîner des conflits qui doivent être résolus.
- Renommage et modification: Si un commit renomme un fichier et que des commits ultérieurs font des modifications à l’ancien nom, la fusion de ces commits peut perturber Git, provoquant un conflit.
- Changements dans les fichiers binaires : Les outils de comparaison de diff en mode texte ne conviennent pas bien pour fusionner les fichiers binaires. Si plusieurs commits modifient le même fichier binaire et que nous essayons de les fusionner, un conflit peut survenir car Git ne peut pas automatiquement réconcilier ces modifications.
- Histoire complexe : Si les commits ont une histoire complexe avec plusieurs fusionnes, des branches ou des réinitialisations intermédiaires, la fusion de ces derniers peut entraîner des conflits en raison de la nature non linéaire des modifications.
Lors de la fusion, Git tentera d’appliquer chaque modification une par une. Si il rencontre des conflits au cours du processus, il s’arrêtera et nous permettra de les résoudre.
Les conflits seront marqués avec des marqueurs de conflit <<<<<<
et >>>>>>
. Pour gérer les conflits, nous devons ouvrir les fichiers et résoudre chacun manuellement en sélectionnant la partie de code que nous souhaitons garder.
Après avoir résolu les conflits, nous devons mettre en cache les fichiers résolus à l’aide de la commande git add
. Ensuite, nous pouvons continuer la référencement à l’aide de la commande suivante :
git rebase --continue
Pour en savoir plus sur les conflits Git, consultez ce didacticiel sur Comment résoudre les conflits de fusion dans Git.
Alternatives au référencement avec Squash
La commande git merge --squash
est une méthode alternative à git rebase -i
pour combiner plusieurs commits en un seul. Cette commande est particulièrement utile lorsque nous voulons fusionner les modifications d’une branche principale en squattant tous les commits individuels en un seul. Voici un aperçu de la manière de squatter en utilisant git merge
:
- Nous naviguons vers la branche cible dans laquelle nous voulons intégrer les modifications.
- Nous exécutons la commande
git merge --squash <nom-de-branche>
en remplaçant<nom-de-branche>
par le nom de la branche. - Nous validons les modifications avec
git commit
pour créer un seul commit qui représente toutes les modifications de la branche fonctionnelle.
Par exemple, disons que nous souhaitons intégrer les modifications de la branche feature/login-form
dans main
en tant que commit unique :
git checkout main git merge --squash feature-branch git commit -m "Implement login form"
Ceci sont les limitations de cette approche par rapport à git rebase -i
:
- Granularité de contrôle : Moins de contrôle sur les commits individuels. Avec le rebase, nous pouvons choisir lesquels des commits fusionner, tandis que la fusion force la combinaison de toutes les modifications dans un seul commit.
- Histoire intermédiaire : Lors de l’utilisation de merge, l’historique des commits individuels de la branche de fonctionnalité est perdu dans la branche principale. Cela peut rendre plus difficile le suivi des modifications incrémentales apportées pendant le développement de la fonctionnalité.
- Revue pré-commit : Puisque cela met en place toutes les modifications comme un seul ensemble de modifications, nous ne pouvons pas passer en revue ou tester chaque commit individuellement avant de les fusionner, contrairement à une rébase interactive où chaque commit peut être passé en revue et testé dans un ordre séquentiel.
Conclusion
Intégrer des commits fréquents et de petite taille dans le flux de travail de développement favorise la collaboration et une documentation claire, mais cela peut également encombrer l’historique du projet. Fusionner les commits établit un équilibre, en préservant les jalons importants tout en éliminant le bruit des petites modifications itératives.
Pour en savoir plus sur Git, je recommande ces ressources :
Source:
https://www.datacamp.com/tutorial/git-squash-commits