Besoin de supprimer un fichier de git parce qu'il contient un mot de passe ou parce qu'il est trop gros ?
Pas si compliqué ! Mais attention quand même !
Si vous avez suivi une de mes formations, j'espère que vous vous rappelez de deux choses :
git utilise des snapshots, ce qui veut dire que chaque commit contient une "copie" (compressée) de chaque fichier
la grande majorité des commandes git ne supprime pas de données mais en ajoute
Le premier point implique que si vous voulez supprimer un fichier dans TOUT l'historique, il faut le supprimer dans tous les commits où il est présent, sans quoi, dans le cas d'un fichier contenant un mot de passe par exemple, il sera possible de récupérer ce mot de passe depuis un ancien commit.
Le second point implique qu'il faut bien vérifier qu'à la suite de nos modifications, le fichier est bien supprimé et pas juste "caché". Nous verrons d'ailleurs qu'à la suite de la première commande exécutée pour ré-écrire l'historique, il y aura encore un moyen de récupérer notre fichier.
Supposons que l'on ait l'historique suivante :
$ git log --oneline --graph --all --decorate
* d737928 2023-03-02 | (HEAD -> master) Add TODO file <Fabrice Fontenoy>
* ad86106 2023-03-02 | Add a last line to README file <Fabrice Fontenoy>
* 66b6a6e 2023-03-02 | Add line to README file <Fabrice Fontenoy>
* 8846a61 2023-03-02 | Init README file <Fabrice Fontenoy>
Et que l'on s'est rendu compte qu'on a versionné un fichier password.txt qui, comme son nom l'indique, contient un password.
D'abord, on va regarder quand ce fichier a été ajouté :
$ git --oneline --all -- password.txt
* ad86106 Add a last line to README file
On trouve ainsi le commit où le fichier a été ajouté et ce commit va nous servir de base pour la ré-écriture de notre historique.
Et pour cela, on va utiliser la commande filter-branch :
$ git filter-branch --index-filter \
'git rm --ignore-unmatch --cached password.txt' -- ad86106^..
Je vous passe les détails sur les options qui sont là pour des questions de performance (évite de faire des checkouts en modifiant uniquement l'index) et pour ne pas remonter d'erreur en cas de fichier non présent sur un commit.
Ce qu'il faut comprendre, c'est que cette commande va reprendre les commits un à un à partir du commit spécifié et modifier l'historique en exécutant la commande donnée.
Une fois la commande exécutée vous ne verrez plus votre fichier password.txt. Mais attention, la vérité est ailleurs...
$ git log --oneline --graph --all --decorate
* adfcf8a 2023-03-02 | (HEAD -> master) Add TODO file <Fabrice Fontenoy>
* fa6e5d7 2023-03-02 | Add a last line to README file <Fabrice Fontenoy>
| * d737928 2023-03-02 | (refs/original/refs/heads/master) Add TODO file <Fabrice Fontenoy>
| * ad86106 2023-03-02 | Add a last line to README file <Fabrice Fontenoy>
|/
* 66b6a6e 2023-03-02 | Add line to README file <Fabrice Fontenoy>
* 8846a61 2023-03-02 | Init README file <Fabrice Fontenoy>
On voit que git a réécrit l'historique en ajoutant de nouveaux commits mais sans supprimer les anciens.
À ce stade, on pourrait faire un checkout du commit d737928 et récupérer notre mot de passe.
Pour complètement supprimer le fichier, il faut d'abord supprimer toutes les références encore existantes vers les commits que l'on souhaite supprimer :
$ rm -Rf .git/refs/original
$ rm -Rf .git/logs/
$ git log --oneline --graph --all --decorate d737928
* adfcf8a 2023-03-02 | (HEAD -> master) Add TODO file <Fabrice Fontenoy>
* fa6e5d7 2023-03-02 | Add a last line to README file <Fabrice Fontenoy>
| * d737928 2023-03-02 | Add TODO file <Fabrice Fontenoy>
| * ad86106 2023-03-02 | Add a last line to README file <Fabrice Fontenoy>
|/
* 66b6a6e 2023-03-02 | Add line to README file <Fabrice Fontenoy>
* 8846a61 2023-03-02 | Init README file <Fabrice Fontenoy>
Une fois les commits déréférencés, il ne reste plus qu'à lancer le garbage collector de git et à nettoyer le répertoire pour notamment supprimer définitivement les commits plus accessibles :
$ git gc
$ git prune --expire now
$ git log --oneline --graph --all --decorate d737928
fatal: argument 'd737928' ambigu : révision inconnue ou chemin inexistant.
Utilisez '--' pour séparer les chemins des révisions, comme ceci :
'git <commande> [<révision>...] -- [<chemin>...]'
Et voilà ! Plus de mot de passe ! Ouf !
----
Vous avez aimé cette article ? Vous voulez devenir un expert git ?
N'hésitez pas à me contacter !
Bình luận