Édition avancée de l'historique Git : Amendement des commits et rebase interactif

Utilisez git commit --amend et le rebase interactif pour nettoyer l'historique local sans perturber les branches partagées.

Édition avancée de l'historique Git : Amendement des commits et rebase interactif

L'édition avancée de l'historique Git vous aide à nettoyer les commits avant qu'ils ne fassent partie de l'historique partagé. En comprenant l'amendement des commits et le rebase interactif, vous pouvez corriger de petites erreurs, combiner des commits bruyants et présenter une histoire plus claire pour la revue de code.

La règle clé est simple : modifier l'historique local est normal, mais réécrire l'historique partagé nécessite de la prudence. Les commandes sont puissantes car elles changent les identifiants des commits, pas seulement les messages.

Ce que l'édition de l'historique Git change réellement

Chaque commit Git a un identifiant basé sur son contenu, ses métadonnées, le commit parent, l'auteur, la date et le message. Si vous amendez un commit ou le réécrivez lors d'un rebase, Git crée un nouveau commit avec un nouvel identifiant.

C'est acceptable lorsque le commit n'existe que sur votre machine. C'est risqué lorsque d'autres personnes ont déjà récupéré l'ancien commit. Leurs branches peuvent encore pointer vers l'ancien historique, tandis que la vôtre pointe vers l'historique réécrit.

Utilisez l'édition de l'historique pour :

  • Corriger une faute de frappe dans le dernier message de commit.
  • Ajouter un fichier oublié à votre dernier commit local.
  • Fusionner plusieurs commits de travail en cours avant d'ouvrir une pull request.
  • Réorganiser les commits locaux pour faciliter la revue des modifications connexes.
  • Diviser un gros commit local en plusieurs commits logiques plus petits.

Évitez l'édition de l'historique sur :

  • Les branches principales.
  • Les branches de release.
  • Les branches de fonctionnalités partagées utilisées par plusieurs personnes.
  • Toute branche avec des tags de déploiement ou des commits sensibles à l'audit.

Avant de commencer, vérifiez sur quelle branche vous êtes et ce qui a déjà été poussé :

git status
git branch --show-current
git log --oneline --decorate -5

Si vous n'êtes pas sûr que la réécriture soit sûre, créez une branche de sauvegarde :

git branch backup-before-rebase

Cette branche vous donne un point de récupération connu si le rebase tourne mal.

Amender le dernier commit

git commit --amend remplace le commit le plus récent par un nouveau. C'est le moyen le plus rapide de corriger le dernier commit avant que quelqu'un d'autre en dépende.

Pour modifier uniquement le message du commit :

git commit --amend

Git ouvre votre éditeur avec le message actuel. Enregistrez le message corrigé, et Git crée un commit de remplacement.

Pour ajouter un fichier oublié au dernier commit :

git add chemin/vers/fichier
git commit --amend --no-edit

Le drapeau --no-edit conserve le message existant. Cela est utile lorsque le message du commit est déjà correct et que vous avez seulement oublié de stagifier un changement.

Voici un scénario courant. Vous commitez une mise à jour d'un Dockerfile, puis vous remarquez que vous avez oublié le changement correspondant dans .dockerignore :

git add Dockerfile
git commit -m "Améliorer la mise en cache de la construction Docker"
git add .dockerignore
git commit --amend --no-edit

Maintenant, la branche a un seul commit propre au lieu d'un deuxième commit qui dit "fichier oublié." Cela facilite la revue.

Si le commit original a déjà été poussé, la branche distante a toujours l'ancien identifiant de commit. Pousser le commit amendé nécessite une mise à jour forcée :

git push --force-with-lease

Préférez --force-with-lease à --force. Il refuse d'écraser la branche distante si quelqu'un d'autre a poussé de nouveaux travaux depuis votre dernier fetch.

Nettoyer plusieurs commits avec le rebase interactif

Le rebase interactif vous permet de modifier plusieurs commits à la fois. Vous choisissez une plage, puis Git ouvre une liste de tâches où vous pouvez choisir, reformuler, fusionner, réparer, réorganiser ou arrêter sur des commits.

Pour modifier les cinq derniers commits :

git rebase -i HEAD~5

Vous verrez une liste comme celle-ci :

pick a1b2c3d Ajouter script de déploiement
pick b2c3d4e Corriger faute de frappe
pick c3d4e5f Ajouter vérification de santé
pick d4e5f6a Mettre à jour la documentation
pick e5f6a7b Ajuster le délai d'attente

Changez la commande au début de chaque ligne pour contrôler ce qui se passe. Les choix courants sont :

  • pick : conserver le commit tel quel.
  • reword : conserver les modifications mais éditer le message.
  • squash : combiner ce commit dans le précédent et éditer le message combiné.
  • fixup : combiner ce commit dans le précédent et ignorer le message de ce commit.
  • edit : s'arrêter à ce commit pour pouvoir modifier son contenu.
  • drop : supprimer le commit.

Par exemple, si "Corriger faute de frappe" appartient à "Ajouter script de déploiement", changez la deuxième ligne :

pick a1b2c3d Ajouter script de déploiement
fixup b2c3d4e Corriger faute de frappe
pick c3d4e5f Ajouter vérification de santé
pick d4e5f6a Mettre à jour la documentation
pick e5f6a7b Ajuster le délai d'attente

Enregistrez et fermez l'éditeur. Git rejoue les commits et combine la correction de faute de frappe dans le commit précédent.

Le rebase interactif est également utile pour améliorer les messages de commit avant une pull request. Si trois commits disent tous "mise à jour", utilisez reword et transformez-les en messages qui expliquent le changement réel.

Si un conflit apparaît, résolvez-le comme un conflit de fusion normal :

git status
git add fichier-résolu
git rebase --continue

Si vous décidez que le rebase n'en vaut pas la peine :

git rebase --abort

Cela ramène votre branche à l'état avant le début du rebase.

Diviser un commit pendant un rebase

Parfois, un commit contient deux modifications non liées. Par exemple, vous pourriez avoir un commit qui met à jour un manifeste Kubernetes et corrige également une section du README. Ces modifications devraient probablement être séparées.

Lancez un rebase interactif :

git rebase -i HEAD~3

Changez pick en edit pour le commit que vous voulez diviser. Lorsque Git s'arrête à ce commit, réinitialisez-le tout en conservant les modifications dans votre arbre de travail :

git reset HEAD^

Maintenant, stagifiez et commitez la première partie logique :

git add k8s/deployment.yaml
git commit -m "Mettre à jour la vérification de santé du déploiement"

Ensuite, stagifiez et commitez la deuxième partie :

git add README.md
git commit -m "Documenter le comportement de la vérification de santé"

Continuez le rebase :

git rebase --continue

Cela transforme un commit large en deux commits ciblés. Les réviseurs peuvent comprendre chaque changement sans avoir à séparer mentalement des travaux non liés.

Quand demander de l'aide avant de réécrire

Demandez de l'aide avant de réécrire toute branche que d'autres personnes utilisent. Un chef d'équipe, un ingénieur de release ou un mainteneur de dépôt peut vous dire si la branche est sûre à réécrire et comment l'équipe gère les pushs forcés.

Demandez de l'aide immédiatement si un rebase affecte des commits qui sont déjà déployés, tagués ou référencés par des rapports d'incident. Dans ces cas, préserver l'historique est souvent plus important que de le rendre soigné.

Pour les branches de fonctionnalités courantes, l'édition de l'historique est une étape de nettoyage normale. Utilisez git commit --amend pour le dernier commit, le rebase interactif pour une petite série locale, et --force-with-lease lorsque vous devez mettre à jour une branche distante que vous possédez.