Si un ancien commit contient une modification indésirable sur un fichier, vous pouvez annuler la modification de ce fichier sur votre branche actuelle et enregistrer le résultat dans un nouveau commit. Cela conserve les commits ultérieurs en place et préserve leurs modifications lorsque le patch inverse s'applique toujours. Sur macOS, utilisez Terminal depuis le dépôt ; examinez d'abord le patch, en particulier si le travail ultérieur a touché aux mêmes lignes.

Choisir la bonne méthode de modification de l'historique

Cette procédure ajoute un commit qui annule la modification apportée par le commit cible à un chemin donné. Elle n'efface pas ce fichier de l'ancien commit, de sorte que l'ancienne version reste dans l'historique. Cette approche évite de réécrire l'historique de la branche et est généralement appropriée lorsque le commit a déjà été partagé ou que des commits ultérieurs en dépendent. Git décrit git revert comme l'enregistrement d'un nouveau commit qui annule un commit antérieur, mais il ne propose pas d'option intégrée pour annuler uniquement un chemin spécifique d'un commit existant. À la place, les étapes ci-dessous génèrent le patch de ce chemin et l'appliquent à l'envers. Documentation Git : git-revert

Si la modification du fichier doit être supprimée du commit historique lui-même, il s'agit d'une tâche de réécriture de l'historique, généralement gérée à l'aide d'un rebase interactif. La réécriture modifie les identifiants des commits et peut perturber les collaborateurs dont le travail est basé sur les anciens commits. Ce guide présente l'approche additive, plus sûre.

Annuler la modification d'un fichier dans le commit cible

Ouvrez le Terminal, accédez au dépôt et vérifiez l'arbre de travail avant de modifier quoi que ce soit. Remplacez TARGET par l'identifiant du commit et path/to/file par le chemin relatif à la racine du dépôt. L'exemple utilise un fichier de patch temporaire dans le répertoire /tmp de macOS ; choisissez un autre nom temporaire si ce fichier existe déjà.

git status --short
git show --stat --oneline TARGET
git diff TARGET^ TARGET -- "path/to/file" > /tmp/remove-file-change.patch

Le diff compare le commit cible avec son premier parent et limite le résultat au chemin spécifié. Pour un commit standard, hors fusion, cela représente la modification apportée par ce commit au fichier. Vérifiez que le résumé correspond bien au commit visé et que le patch n'est pas vide. Vous pouvez l'examiner avec less /tmp/remove-file-change.patch ; appuyez sur q pour quitter. Git documente les diffs de commit à commit avec une limitation facultative par chemin. Documentation Git : git-diff

Avant de l'appliquer, assurez-vous de n'avoir aucune modification non liée, indexée ou non indexée, dans le fichier cible. Le patch inverse modifie la copie de travail, un arbre de travail propre est donc le point de départ le plus simple. Si vous avez un travail local utile, validez-le ou mettez-le de côté en toute sécurité d'abord ; ne l'écrasez pas avec une commande de restauration.

Vérifiez si le patch inverse s'applique correctement, puis appliquez-le :

git apply --reverse --check /tmp/remove-file-change.patch
git apply --reverse /tmp/remove-file-change.patch

--reverse applique le diff à l'envers, tandis que --check vérifie s'il peut s'appliquer sans modifier les fichiers. Par défaut, git apply fait échouer l'intégralité du patch si certains blocs (hunks) ne s'ajustent pas, plutôt que de n'appliquer que les parties compatibles. Si la vérification échoue, arrêtez-vous et examinez le fichier et le patch. Des modifications ultérieures peuvent chevaucher la modification ciblée, empêchant Git de déterminer avec certitude ce qui doit être supprimé. Réglez le contenu souhaité manuellement, ou utilisez un processus de réécriture de l'historique s'il est indispensable de préserver une séquence historique éditée avec précision. N'ajoutez pas --reject à la légère : cela peut appliquer uniquement les morceaux correspondants et laisser les blocs rejetés de côté. Documentation Git : git-apply

Vérifier et commiter le résultat

Examinez exactement ce qui a changé avant l'indexation. git diff affiche les modifications de l'arbre de travail ; utilisez à nouveau le chemin pour cibler la révision. Si le résultat est correct, indexez ce fichier et vérifiez le diff indexé avant de créer un nouveau commit.

git diff -- "path/to/file"
git add -- "path/to/file"
git diff --cached -- "path/to/file"
git status --short
git commit -m "Remove file change from earlier commit"

Les guillemets protègent le chemin de l'interprétation par le shell, et -- marque la fin des options. Ils sont particulièrement utiles pour les noms contenant des espaces ou des métacaractères du shell. Le commit enregistre l'index prêt pour la validation ; vérifier git diff --cached permet de contrôler le contenu exact qui s'apprête à être validé. Documentation Git : git-commit

Après avoir validé le commit, vérifiez git status --short et examinez le nouveau commit avec git show --stat --oneline HEAD. Si le commit d'origine avait ajouté le fichier, l'inversion de son patch peut le supprimer — cela est normal si aucun contenu ultérieur ne doit être conservé. Si des commits ultérieurs ont ajouté du contenu utile à ce fichier, ces ajouts devraient subsister lorsque le patch inverse s'applique sans encombre ; examinez le fichier final et le diff pour confirmer.

Cas nécessitant une attention particulière

Un commit de fusion a plusieurs parents, et sa modification dépend du parent auquel vous le comparez. TARGET^ désigne le premier parent ; il se peut qu'il ne représente pas la modification que vous aviez l'intention de supprimer. Identifiez le bon parent et comparez explicitement avec lui avant d'appliquer quoi que ce soit. Les renommages, les fichiers binaires et les fichiers déplacés depuis lors peuvent également rendre inadapté un patch limité par un chemin ; vérifiez le diff et interrompez l'opération s'il ne décrit pas la modification de fichier voulue. Pour les annulations spécifiques aux fusions, la documentation de revert de Git détaille le choix du parent de la branche principale (mainline) et ses conséquences. Documentation Git : git-revert

Le point de contrôle fiable reste le patch examiné : il ne doit présenter que la modification apportée par le commit cible au chemin prévu. S'il ne peut pas s'appliquer proprement au fichier actuel, ne forcez pas. Reconstituez soigneusement le fichier actuel tel que vous le souhaitez, puis n'indexez et ne commitez qu'après avoir passé en revue le résultat final.

Sources