Réécrire l'historique
git commit --amend et autres méthodes de réécriture de l'historique
Introduction
Ce tutoriel aborde diverses méthodes de réécriture et de modification de l'historique Git. Git utilise plusieurs méthodes différentes pour enregistrer les changements. Nous aborderons les atouts et les faiblesses de chacune d'elles, et nous expliquerons comment les utiliser. Ce tutoriel développe certaines des raisons les plus courantes pour lesquelles les instantanés commités sont écrasés et vous explique comment éviter les pièges associés à cette opération.
La principale tâche de Git est de s'assurer que vous ne perdiez jamais un changement commité. Git est également conçu pour vous donner le contrôle total de votre workflow de développement. Il vous permet notamment de définir avec exactitude l'apparence de votre historique de projet, mais il crée également des conditions pouvant entraîner la perte de commits. Git fournit ses commandes de réécriture de l'historique en se dégageant de toute responsabilité en cas de perte de contenu découlant de leur utilisation.
Git comprend plusieurs mécanismes de stockage de l'historique et d'enregistrement des changements, parmi lesquels : commit --amend
, git rebase
et git reflog
. Ces options permettent de personnaliser le workflow. À l'issue de ce tutoriel, vous maîtriserez des commandes qui vous permettront de restructurer vos commits Git et vous serez en mesure d'éviter les pièges courants lors de la réécriture de l'historique.
Ressource connexe
Fiche de révision sur Git
DÉCOUVRIR LA SOLUTION
Découvrir Git avec Bitbucket Cloud
Modification du dernier commit : git commit --amend
La commande git commit --amend
permet de modifier facilement le commit le plus récent. Elle vous permet de combiner les changements stagés avec l'ancien commit au lieu de créer un commit totalement nouveau. Elle peut également être utilisée pour modifier le message de commit sans changer son instantané. Cependant, la modification ne se contente pas de changer le dernier commit. Elle le remplace totalement, ce qui signifie que le commit modifié constitue une toute nouvelle entité qui dispose de sa propre réf. Pour Git, elle ressemblera à un tout nouveau commit, marqué par un astérisque (*) dans le schéma ci-dessous. Voici quelques cas d'usage courants de git commit --amend
. Nous aborderons les cas d'usage dans les sections suivantes.
Modifier le dernier message de commit Git
git commit --amend
Imaginons que vous venez de faire un commit et que vous avez fait une erreur dans votre message. L'exécution de cette commande lorsqu'aucun élément n'est stagé vous permet de modifier le message du commit précédent sans modifier son instantané.
Des commits prématurés apparaissent en permanence au cours de votre développement quotidien. On peut facilement oublier de stager un fichier ou formater le message de commit de manière incorrecte. Le flag --amend
est un moyen pratique de corriger ces erreurs mineures.
git commit --amend -m "an updated commit message"
En ajoutant l'option -m
, vous pouvez transmettre un nouveau message à partir de la ligne de commande sans être invité à ouvrir un éditeur.
Modifier des fichiers commités
L'exemple suivant illustre un scénario Git commun. Imaginons que nous avons édité des fichiers que nous souhaitons commiter dans un instantané unique, mais que nous avons oublié d'ajouter l'un des fichiers lors de la première tentative. Pour réparer l'erreur, il suffit de stager l'autre fichier et de faire un commit avec le flag --amend
:
# Edit hello.py and main.py
git add hello.py
git commit
# Realize you forgot to add the changes from main.py
git add main.py
git commit --amend --no-edit
Le flag --no-edit
vous permet de modifier votre commit sans changer son message. Le commit obtenu remplacera le commit incomplet. Il sera structuré comme si nous avions commité des changements apportés à hello.py
et main.py
dans un instantané unique.
Ne pas modifier les commits publics
Les commits modifiés sont en réalité des commits entièrement nouveaux. Les anciens commits ne se trouveront plus dans votre branche courante. Les conséquences associées sont les mêmes que pour la réinitialisation d'un instantané public. Évitez de modifier un commit sur lequel repose le travail d'autres développeurs. Cette situation est déroutante, et il est difficile de revenir en arrière.
Récapitulatif
Pour résumer, git commit --amend
vous permet de sélectionner le dernier commit afin d'y ajouter de nouveaux changements stagés. Vous pouvez ajouter ou supprimer des changements à partir de la zone de staging afin de les appliquer avec commit --amend
. Si aucun changement n'est stagé, --amend
vous invite tout de même à modifier le dernier message de log du commit. Utilisez --amend
avec précaution dans les commits partagés avec d'autres membres de l'équipe. Lorsque vous modifiez un commit partagé avec un autre utilisateur, vous devrez peut-être résoudre des conflits de merge, une opération chronophage.
Modifier des commits plus anciens ou plusieurs commits
Pour modifier des commits plus anciens ou plusieurs commits, vous pouvez utiliser git rebase
afin de combiner une séquence de commits dans un nouveau commit de base. Dans le mode standard, git rebase
vous permet de réécrire l'historique, c'est-à-dire d'appliquer automatiquement des commits de votre branche de travail courante à la branche head transmise. Puisque vos nouveaux commits vont remplacer les anciens, il est important de ne pas utiliser git rebase
sur des commits publics. Sinon, votre historique de projet disparaîtra.
Lorsqu'il est important de conserver un historique de projet propre, vous pouvez ajouter l'option -i
à la commande git rebase
pour exécuter un rebase interactif
. Vous avez ainsi la possibilité de modifier des commits individuels du processus plutôt que de déplacer tous les commits. Pour de plus amples informations sur le rebase interactif et des commandes rebase supplémentaires, reportez-vous à la page git rebase.
Modifier des fichiers commités
Durant un rebase, la commande edit ou e
interrompt la lecture du rebase à ce commit, ce qui vous permet d'apporter des changements supplémentaires avec git commit --amend
. Git interrompt la lecture et affiche le message suivant :
Stopped at 5d025d1... formatting
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Messages multiples
Chaque commit Git normal comprend un message de log qui explique ce qu'il s'est passé dans le commit. Ces messages fournissent de précieux renseignements qui sont ajoutés à l'historique de projet. Durant un rebase, vous pouvez exécuter quelques commandes dans des commits pour modifier leurs messages.
Squasher des commits pour nettoyer l'historique
La commande squash ou s
montre la véritable utilité du rebase. Squash vous permet de spécifier les commits à merger dans les commits précédents. C'est ainsi que vous obtenez un « historique propre ». Durant la lecture du rebase, Git exécute la commande rebase spécifiée pour chaque commit. Dans le cas du squash de commits, Git ouvre l'éditeur de texte que vous avez configuré et vous invite à combiner les messages de commit spécifiés. Le processus global peut être illustré comme suit :
Notez que les commits modifiés à l'aide d'une commande rebase possèdent un ID différent des commits d'origine. Les commits marqués d'une coche recevront un nouvel ID si les commits précédents ont été réécrits.
Les solutions d'hébergement Git modernes telles que Bitbucket proposent désormais des fonctionnalités de « squash automatique » lors du merge. Ces fonctionnalités effectuent automatiquement un rebase et un squash des commits d'une branche lorsque vous utilisez l'interface utilisateur des solutions d'hébergement. Pour de plus amples informations, reportez-vous à l'article « Squasher des commits lors d'un merge de branche Git avec Bitbucket »
Récapitulatif
git rebase vous donne la possibilité de modifier votre historique, alors que le rebase interactif vous permet de le faire sans laisser de traces. Vous avez ainsi la liberté de faire des erreurs, de les corriger et d'affiner votre travail tout en conservant un historique de projet propre et linéaire.
Le filet de sécurité : git reflog
Les logs de référence ou reflogs constituent un mécanisme utilisé par Git pour enregistrer les mises à jour appliquées aux pointes des branches et à d'autres références de commit. Le reflog vous permet de revenir sur les commits même s'ils ne sont pas référencés par une branche ou un tag. Une fois l'historique réécrit, le reflog contient des informations sur l'ancien état des branches et vous permet d'y revenir si nécessaire. À chaque mise à jour de la pointe de votre branche quelle qu'en soit la raison (en changeant les branches, en faisant un pull de nouveaux changements, en réécrivant l'historique ou simplement en ajoutant de nouveaux commits), une nouvelle entrée est ajoutée au reflog. Dans cette section, nous verrons brièvement la commande git reflog
et nous étudierons quelques cas d'usage courants.
Utilisation
git reflog
Cette commande affiche le reflog pour le dépôt local.
git reflog --relative-date
Cette commande affiche le reflog avec les informations de date relative (par ex., deux semaines plus tôt).
Exemple
Pour comprendre git reflog
, nous allons étudier un exemple concret.
0a2e358 HEAD@{0}: reset: moving to HEAD~2
0254ea7 HEAD@{1}: checkout: moving from 2.2 to main
c10f740 HEAD@{2}: checkout: moving from main to 2.2
Le reflog ci-dessus montre un check-out de la branche principale sur la branche 2.2 et l'opération inverse. À partir de là, nous faisons un hard reset d'un commit plus ancien. La dernière activité est représentée dans la partie supérieure, nommée HEAD@{0}
.
Si vous êtes revenu en arrière sans le vouloir, le reflog contiendra un commit principal qui pointait vers (0254ea7)
avant que vous ne déplaciez accidentellement les deux commits.
git reset --hard 0254ea7
Grâce à git reset, il est possible de changer la branche principale et de rétablir le commit à son état précédent. C'est votre filet de sécurité en cas de modification accidentelle de votre historique.
Attention : le reflog ne constitue un filet de sécurité que si les changements ont été commités dans votre dépôt local et qu'il suit seulement les déplacements de la pointe de la branche du dépôt. De plus, les entrées reflog ont une date d'expiration. Par défaut, les entrées reflog expirent au bout de 90 jours.
Pour de plus amples informations, reportez-vous à notre page git reflog.
Résumé
Dans cet article, nous avons vu plusieurs méthodes pour modifier l'historique et annuler des changements dans Git. Nous avons étudié brièvement le processus git rebase. Voici les enseignements clés de ce module :
- Vous disposez de plusieurs méthodes pour réécrire l'historique avec Git.
- Utilisez
git commit --amend
pour modifier votre dernier message de log. - Utilisez
git commit --amend
pour apporter des changements au dernier commit. - Utilisez
git rebase
pour combiner des commits et modifier l'historique d'une branche. - À l'inverse d'une commande git rebase standard,
git rebase -i
vous offre un contrôle plus fin sur les changements apportés à l'historique.
Pour en savoir plus sur les commandes abordées, reportez-vous aux pages correspondantes :
Partager cet article
Thème suivant
Lectures recommandées
Ajoutez ces ressources à vos favoris pour en savoir plus sur les types d'équipes DevOps, ou pour les mises à jour continues de DevOps chez Atlassian.